From c072178a35c1980d5a34a8838de419766caa3fab Mon Sep 17 00:00:00 2001 From: Roemer Date: Wed, 5 Nov 2025 09:10:40 +0000 Subject: [PATCH] fix: Improve python version handling --- features/src/python/NOTES.md | 11 +++++++++++ features/src/python/README.md | 14 +++++++++++++- features/src/python/devcontainer-feature.json | 2 +- features/src/python/installer.go | 4 ++-- features/test/python/install-alpha.sh | 7 +++++++ features/test/python/scenarios.json | 13 +++++++++++++ installer/apk.go | 5 +++-- installer/apt.go | 8 ++++---- installer/feature.go | 2 +- 9 files changed, 55 insertions(+), 11 deletions(-) create mode 100644 features/src/python/NOTES.md create mode 100755 features/test/python/install-alpha.sh diff --git a/features/src/python/NOTES.md b/features/src/python/NOTES.md new file mode 100644 index 0000000..7907e4b --- /dev/null +++ b/features/src/python/NOTES.md @@ -0,0 +1,11 @@ +## Notes + +### .0 versions for old Python versions + +Python <= 3.2 didn't use correct semver and therefore the .0 version was missing the .0 (eg. 3.1.0 was only released as 3.1). + +Because of this, it is not possible to install an exact .0 version for those Python releases, it will take the newest patch release in that case. + +Example: +* version 3.1 => would install 3.1.5 +* version 3.1.0 => would unfortunately fail diff --git a/features/src/python/README.md b/features/src/python/README.md index 6eb8971..eee295d 100755 --- a/features/src/python/README.md +++ b/features/src/python/README.md @@ -6,7 +6,7 @@ A package which installs Python. ```json "features": { - "ghcr.io/postfinance/devcontainer-features/python:0.1.0": { + "ghcr.io/postfinance/devcontainer-features/python:0.2.0": { "version": "latest", "downloadUrl": "", "pipIndex": "", @@ -32,3 +32,15 @@ A package which installs Python. - `ms-python.python` - `ms-python.vscode-pylance` + +## Notes + +### .0 versions for old Python versions + +Python <= 3.2 didn't use correct semver and therefore the .0 version was missing the .0 (eg. 3.1.0 was only released as 3.1). + +Because of this, it is not possible to install an exact .0 version for those Python releases, it will take the newest patch release in that case. + +Example: +* version 3.1 => would install 3.1.5 +* version 3.1.0 => would unfortunately fail diff --git a/features/src/python/devcontainer-feature.json b/features/src/python/devcontainer-feature.json index 1536e3a..65ae05a 100644 --- a/features/src/python/devcontainer-feature.json +++ b/features/src/python/devcontainer-feature.json @@ -1,6 +1,6 @@ { "id": "python", - "version": "0.1.0", + "version": "0.2.0", "name": "Python", "description": "A package which installs Python.", "options": { diff --git a/features/src/python/installer.go b/features/src/python/installer.go index 84c9c30..0280633 100644 --- a/features/src/python/installer.go +++ b/features/src/python/installer.go @@ -17,7 +17,7 @@ import ( // Configuration ////////// -var pythonVersionRegexp *regexp.Regexp = regexp.MustCompile(`^(?P\d+)\.(?P\d+)(?:\.(?P\d+))?(?:(?P[a-z]+)(?P\d+))?$`) +var pythonVersionRegexp *regexp.Regexp = regexp.MustCompile(`^v(?P(\d+)\.(\d+)(?:\.(\d+))?(?:([a-z]+)(\d+))?)$`) ////////// // Main @@ -110,7 +110,7 @@ func (c *pythonComponent) InstallVersion(version *gover.Version) error { } // Create a symlink to the installed python version symLinkPath := "/usr/local/python/current" - targetPath := fmt.Sprintf("/usr/local/python/%s", folderName) + targetPath := fmt.Sprintf("/usr/local/python/%s", version.Raw) if err := installer.Tools.FileSystem.CreateSymLink(targetPath, symLinkPath, false); err != nil { return err } diff --git a/features/test/python/install-alpha.sh b/features/test/python/install-alpha.sh new file mode 100755 index 0000000..81f40cd --- /dev/null +++ b/features/test/python/install-alpha.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -e + +[[ -f "$(dirname "$0")/../functions.sh" ]] && source "$(dirname "$0")/../functions.sh" +[[ -f "$(dirname "$0")/functions.sh" ]] && source "$(dirname "$0")/functions.sh" + +check_version "$(python --version)" "Python 3.15.0a1" diff --git a/features/test/python/scenarios.json b/features/test/python/scenarios.json index f3bf969..203c27c 100644 --- a/features/test/python/scenarios.json +++ b/features/test/python/scenarios.json @@ -11,5 +11,18 @@ "version": "3.9.19" } } + }, + "install-alpha": { + "build": { + "dockerfile": "Dockerfile", + "options": [ + "--add-host=host.docker.internal:host-gateway" + ] + }, + "features": { + "./python": { + "version": "3.15.0a1" + } + } } } \ No newline at end of file diff --git a/installer/apk.go b/installer/apk.go index 70ab503..bcb813a 100644 --- a/installer/apk.go +++ b/installer/apk.go @@ -1,6 +1,7 @@ package installer import ( + "fmt" "strings" "github.com/roemer/gotaskr/execr" @@ -11,7 +12,7 @@ type apk struct{} func (a apk) InstallDependencies(dependencies ...string) error { args := append([]string{"add", "--no-cache"}, dependencies...) if err := execr.Run(true, "apk", args...); err != nil { - return err + return fmt.Errorf("failed to install dependencies: %w", err) } return nil } @@ -21,7 +22,7 @@ func (a apk) InstallLocalPackage(packagePath string) error { packagePath = "./" + packagePath } if err := execr.Run(true, "apk", "add", "--no-cache", packagePath); err != nil { - return err + return fmt.Errorf("failed to install local package: %w", err) } return nil } diff --git a/installer/apt.go b/installer/apt.go index 5c177e6..74e5569 100644 --- a/installer/apt.go +++ b/installer/apt.go @@ -15,11 +15,11 @@ func (a apt) InstallDependencies(dependencies ...string) error { return nil } if err := execr.Run(false, "apt-get", "update"); err != nil { - return err + return fmt.Errorf("failed to update apt-get: %w", err) } args := append([]string{"install", "-y"}, dependencies...) if err := execr.Run(true, "apt-get", args...); err != nil { - return err + return fmt.Errorf("failed to install dependencies: %w", err) } a.CleanCache() return nil @@ -30,10 +30,10 @@ func (a apt) InstallLocalPackage(packagePath string) error { packagePath = "./" + packagePath } if err := execr.Run(false, "apt-get", "update"); err != nil { - return err + return fmt.Errorf("failed to update apt-get: %w", err) } if err := execr.Run(true, "apt-get", "install", "-y", packagePath); err != nil { - return err + return fmt.Errorf("failed to install local package: %w", err) } a.CleanCache() return nil diff --git a/installer/feature.go b/installer/feature.go index 94bab3c..d0509bf 100644 --- a/installer/feature.go +++ b/installer/feature.go @@ -96,7 +96,7 @@ func (f *Feature) Process() error { return err } // Get the max according to the reference version - versionToInstall = gover.FindMax(allVersions, referenceVersion, false) + versionToInstall = gover.FindMax(allVersions, referenceVersion, true) } }