diff --git a/build.go b/build.go index ac09b1e..35fd2da 100644 --- a/build.go +++ b/build.go @@ -21,20 +21,19 @@ import ( type PackagerParameters interface { } -func validateResult(result packit.BuildResult, err error) (packit.BuildResult, error) { - if err != nil { - return packit.BuildResult{}, err - } - - return result, err -} - func Build( logger scribe.Emitter, commonBuildParameters pythoninstallers.CommonBuildParameters, buildParameters map[string]PackagerParameters, ) packit.BuildFunc { return func(context packit.BuildContext) (packit.BuildResult, error) { + + if len(context.Plan.Entries) == 0 { + return packit.BuildResult{}, packit.Fail.WithMessage("empty plan: %s", context.Plan) + } + + var results []packit.BuildResult + for _, entry := range context.Plan.Entries { logger.Title("Handling %s", entry.Name) parameters, ok := buildParameters[entry.Name] @@ -50,7 +49,10 @@ func Build( commonBuildParameters, )(context) - return validateResult(result, err) + if err != nil { + return packit.BuildResult{}, err + } + results = append(results, result) case pipenv.Pipenv: result, err := pipenv.Build( @@ -58,7 +60,10 @@ func Build( commonBuildParameters, )(context) - return validateResult(result, err) + if err != nil { + return packit.BuildResult{}, err + } + results = append(results, result) case miniconda.Conda: result, err := miniconda.Build( @@ -66,7 +71,10 @@ func Build( commonBuildParameters, )(context) - return validateResult(result, err) + if err != nil { + return packit.BuildResult{}, err + } + results = append(results, result) case poetry.PoetryDependency: result, err := poetry.Build( @@ -74,7 +82,10 @@ func Build( commonBuildParameters, )(context) - return validateResult(result, err) + if err != nil { + return packit.BuildResult{}, err + } + results = append(results, result) case uv.Uv: result, err := uv.Build( @@ -82,13 +93,33 @@ func Build( commonBuildParameters, )(context) - return validateResult(result, err) + if err != nil { + return packit.BuildResult{}, err + } + results = append(results, result) default: return packit.BuildResult{}, packit.Fail.WithMessage("unknown plan: %s", entry.Name) } } - return packit.BuildResult{}, packit.Fail.WithMessage("empty plan: %s", context.Plan) + return combineResults(results...), nil + } +} + +func combineResults(results ...packit.BuildResult) packit.BuildResult { + if len(results) < 1 { + return packit.BuildResult{} + } + combinedResults := results[0] + + for i := range results { + if i == 0 { + continue + } + combinedResults.Layers = append(combinedResults.Layers, results[i].Layers...) + combinedResults.Launch.BOM = append(combinedResults.Launch.BOM, results[i].Launch.BOM...) + combinedResults.Build.BOM = append(combinedResults.Build.BOM, results[i].Build.BOM...) } + return combinedResults } diff --git a/detect_test.go b/detect_test.go index 704f439..a401e36 100644 --- a/detect_test.go +++ b/detect_test.go @@ -13,7 +13,9 @@ import ( "github.com/paketo-buildpacks/packit/v2" "github.com/paketo-buildpacks/packit/v2/scribe" + pythoninstallers "github.com/paketo-buildpacks/python-installers" + common "github.com/paketo-buildpacks/python-installers/pkg/installers/common" miniconda "github.com/paketo-buildpacks/python-installers/pkg/installers/miniconda" pip "github.com/paketo-buildpacks/python-installers/pkg/installers/pip" @@ -61,7 +63,7 @@ func testDetect(t *testing.T, context spec.G, it spec.S) { Requires: []packit.BuildPlanRequirement{ { Name: pip.CPython, - Metadata: pip.BuildPlanMetadata{ + Metadata: common.BuildPlanMetadata{ Build: true, }, }, @@ -78,19 +80,20 @@ func testDetect(t *testing.T, context spec.G, it spec.S) { plans = append(plans, packit.BuildPlan{ Provides: []packit.BuildPlanProvision{ + {Name: pipenv.Pip}, {Name: pipenv.Pipenv}, }, Requires: []packit.BuildPlanRequirement{ { - Name: pipenv.Pip, - Metadata: pipenv.BuildPlanMetadata{ + Name: pipenv.CPython, + Metadata: common.BuildPlanMetadata{ Build: true, Launch: false, }, }, { - Name: pipenv.CPython, - Metadata: pipenv.BuildPlanMetadata{ + Name: pipenv.Pip, + Metadata: common.BuildPlanMetadata{ Build: true, Launch: false, }, @@ -130,23 +133,24 @@ func testDetect(t *testing.T, context spec.G, it spec.S) { withPoetry := append(plans, packit.BuildPlan{ Provides: []packit.BuildPlanProvision{ - {Name: "poetry"}, + {Name: poetry.Pip}, + {Name: poetry.PoetryDependency}, }, Requires: []packit.BuildPlanRequirement{ - { - Name: poetry.Pip, - Metadata: poetry.BuildPlanMetadata{ - Build: true, - }, - }, { Name: poetry.CPython, - Metadata: poetry.BuildPlanMetadata{ + Metadata: common.BuildPlanMetadata{ Build: true, Version: "1.2.3", VersionSource: "pyproject.toml", }, }, + { + Name: poetry.Pip, + Metadata: common.BuildPlanMetadata{ + Build: true, + }, + }, }, }, ) diff --git a/integration.json b/integration.json index 280f8de..c86c699 100644 --- a/integration.json +++ b/integration.json @@ -6,6 +6,5 @@ ], "build-plan": "index.docker.io/paketocommunity/build-plan", - "cpython": "index.docker.io/paketobuildpacks/cpython", - "pip": "index.docker.io/paketobuildpacks/pip" + "cpython": "index.docker.io/paketobuildpacks/cpython" } diff --git a/integration/helpers.go b/integration/helpers.go index 1cb1978..a49bd28 100644 --- a/integration/helpers.go +++ b/integration/helpers.go @@ -31,10 +31,6 @@ type TestSettings struct { Online string Offline string } - Pip struct { - Online string - Offline string - } BuildPlan struct { Online string } @@ -47,7 +43,6 @@ type TestSettings struct { Config struct { CPython string `json:"cpython"` - Pip string `json:"pip"` BuildPlan string `json:"build-plan"` } } diff --git a/integration/installers/init_test.go b/integration/installers/init_test.go index 0d79c63..b721979 100644 --- a/integration/installers/init_test.go +++ b/integration/installers/init_test.go @@ -69,15 +69,6 @@ func TestIntegration(t *testing.T) { Execute(settings.Config.CPython) Expect(err).NotTo(HaveOccurred()) - settings.Buildpacks.Pip.Online, err = buildpackStore.Get. - Execute(settings.Config.Pip) - Expect(err).NotTo(HaveOccurred()) - - settings.Buildpacks.Pip.Offline, err = buildpackStore.Get. - WithOfflineDependencies(). - Execute(settings.Config.Pip) - Expect(err).NotTo(HaveOccurred()) - settings.Buildpacks.PythonInstallers.Online, err = buildpackStore.Get. WithVersion("1.2.3"). Execute(root) diff --git a/integration/installers/pipenv_default_test.go b/integration/installers/pipenv_default_test.go index d95133d..7937f22 100644 --- a/integration/installers/pipenv_default_test.go +++ b/integration/installers/pipenv_default_test.go @@ -64,7 +64,6 @@ func pipenvTestDefault(t *testing.T, context spec.G, it spec.S) { WithPullPolicy("never"). WithBuildpacks( settings.Buildpacks.CPython.Online, - settings.Buildpacks.Pip.Online, settings.Buildpacks.PythonInstallers.Online, settings.Buildpacks.BuildPlan.Online, ). @@ -127,7 +126,6 @@ func pipenvTestDefault(t *testing.T, context spec.G, it spec.S) { WithPullPolicy("never"). WithBuildpacks( settings.Buildpacks.CPython.Online, - settings.Buildpacks.Pip.Online, settings.Buildpacks.PythonInstallers.Online, settings.Buildpacks.BuildPlan.Online, ). diff --git a/integration/installers/pipenv_layer_reuse_test.go b/integration/installers/pipenv_layer_reuse_test.go index e6b5491..cb97844 100644 --- a/integration/installers/pipenv_layer_reuse_test.go +++ b/integration/installers/pipenv_layer_reuse_test.go @@ -80,7 +80,6 @@ func pipenvTestLayerReuse(t *testing.T, context spec.G, it spec.S) { WithPullPolicy("never"). WithBuildpacks( settings.Buildpacks.CPython.Online, - settings.Buildpacks.Pip.Online, settings.Buildpacks.PythonInstallers.Online, settings.Buildpacks.BuildPlan.Online, ). @@ -93,7 +92,6 @@ func pipenvTestLayerReuse(t *testing.T, context spec.G, it spec.S) { WithPullPolicy("never"). WithBuildpacks( settings.Buildpacks.CPython.Online, - settings.Buildpacks.Pip.Online, settings.Buildpacks.PythonInstallers.Online, settings.Buildpacks.BuildPlan.Online, ). @@ -119,8 +117,8 @@ func pipenvTestLayerReuse(t *testing.T, context spec.G, it spec.S) { return cLogs.String() }).Should(MatchRegexp(`pipenv, version \d+\.\d+\.\d+`)) - Expect(secondImage.Buildpacks[2].Key).To(Equal(buildpackInfo.Buildpack.ID)) - Expect(secondImage.Buildpacks[2].Layers["pipenv"].SHA).To(Equal(firstImage.Buildpacks[2].Layers["pipenv"].SHA)) + Expect(secondImage.Buildpacks[1].Key).To(Equal(buildpackInfo.Buildpack.ID)) + Expect(secondImage.Buildpacks[1].Layers["pipenv"].SHA).To(Equal(firstImage.Buildpacks[1].Layers["pipenv"].SHA)) }) }) @@ -142,7 +140,6 @@ func pipenvTestLayerReuse(t *testing.T, context spec.G, it spec.S) { WithPullPolicy("never"). WithBuildpacks( settings.Buildpacks.CPython.Online, - settings.Buildpacks.Pip.Online, settings.Buildpacks.PythonInstallers.Online, settings.Buildpacks.BuildPlan.Online, ). @@ -154,7 +151,6 @@ func pipenvTestLayerReuse(t *testing.T, context spec.G, it spec.S) { WithPullPolicy("never"). WithBuildpacks( settings.Buildpacks.CPython.Online, - settings.Buildpacks.Pip.Online, settings.Buildpacks.PythonInstallers.Online, settings.Buildpacks.BuildPlan.Online, ). @@ -200,8 +196,8 @@ func pipenvTestLayerReuse(t *testing.T, context spec.G, it spec.S) { return cLogs.String() }).Should(MatchRegexp(`pipenv, version \d+\.\d+\.\d+`)) - Expect(secondImage.Buildpacks[2].Key).To(Equal(buildpackInfo.Buildpack.ID)) - Expect(secondImage.Buildpacks[2].Layers["pipenv"].SHA).ToNot(Equal(firstImage.Buildpacks[2].Layers["pipenv"].SHA)) + Expect(secondImage.Buildpacks[1].Key).To(Equal(buildpackInfo.Buildpack.ID)) + Expect(secondImage.Buildpacks[1].Layers["pipenv"].SHA).ToNot(Equal(firstImage.Buildpacks[1].Layers["pipenv"].SHA)) }) }) } diff --git a/integration/installers/pipenv_versions_test.go b/integration/installers/pipenv_versions_test.go index 1ac3d2d..40d3e98 100644 --- a/integration/installers/pipenv_versions_test.go +++ b/integration/installers/pipenv_versions_test.go @@ -79,7 +79,6 @@ func pipenvTestVersions(t *testing.T, context spec.G, it spec.S) { WithPullPolicy("never"). WithBuildpacks( settings.Buildpacks.CPython.Online, - settings.Buildpacks.Pip.Online, settings.Buildpacks.PythonInstallers.Online, settings.Buildpacks.BuildPlan.Online, ). @@ -110,7 +109,6 @@ func pipenvTestVersions(t *testing.T, context spec.G, it spec.S) { WithPullPolicy("never"). WithBuildpacks( settings.Buildpacks.CPython.Online, - settings.Buildpacks.Pip.Online, settings.Buildpacks.PythonInstallers.Online, settings.Buildpacks.BuildPlan.Online, ). diff --git a/integration/installers/poetry_default_test.go b/integration/installers/poetry_default_test.go index ce2a844..e6e7f4f 100644 --- a/integration/installers/poetry_default_test.go +++ b/integration/installers/poetry_default_test.go @@ -65,7 +65,6 @@ func poetryTestDefault(t *testing.T, context spec.G, it spec.S) { WithPullPolicy("never"). WithBuildpacks( settings.Buildpacks.CPython.Online, - settings.Buildpacks.Pip.Online, settings.Buildpacks.PythonInstallers.Online, settings.Buildpacks.BuildPlan.Online, ). @@ -98,7 +97,6 @@ func poetryTestDefault(t *testing.T, context spec.G, it spec.S) { " Resolving CPython version", " Candidate version sources (in priority order):", ` pyproject.toml -> "3.13.*"`, - ` -> ""`, ` -> ""`, )) Expect(logs).To(ContainLines( @@ -128,7 +126,6 @@ func poetryTestDefault(t *testing.T, context spec.G, it spec.S) { WithPullPolicy("never"). WithBuildpacks( settings.Buildpacks.CPython.Online, - settings.Buildpacks.Pip.Online, settings.Buildpacks.PythonInstallers.Online, settings.Buildpacks.BuildPlan.Online, ). @@ -161,7 +158,6 @@ func poetryTestDefault(t *testing.T, context spec.G, it spec.S) { " Resolving CPython version", " Candidate version sources (in priority order):", ` pyproject.toml -> "3.12.*"`, - ` -> ""`, ` -> ""`, )) Expect(logs).To(ContainLines( @@ -206,7 +202,6 @@ func poetryTestDefault(t *testing.T, context spec.G, it spec.S) { WithPullPolicy("never"). WithBuildpacks( settings.Buildpacks.CPython.Online, - settings.Buildpacks.Pip.Online, settings.Buildpacks.PythonInstallers.Online, settings.Buildpacks.BuildPlan.Online, ). diff --git a/integration/installers/poetry_layer_reuse_test.go b/integration/installers/poetry_layer_reuse_test.go index 6219503..9750ada 100644 --- a/integration/installers/poetry_layer_reuse_test.go +++ b/integration/installers/poetry_layer_reuse_test.go @@ -77,7 +77,6 @@ func poetryTestLayerReuse(t *testing.T, context spec.G, it spec.S) { WithPullPolicy("never"). WithBuildpacks( settings.Buildpacks.CPython.Online, - settings.Buildpacks.Pip.Online, settings.Buildpacks.PythonInstallers.Online, settings.Buildpacks.BuildPlan.Online, ). @@ -103,7 +102,6 @@ func poetryTestLayerReuse(t *testing.T, context spec.G, it spec.S) { WithPullPolicy("never"). WithBuildpacks( settings.Buildpacks.CPython.Online, - settings.Buildpacks.Pip.Online, settings.Buildpacks.PythonInstallers.Online, settings.Buildpacks.BuildPlan.Online, ). @@ -129,7 +127,7 @@ func poetryTestLayerReuse(t *testing.T, context spec.G, it spec.S) { return cLogs.String() }).Should(MatchRegexp(`Poetry.*version \d+\.\d+\.\d+`)) - Expect(secondImage.Buildpacks[0].Layers["poetry"].SHA).To(Equal(firstImage.Buildpacks[0].Layers["poetry"].SHA)) + Expect(secondImage.Buildpacks[1].Layers["poetry"].SHA).To(Equal(firstImage.Buildpacks[1].Layers["poetry"].SHA)) }) }) } diff --git a/integration/installers/poetry_versions_test.go b/integration/installers/poetry_versions_test.go index 4c6f1de..64cb615 100644 --- a/integration/installers/poetry_versions_test.go +++ b/integration/installers/poetry_versions_test.go @@ -79,7 +79,6 @@ func poetryTestVersions(t *testing.T, context spec.G, it spec.S) { WithPullPolicy("never"). WithBuildpacks( settings.Buildpacks.CPython.Online, - settings.Buildpacks.Pip.Online, settings.Buildpacks.PythonInstallers.Online, settings.Buildpacks.BuildPlan.Online, ). @@ -110,7 +109,6 @@ func poetryTestVersions(t *testing.T, context spec.G, it spec.S) { WithPullPolicy("never"). WithBuildpacks( settings.Buildpacks.CPython.Online, - settings.Buildpacks.Pip.Online, settings.Buildpacks.PythonInstallers.Online, settings.Buildpacks.BuildPlan.Online, ). diff --git a/pkg/installers/common/common.go b/pkg/installers/common/common.go index 3280932..1baeab4 100644 --- a/pkg/installers/common/common.go +++ b/pkg/installers/common/common.go @@ -29,3 +29,19 @@ type CommonBuildParameters struct { Clock chronos.Clock Logger scribe.Emitter } + +// BuildPlanMetadata is the buildpack specific data included in build plan +// requirements. +type BuildPlanMetadata struct { + // Build denotes the dependency is needed at build-time. + Build bool `toml:"build"` + + // Launch denotes the dependency is needed at runtime. + Launch bool `toml:"launch"` + + // Version denotes the version of a dependency, if there is one. + Version string `toml:"version"` + + // VersionSource denotes where dependency version came from (e.g. an environment variable). + VersionSource string `toml:"version-source"` +} diff --git a/pkg/installers/miniconda/build_test.go b/pkg/installers/miniconda/build_test.go index 16c6d0d..6ec14ba 100644 --- a/pkg/installers/miniconda/build_test.go +++ b/pkg/installers/miniconda/build_test.go @@ -13,6 +13,7 @@ import ( "github.com/paketo-buildpacks/packit/v2" "github.com/paketo-buildpacks/packit/v2/chronos" + pythoninstallers "github.com/paketo-buildpacks/python-installers/pkg/installers/common" "github.com/paketo-buildpacks/python-installers/pkg/installers/miniconda" "github.com/paketo-buildpacks/python-installers/pkg/installers/miniconda/fakes" diff --git a/pkg/installers/miniconda/detect_test.go b/pkg/installers/miniconda/detect_test.go index aa1c331..a85e4f6 100644 --- a/pkg/installers/miniconda/detect_test.go +++ b/pkg/installers/miniconda/detect_test.go @@ -8,9 +8,10 @@ import ( "testing" "github.com/paketo-buildpacks/packit/v2" - "github.com/paketo-buildpacks/python-installers/pkg/installers/miniconda" "github.com/sclevine/spec" + "github.com/paketo-buildpacks/python-installers/pkg/installers/miniconda" + . "github.com/onsi/gomega" ) diff --git a/pkg/installers/miniconda/script_runner_test.go b/pkg/installers/miniconda/script_runner_test.go index 7c76a83..9fbec85 100644 --- a/pkg/installers/miniconda/script_runner_test.go +++ b/pkg/installers/miniconda/script_runner_test.go @@ -10,9 +10,10 @@ import ( "path/filepath" "testing" + "github.com/sclevine/spec" + "github.com/paketo-buildpacks/python-installers/pkg/installers/miniconda" "github.com/paketo-buildpacks/python-installers/pkg/installers/miniconda/fakes" - "github.com/sclevine/spec" . "github.com/onsi/gomega" ) diff --git a/pkg/installers/pip/build_test.go b/pkg/installers/pip/build_test.go index f28e0c6..2e0ebbf 100644 --- a/pkg/installers/pip/build_test.go +++ b/pkg/installers/pip/build_test.go @@ -20,10 +20,11 @@ import ( "github.com/paketo-buildpacks/packit/v2/postal" "github.com/paketo-buildpacks/packit/v2/sbom" "github.com/paketo-buildpacks/packit/v2/scribe" + "github.com/sclevine/spec" + pythoninstallers "github.com/paketo-buildpacks/python-installers/pkg/installers/common" "github.com/paketo-buildpacks/python-installers/pkg/installers/pip" "github.com/paketo-buildpacks/python-installers/pkg/installers/pip/fakes" - "github.com/sclevine/spec" . "github.com/onsi/gomega" ) diff --git a/pkg/installers/pip/detect.go b/pkg/installers/pip/detect.go index 3102309..2794337 100644 --- a/pkg/installers/pip/detect.go +++ b/pkg/installers/pip/detect.go @@ -9,22 +9,48 @@ import ( "regexp" "github.com/paketo-buildpacks/packit/v2" + + pythoninstallers "github.com/paketo-buildpacks/python-installers/pkg/installers/common" ) -// BuildPlanMetadata is the buildpack specific data included in build plan -// requirements. -type BuildPlanMetadata struct { - // Build denotes the dependency is needed at build-time. - Build bool `toml:"build"` +// Return a pip requirement +func GetVersionedRequirement() *packit.BuildPlanRequirement { + pipVersion := os.Getenv("BP_PIP_VERSION") + if pipVersion == "" { + return nil + } - // Launch denotes the dependency is needed at runtime. - Launch bool `toml:"launch"` + // Pip releases are of the form X.Y rather than X.Y.0, so in order + // to support selecting the exact version X.Y we have to up-convert + // X.Y to X.Y.0. + // Otherwise X.Y would match the latest patch release + // X.Y.Z if it is available. + var xDotYPattern = regexp.MustCompile(`^\d+\.\d+$`) + if xDotYPattern.MatchString(pipVersion) { + pipVersion = pipVersion + ".0" + } + + return &packit.BuildPlanRequirement{ + Name: Pip, + Metadata: pythoninstallers.BuildPlanMetadata{ + VersionSource: "BP_PIP_VERSION", + Version: pipVersion, + }, + } +} - // Version denotes the version of a dependency, if there is one. - Version string `toml:"version"` +func GetRequirement() packit.BuildPlanRequirement { + requirement := GetVersionedRequirement() + if requirement != nil { + return *requirement + } - // VersionSource denotes where dependency version came from (e.g. an environment variable). - VersionSource string `toml:"version-source"` + return packit.BuildPlanRequirement{ + Name: Pip, + Metadata: pythoninstallers.BuildPlanMetadata{ + Build: true, + }, + } } // Detect will return a packit.DetectFunc that will be invoked during the @@ -41,32 +67,16 @@ func Detect() packit.DetectFunc { requirements := []packit.BuildPlanRequirement{ { Name: CPython, - Metadata: BuildPlanMetadata{ + Metadata: pythoninstallers.BuildPlanMetadata{ Build: true, }, }, } - pipVersion := os.Getenv("BP_PIP_VERSION") + pipRequirement := GetVersionedRequirement() - if pipVersion != "" { - // Pip releases are of the form X.Y rather than X.Y.0, so in order - // to support selecting the exact version X.Y we have to up-convert - // X.Y to X.Y.0. - // Otherwise X.Y would match the latest patch release - // X.Y.Z if it is available. - var xDotYPattern = regexp.MustCompile(`^\d+\.\d+$`) - if xDotYPattern.MatchString(pipVersion) { - pipVersion = pipVersion + ".0" - } - - requirements = append(requirements, packit.BuildPlanRequirement{ - Name: Pip, - Metadata: BuildPlanMetadata{ - VersionSource: "BP_PIP_VERSION", - Version: pipVersion, - }, - }) + if pipRequirement != nil { + requirements = append(requirements, *pipRequirement) } return packit.DetectResult{ diff --git a/pkg/installers/pip/detect_test.go b/pkg/installers/pip/detect_test.go index 97689c1..9f073ae 100644 --- a/pkg/installers/pip/detect_test.go +++ b/pkg/installers/pip/detect_test.go @@ -8,10 +8,12 @@ import ( "testing" "github.com/paketo-buildpacks/packit/v2" - "github.com/paketo-buildpacks/python-installers/pkg/installers/pip" "github.com/sclevine/spec" . "github.com/onsi/gomega" + + pythoninstallers "github.com/paketo-buildpacks/python-installers/pkg/installers/common" + "github.com/paketo-buildpacks/python-installers/pkg/installers/pip" ) func testDetect(t *testing.T, context spec.G, it spec.S) { @@ -39,7 +41,7 @@ func testDetect(t *testing.T, context spec.G, it spec.S) { Requires: []packit.BuildPlanRequirement{ { Name: pip.CPython, - Metadata: pip.BuildPlanMetadata{ + Metadata: pythoninstallers.BuildPlanMetadata{ Build: true, }, }, @@ -63,13 +65,13 @@ func testDetect(t *testing.T, context spec.G, it spec.S) { Requires: []packit.BuildPlanRequirement{ { Name: pip.CPython, - Metadata: pip.BuildPlanMetadata{ + Metadata: pythoninstallers.BuildPlanMetadata{ Build: true, }, }, { Name: pip.Pip, - Metadata: pip.BuildPlanMetadata{ + Metadata: pythoninstallers.BuildPlanMetadata{ Version: "some-version", VersionSource: "BP_PIP_VERSION", }, @@ -95,13 +97,13 @@ func testDetect(t *testing.T, context spec.G, it spec.S) { Requires: []packit.BuildPlanRequirement{ { Name: pip.CPython, - Metadata: pip.BuildPlanMetadata{ + Metadata: pythoninstallers.BuildPlanMetadata{ Build: true, }, }, { Name: pip.Pip, - Metadata: pip.BuildPlanMetadata{ + Metadata: pythoninstallers.BuildPlanMetadata{ Version: "2.11.0", VersionSource: "BP_PIP_VERSION", }, @@ -128,13 +130,13 @@ func testDetect(t *testing.T, context spec.G, it spec.S) { Requires: []packit.BuildPlanRequirement{ { Name: pip.CPython, - Metadata: pip.BuildPlanMetadata{ + Metadata: pythoninstallers.BuildPlanMetadata{ Build: true, }, }, { Name: pip.Pip, - Metadata: pip.BuildPlanMetadata{ + Metadata: pythoninstallers.BuildPlanMetadata{ Version: "22.1.3", VersionSource: "BP_PIP_VERSION", }, @@ -161,13 +163,13 @@ func testDetect(t *testing.T, context spec.G, it spec.S) { Requires: []packit.BuildPlanRequirement{ { Name: pip.CPython, - Metadata: pip.BuildPlanMetadata{ + Metadata: pythoninstallers.BuildPlanMetadata{ Build: true, }, }, { Name: pip.Pip, - Metadata: pip.BuildPlanMetadata{ + Metadata: pythoninstallers.BuildPlanMetadata{ Version: "some.other", VersionSource: "BP_PIP_VERSION", }, diff --git a/pkg/installers/pip/pip_install_process_test.go b/pkg/installers/pip/pip_install_process_test.go index e90c932..abfddcc 100644 --- a/pkg/installers/pip/pip_install_process_test.go +++ b/pkg/installers/pip/pip_install_process_test.go @@ -11,9 +11,10 @@ import ( "testing" "github.com/paketo-buildpacks/packit/v2/pexec" + "github.com/sclevine/spec" + pip "github.com/paketo-buildpacks/python-installers/pkg/installers/pip" "github.com/paketo-buildpacks/python-installers/pkg/installers/pip/fakes" - "github.com/sclevine/spec" . "github.com/onsi/gomega" ) diff --git a/pkg/installers/pip/site_process_test.go b/pkg/installers/pip/site_process_test.go index 25057af..b8146f5 100644 --- a/pkg/installers/pip/site_process_test.go +++ b/pkg/installers/pip/site_process_test.go @@ -12,9 +12,10 @@ import ( "testing" "github.com/paketo-buildpacks/packit/v2/pexec" + "github.com/sclevine/spec" + pip "github.com/paketo-buildpacks/python-installers/pkg/installers/pip" "github.com/paketo-buildpacks/python-installers/pkg/installers/pip/fakes" - "github.com/sclevine/spec" . "github.com/onsi/gomega" ) diff --git a/pkg/installers/pipenv/build.go b/pkg/installers/pipenv/build.go index 225fc1c..85adddc 100644 --- a/pkg/installers/pipenv/build.go +++ b/pkg/installers/pipenv/build.go @@ -32,7 +32,7 @@ type DependencyManager interface { // InstallProcess defines the interface for installing the pipenv dependency into a layer. type InstallProcess interface { - Execute(version, destLayerPath string) error + Execute(version, destLayerPath, pipLayerPath string) error } // SitePackageProcess defines the interface for looking up site packages within a layer. @@ -128,8 +128,13 @@ func Build( logger.Process("Executing build process") logger.Subprocess(fmt.Sprintf("Installing Pipenv %s", dependency.Version)) + pipLayer, err := context.Layers.Get(Pip) + if err != nil { + return packit.BuildResult{}, err + } + duration, err := clock.Measure(func() error { - return installProcess.Execute(dependency.Version, pipenvLayer.Path) + return installProcess.Execute(dependency.Version, pipenvLayer.Path, pipLayer.Path) }) if err != nil { diff --git a/pkg/installers/pipenv/build_test.go b/pkg/installers/pipenv/build_test.go index 16a0cf6..e23e418 100644 --- a/pkg/installers/pipenv/build_test.go +++ b/pkg/installers/pipenv/build_test.go @@ -20,10 +20,11 @@ import ( "github.com/paketo-buildpacks/packit/v2/postal" "github.com/paketo-buildpacks/packit/v2/sbom" "github.com/paketo-buildpacks/packit/v2/scribe" + "github.com/sclevine/spec" + pythoninstallers "github.com/paketo-buildpacks/python-installers/pkg/installers/common" "github.com/paketo-buildpacks/python-installers/pkg/installers/pipenv" "github.com/paketo-buildpacks/python-installers/pkg/installers/pipenv/fakes" - "github.com/sclevine/spec" . "github.com/onsi/gomega" ) @@ -175,6 +176,7 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { Expect(installProcess.ExecuteCall.Receives.Version).To(ContainSubstring("pipenv-dependency-version")) Expect(installProcess.ExecuteCall.Receives.DestLayerPath).To(Equal(filepath.Join(layersDir, "pipenv"))) + Expect(installProcess.ExecuteCall.Receives.PipLayerPath).To(Equal(filepath.Join(layersDir, "pip"))) }) context("when build plan entries require pipenv at build/launch", func() { diff --git a/pkg/installers/pipenv/detect.go b/pkg/installers/pipenv/detect.go index 0603ae1..1923cb3 100644 --- a/pkg/installers/pipenv/detect.go +++ b/pkg/installers/pipenv/detect.go @@ -8,24 +8,10 @@ import ( "os" "github.com/paketo-buildpacks/packit/v2" -) - -// BuildPlanMetadata is the buildpack specific data included in build plan -// requirements. -type BuildPlanMetadata struct { - // VersionSource denotes where dependency version came from (e.g. an - // environment variable). - VersionSource string `toml:"version-source"` - - // Version denotes the version of a dependency, if there is one. - Version string `toml:"version"` - // Build denotes the dependency is needed at build-time. - Build bool `toml:"build"` - - // Launch denotes the dependency is needed at runtime. - Launch bool `toml:"launch"` -} + pythoninstallers "github.com/paketo-buildpacks/python-installers/pkg/installers/common" + "github.com/paketo-buildpacks/python-installers/pkg/installers/pip" +) // Detect will return a packit.DetectFunc that will be invoked during the // detect phase of the buildpack lifecycle. @@ -39,25 +25,21 @@ func Detect() packit.DetectFunc { return func(context packit.DetectContext) (packit.DetectResult, error) { requirements := []packit.BuildPlanRequirement{ - { - Name: Pip, - Metadata: BuildPlanMetadata{ - Build: true, - }, - }, { Name: CPython, - Metadata: BuildPlanMetadata{ + Metadata: pythoninstallers.BuildPlanMetadata{ Build: true, }, }, } + requirements = append(requirements, pip.GetRequirement()) + pipEnvVersion, ok := os.LookupEnv("BP_PIPENV_VERSION") if ok { requirements = append(requirements, packit.BuildPlanRequirement{ Name: Pipenv, - Metadata: BuildPlanMetadata{ + Metadata: pythoninstallers.BuildPlanMetadata{ Version: pipEnvVersion, VersionSource: "BP_PIPENV_VERSION", }, @@ -67,6 +49,7 @@ func Detect() packit.DetectFunc { return packit.DetectResult{ Plan: packit.BuildPlan{ Provides: []packit.BuildPlanProvision{ + {Name: Pip}, {Name: Pipenv}, }, Requires: requirements, diff --git a/pkg/installers/pipenv/detect_test.go b/pkg/installers/pipenv/detect_test.go index 19854eb..6a19823 100644 --- a/pkg/installers/pipenv/detect_test.go +++ b/pkg/installers/pipenv/detect_test.go @@ -10,9 +10,12 @@ import ( "testing" "github.com/paketo-buildpacks/packit/v2" - "github.com/paketo-buildpacks/python-installers/pkg/installers/pipenv" + "github.com/sclevine/spec" + pythoninstallers "github.com/paketo-buildpacks/python-installers/pkg/installers/common" + "github.com/paketo-buildpacks/python-installers/pkg/installers/pipenv" + . "github.com/onsi/gomega" ) @@ -41,19 +44,20 @@ func testDetect(t *testing.T, context spec.G, it spec.S) { Expect(result).To(Equal(packit.DetectResult{ Plan: packit.BuildPlan{ Provides: []packit.BuildPlanProvision{ - {Name: "pipenv"}, + {Name: pipenv.Pip}, + {Name: pipenv.Pipenv}, }, Requires: []packit.BuildPlanRequirement{ { - Name: pipenv.Pip, - Metadata: pipenv.BuildPlanMetadata{ + Name: pipenv.CPython, + Metadata: pythoninstallers.BuildPlanMetadata{ Build: true, Launch: false, }, }, { - Name: pipenv.CPython, - Metadata: pipenv.BuildPlanMetadata{ + Name: pipenv.Pip, + Metadata: pythoninstallers.BuildPlanMetadata{ Build: true, Launch: false, }, @@ -76,26 +80,27 @@ func testDetect(t *testing.T, context spec.G, it spec.S) { Expect(result).To(Equal(packit.DetectResult{ Plan: packit.BuildPlan{ Provides: []packit.BuildPlanProvision{ - {Name: "pipenv"}, + {Name: pipenv.Pip}, + {Name: pipenv.Pipenv}, }, Requires: []packit.BuildPlanRequirement{ { - Name: pipenv.Pip, - Metadata: pipenv.BuildPlanMetadata{ + Name: pipenv.CPython, + Metadata: pythoninstallers.BuildPlanMetadata{ Build: true, Launch: false, }, }, { - Name: pipenv.CPython, - Metadata: pipenv.BuildPlanMetadata{ + Name: pipenv.Pip, + Metadata: pythoninstallers.BuildPlanMetadata{ Build: true, Launch: false, }, }, { - Name: "pipenv", - Metadata: pipenv.BuildPlanMetadata{ + Name: pipenv.Pipenv, + Metadata: pythoninstallers.BuildPlanMetadata{ Version: "1.2.3", VersionSource: "BP_PIPENV_VERSION", }, diff --git a/pkg/installers/pipenv/fakes/install_process.go b/pkg/installers/pipenv/fakes/install_process.go index 96ae291..1397320 100644 --- a/pkg/installers/pipenv/fakes/install_process.go +++ b/pkg/installers/pipenv/fakes/install_process.go @@ -13,22 +13,24 @@ type InstallProcess struct { Receives struct { Version string DestLayerPath string + PipLayerPath string } Returns struct { Error error } - Stub func(string, string) error + Stub func(string, string, string) error } } -func (f *InstallProcess) Execute(param1 string, param2 string) error { +func (f *InstallProcess) Execute(param1 string, param2 string, param3 string) error { f.ExecuteCall.mutex.Lock() defer f.ExecuteCall.mutex.Unlock() f.ExecuteCall.CallCount++ f.ExecuteCall.Receives.Version = param1 f.ExecuteCall.Receives.DestLayerPath = param2 + f.ExecuteCall.Receives.PipLayerPath = param3 if f.ExecuteCall.Stub != nil { - return f.ExecuteCall.Stub(param1, param2) + return f.ExecuteCall.Stub(param1, param2, param3) } return f.ExecuteCall.Returns.Error } diff --git a/pkg/installers/pipenv/pipenv_install_process.go b/pkg/installers/pipenv/pipenv_install_process.go index 068d28f..eb0af56 100644 --- a/pkg/installers/pipenv/pipenv_install_process.go +++ b/pkg/installers/pipenv/pipenv_install_process.go @@ -8,6 +8,7 @@ import ( "bytes" "fmt" "os" + "path/filepath" "github.com/paketo-buildpacks/packit/v2/pexec" ) @@ -32,13 +33,14 @@ func NewPipenvInstallProcess(executable Executable) PipenvInstallProcess { // Execute installs the provided version of pipenv from the internet into the // layer path designated by targetLayerPath -func (p PipenvInstallProcess) Execute(version, targetLayerPath string) error { +func (p PipenvInstallProcess) Execute(version, targetLayerPath, pipLayerPath string) error { buffer := bytes.NewBuffer(nil) + pipPath := fmt.Sprintf("PATH=%s", filepath.Join(pipLayerPath, "bin")) err := p.executable.Execute(pexec.Execution{ Args: []string{"install", fmt.Sprintf("pipenv==%s", version), "--user"}, - // Set the PYTHONUSERBASE to ensure that pip is installed to the newly created target layer. - Env: append(os.Environ(), fmt.Sprintf("PYTHONUSERBASE=%s", targetLayerPath)), + // Set the PYTHONUSERBASE to ensure that pipenv is installed to the newly created target layer. + Env: append(os.Environ(), pipPath, fmt.Sprintf("PYTHONUSERBASE=%s", targetLayerPath)), Stdout: buffer, Stderr: buffer, }) diff --git a/pkg/installers/pipenv/pipenv_install_process_test.go b/pkg/installers/pipenv/pipenv_install_process_test.go index 33d0480..8ed5033 100644 --- a/pkg/installers/pipenv/pipenv_install_process_test.go +++ b/pkg/installers/pipenv/pipenv_install_process_test.go @@ -8,12 +8,14 @@ import ( "errors" "fmt" "os" + "path/filepath" "testing" "github.com/paketo-buildpacks/packit/v2/pexec" + "github.com/sclevine/spec" + "github.com/paketo-buildpacks/python-installers/pkg/installers/pipenv" "github.com/paketo-buildpacks/python-installers/pkg/installers/pipenv/fakes" - "github.com/sclevine/spec" . "github.com/onsi/gomega" ) @@ -24,6 +26,7 @@ func testPipenvInstallProcess(t *testing.T, context spec.G, it spec.S) { version = "1.2.3-some.version" destLayerPath string + pipLayerPath string executable *fakes.Executable pipenvInstallProcess pipenv.PipenvInstallProcess @@ -31,6 +34,7 @@ func testPipenvInstallProcess(t *testing.T, context spec.G, it spec.S) { it.Before(func() { destLayerPath = t.TempDir() + pipLayerPath = t.TempDir() executable = &fakes.Executable{} @@ -40,10 +44,13 @@ func testPipenvInstallProcess(t *testing.T, context spec.G, it spec.S) { context("Execute", func() { context("there is a pipenv dependency to install", func() { it("installs it to the pipenv layer", func() { - err := pipenvInstallProcess.Execute(version, destLayerPath) + err := pipenvInstallProcess.Execute(version, destLayerPath, pipLayerPath) Expect(err).NotTo(HaveOccurred()) - Expect(executable.ExecuteCall.Receives.Execution.Env).To(Equal(append(os.Environ(), fmt.Sprintf("PYTHONUSERBASE=%s", destLayerPath)))) + Expect(executable.ExecuteCall.Receives.Execution.Env).To(Equal(append(os.Environ(), + fmt.Sprintf("PATH=%s", filepath.Join(pipLayerPath, "bin")), + fmt.Sprintf("PYTHONUSERBASE=%s", destLayerPath)), + )) Expect(executable.ExecuteCall.Receives.Execution.Args).To(Equal([]string{"install", "pipenv==1.2.3-some.version", "--user"})) }) }) @@ -61,7 +68,7 @@ func testPipenvInstallProcess(t *testing.T, context spec.G, it spec.S) { }) it("returns an error", func() { - err := pipenvInstallProcess.Execute(version, destLayerPath) + err := pipenvInstallProcess.Execute(version, destLayerPath, pipLayerPath) Expect(err).To(MatchError(ContainSubstring("installing pipenv failed"))) Expect(err).To(MatchError(ContainSubstring("stdout output"))) Expect(err).To(MatchError(ContainSubstring("stderr output"))) diff --git a/pkg/installers/pipenv/site_process_test.go b/pkg/installers/pipenv/site_process_test.go index 7a7fb5e..1eb67b9 100644 --- a/pkg/installers/pipenv/site_process_test.go +++ b/pkg/installers/pipenv/site_process_test.go @@ -12,9 +12,10 @@ import ( "testing" "github.com/paketo-buildpacks/packit/v2/pexec" + "github.com/sclevine/spec" + "github.com/paketo-buildpacks/python-installers/pkg/installers/pipenv" "github.com/paketo-buildpacks/python-installers/pkg/installers/pipenv/fakes" - "github.com/sclevine/spec" . "github.com/onsi/gomega" ) diff --git a/pkg/installers/poetry/build.go b/pkg/installers/poetry/build.go index 0a8f570..baea877 100644 --- a/pkg/installers/poetry/build.go +++ b/pkg/installers/poetry/build.go @@ -30,7 +30,7 @@ type DependencyManager interface { // InstallProcess defines the interface for installing the poetry dependency into a layer. type InstallProcess interface { - Execute(version, targetLayerPath string) error + Execute(version, targetLayerPath, pipLayerPath string) error } // SitePackageProcess defines the interface for looking site packages within a layer. @@ -122,8 +122,13 @@ func Build( logger.Process("Executing build process") logger.Subprocess("Installing Poetry %s", dependency.Version) + pipLayer, err := context.Layers.Get(Pip) + if err != nil { + return packit.BuildResult{}, err + } + duration, err := clock.Measure(func() error { - err = installProcess.Execute(dependency.Version, poetryLayer.Path) + err = installProcess.Execute(dependency.Version, poetryLayer.Path, pipLayer.Path) if err != nil { return err } diff --git a/pkg/installers/poetry/build_test.go b/pkg/installers/poetry/build_test.go index 9fd7ae0..fcd8c16 100644 --- a/pkg/installers/poetry/build_test.go +++ b/pkg/installers/poetry/build_test.go @@ -19,10 +19,11 @@ import ( "github.com/paketo-buildpacks/packit/v2/postal" "github.com/paketo-buildpacks/packit/v2/sbom" "github.com/paketo-buildpacks/packit/v2/scribe" + "github.com/sclevine/spec" + pythoninstallers "github.com/paketo-buildpacks/python-installers/pkg/installers/common" "github.com/paketo-buildpacks/python-installers/pkg/installers/poetry" "github.com/paketo-buildpacks/python-installers/pkg/installers/poetry/fakes" - "github.com/sclevine/spec" . "github.com/onsi/gomega" ) diff --git a/pkg/installers/poetry/detect.go b/pkg/installers/poetry/detect.go index b66ac67..71f6c2c 100644 --- a/pkg/installers/poetry/detect.go +++ b/pkg/installers/poetry/detect.go @@ -8,16 +8,13 @@ import ( "os" "path/filepath" + pythoninstallers "github.com/paketo-buildpacks/python-installers/pkg/installers/common" + "github.com/paketo-buildpacks/python-installers/pkg/installers/pip" + "github.com/paketo-buildpacks/packit/v2" "github.com/paketo-buildpacks/packit/v2/fs" ) -type BuildPlanMetadata struct { - VersionSource string `toml:"version-source"` - Build bool `toml:"build"` - Version string `toml:"version"` -} - //go:generate faux --interface PyProjectPythonVersionParser --output fakes/pyproject_parser.go type PyProjectPythonVersionParser interface { // ParsePythonVersion extracts `tool.poetry.dependencies.python` @@ -47,15 +44,9 @@ func Detect(parser PyProjectPythonVersionParser) packit.DetectFunc { } requirements := []packit.BuildPlanRequirement{ - { - Name: Pip, - Metadata: BuildPlanMetadata{ - Build: true, - }, - }, { Name: CPython, - Metadata: BuildPlanMetadata{ + Metadata: pythoninstallers.BuildPlanMetadata{ Build: true, Version: pythonVersion, VersionSource: PyProjectTomlFile, @@ -63,10 +54,12 @@ func Detect(parser PyProjectPythonVersionParser) packit.DetectFunc { }, } + requirements = append(requirements, pip.GetRequirement()) + if version, ok := os.LookupEnv("BP_POETRY_VERSION"); ok { requirements = append(requirements, packit.BuildPlanRequirement{ Name: PoetryDependency, - Metadata: BuildPlanMetadata{ + Metadata: pythoninstallers.BuildPlanMetadata{ VersionSource: "BP_POETRY_VERSION", Version: version, }, @@ -76,6 +69,7 @@ func Detect(parser PyProjectPythonVersionParser) packit.DetectFunc { return packit.DetectResult{ Plan: packit.BuildPlan{ Provides: []packit.BuildPlanProvision{ + {Name: Pip}, {Name: PoetryDependency}, }, Requires: requirements, diff --git a/pkg/installers/poetry/detect_test.go b/pkg/installers/poetry/detect_test.go index 4a807a0..55cc286 100644 --- a/pkg/installers/poetry/detect_test.go +++ b/pkg/installers/poetry/detect_test.go @@ -12,9 +12,12 @@ import ( . "github.com/onsi/gomega" "github.com/paketo-buildpacks/packit/v2" + "github.com/sclevine/spec" + "github.com/paketo-buildpacks/python-installers/pkg/installers/poetry" "github.com/paketo-buildpacks/python-installers/pkg/installers/poetry/fakes" - "github.com/sclevine/spec" + + pythoninstallers "github.com/paketo-buildpacks/python-installers/pkg/installers/common" ) func testDetect(t *testing.T, context spec.G, it spec.S) { @@ -54,23 +57,24 @@ func testDetect(t *testing.T, context spec.G, it spec.S) { Expect(result).To(Equal(packit.DetectResult{ Plan: packit.BuildPlan{ Provides: []packit.BuildPlanProvision{ - {Name: "poetry"}, + {Name: poetry.Pip}, + {Name: poetry.PoetryDependency}, }, Requires: []packit.BuildPlanRequirement{ - { - Name: poetry.Pip, - Metadata: poetry.BuildPlanMetadata{ - Build: true, - }, - }, { Name: poetry.CPython, - Metadata: poetry.BuildPlanMetadata{ + Metadata: pythoninstallers.BuildPlanMetadata{ Build: true, Version: "1.2.3", VersionSource: "pyproject.toml", }, }, + { + Name: poetry.Pip, + Metadata: pythoninstallers.BuildPlanMetadata{ + Build: true, + }, + }, }, }, })) @@ -90,26 +94,27 @@ func testDetect(t *testing.T, context spec.G, it spec.S) { Expect(result).To(Equal(packit.DetectResult{ Plan: packit.BuildPlan{ Provides: []packit.BuildPlanProvision{ - {Name: "poetry"}, + {Name: poetry.Pip}, + {Name: poetry.PoetryDependency}, }, Requires: []packit.BuildPlanRequirement{ - { - Name: poetry.Pip, - Metadata: poetry.BuildPlanMetadata{ - Build: true, - }, - }, { Name: poetry.CPython, - Metadata: poetry.BuildPlanMetadata{ + Metadata: pythoninstallers.BuildPlanMetadata{ Build: true, Version: "1.2.3", VersionSource: "pyproject.toml", }, }, { - Name: "poetry", - Metadata: poetry.BuildPlanMetadata{ + Name: poetry.Pip, + Metadata: pythoninstallers.BuildPlanMetadata{ + Build: true, + }, + }, + { + Name: poetry.PoetryDependency, + Metadata: pythoninstallers.BuildPlanMetadata{ VersionSource: "BP_POETRY_VERSION", Version: "some-version", }, diff --git a/pkg/installers/poetry/fakes/install_process.go b/pkg/installers/poetry/fakes/install_process.go index 3f3a076..a150469 100644 --- a/pkg/installers/poetry/fakes/install_process.go +++ b/pkg/installers/poetry/fakes/install_process.go @@ -13,22 +13,24 @@ type InstallProcess struct { Receives struct { Version string TargetLayerPath string + PipLayerPath string } Returns struct { Error error } - Stub func(string, string) error + Stub func(string, string, string) error } } -func (f *InstallProcess) Execute(param1 string, param2 string) error { +func (f *InstallProcess) Execute(param1 string, param2 string, param3 string) error { f.ExecuteCall.mutex.Lock() defer f.ExecuteCall.mutex.Unlock() f.ExecuteCall.CallCount++ f.ExecuteCall.Receives.Version = param1 f.ExecuteCall.Receives.TargetLayerPath = param2 + f.ExecuteCall.Receives.PipLayerPath = param3 if f.ExecuteCall.Stub != nil { - return f.ExecuteCall.Stub(param1, param2) + return f.ExecuteCall.Stub(param1, param2, param3) } return f.ExecuteCall.Returns.Error } diff --git a/pkg/installers/poetry/poetry_install_process.go b/pkg/installers/poetry/poetry_install_process.go index 440d546..3e4fbbb 100644 --- a/pkg/installers/poetry/poetry_install_process.go +++ b/pkg/installers/poetry/poetry_install_process.go @@ -8,6 +8,7 @@ import ( "bytes" "fmt" "os" + "path/filepath" "github.com/paketo-buildpacks/packit/v2/pexec" ) @@ -32,13 +33,14 @@ func NewPoetryInstallProcess(executable Executable) PoetryInstallProcess { // Execute installs the provided version of pipenv from the internet into the // layer path designated by targetLayerPath -func (p PoetryInstallProcess) Execute(version, targetLayerPath string) error { +func (p PoetryInstallProcess) Execute(version, targetLayerPath, pipLayerPath string) error { buffer := bytes.NewBuffer(nil) + pipPath := fmt.Sprintf("PYTHONPATH=%s", filepath.Join(pipLayerPath)) err := p.executable.Execute(pexec.Execution{ Args: []string{"-m", "pip", "install", fmt.Sprintf("poetry==%s", version), "--user"}, - // Set the PYTHONUSERBASE to ensure that pip is installed to the newly created target layer. - Env: append(os.Environ(), fmt.Sprintf("PYTHONUSERBASE=%s", targetLayerPath)), + // Set the PYTHONUSERBASE to ensure that poetry is installed to the newly created target layer. + Env: append(os.Environ(), pipPath, fmt.Sprintf("PYTHONUSERBASE=%s", targetLayerPath)), Stdout: buffer, Stderr: buffer, }) diff --git a/pkg/installers/poetry/poetry_install_process_test.go b/pkg/installers/poetry/poetry_install_process_test.go index 7c1e9b7..6328795 100644 --- a/pkg/installers/poetry/poetry_install_process_test.go +++ b/pkg/installers/poetry/poetry_install_process_test.go @@ -11,9 +11,10 @@ import ( "testing" "github.com/paketo-buildpacks/packit/v2/pexec" + "github.com/sclevine/spec" + "github.com/paketo-buildpacks/python-installers/pkg/installers/poetry" "github.com/paketo-buildpacks/python-installers/pkg/installers/poetry/fakes" - "github.com/sclevine/spec" . "github.com/onsi/gomega" ) @@ -24,6 +25,7 @@ func testPoetryInstallProcess(t *testing.T, context spec.G, it spec.S) { version string destLayerPath string + pipLayerPath string executable *fakes.Executable poetryInstallProcess poetry.PoetryInstallProcess @@ -33,6 +35,8 @@ func testPoetryInstallProcess(t *testing.T, context spec.G, it spec.S) { var err error destLayerPath, err = os.MkdirTemp("", "poetry") Expect(err).NotTo(HaveOccurred()) + pipLayerPath, err = os.MkdirTemp("", "pip") + Expect(err).NotTo(HaveOccurred()) version = "1.2.3-some.version" @@ -44,10 +48,13 @@ func testPoetryInstallProcess(t *testing.T, context spec.G, it spec.S) { context("Execute", func() { context("there is a poetry dependency to install", func() { it("installs it to the poetry layer", func() { - err := poetryInstallProcess.Execute(version, destLayerPath) + err := poetryInstallProcess.Execute(version, destLayerPath, pipLayerPath) Expect(err).NotTo(HaveOccurred()) - Expect(executable.ExecuteCall.Receives.Execution.Env).To(Equal(append(os.Environ(), fmt.Sprintf("PYTHONUSERBASE=%s", destLayerPath)))) + Expect(executable.ExecuteCall.Receives.Execution.Env).To(Equal(append(os.Environ(), + fmt.Sprintf("PYTHONPATH=%s", pipLayerPath), + fmt.Sprintf("PYTHONUSERBASE=%s", destLayerPath)), + )) Expect(executable.ExecuteCall.Receives.Execution.Args).To(Equal([]string{"-m", "pip", "install", "poetry==1.2.3-some.version", "--user"})) }) }) @@ -65,7 +72,7 @@ func testPoetryInstallProcess(t *testing.T, context spec.G, it spec.S) { }) it("returns an error", func() { - err := poetryInstallProcess.Execute(version, destLayerPath) + err := poetryInstallProcess.Execute(version, destLayerPath, pipLayerPath) Expect(err).To(MatchError(ContainSubstring("installing poetry failed"))) Expect(err).To(MatchError(ContainSubstring("stdout output"))) Expect(err).To(MatchError(ContainSubstring("stderr output"))) diff --git a/pkg/installers/poetry/pyproject_parser_test.go b/pkg/installers/poetry/pyproject_parser_test.go index da5e0f1..5e29d6f 100644 --- a/pkg/installers/poetry/pyproject_parser_test.go +++ b/pkg/installers/poetry/pyproject_parser_test.go @@ -10,8 +10,9 @@ import ( "testing" . "github.com/onsi/gomega" - "github.com/paketo-buildpacks/python-installers/pkg/installers/poetry" "github.com/sclevine/spec" + + "github.com/paketo-buildpacks/python-installers/pkg/installers/poetry" ) func testPyProjectParser(t *testing.T, context spec.G, it spec.S) { diff --git a/pkg/installers/poetry/site_process_test.go b/pkg/installers/poetry/site_process_test.go index cf79897..fd11c4d 100644 --- a/pkg/installers/poetry/site_process_test.go +++ b/pkg/installers/poetry/site_process_test.go @@ -12,9 +12,10 @@ import ( "testing" "github.com/paketo-buildpacks/packit/v2/pexec" + "github.com/sclevine/spec" + "github.com/paketo-buildpacks/python-installers/pkg/installers/poetry" "github.com/paketo-buildpacks/python-installers/pkg/installers/poetry/fakes" - "github.com/sclevine/spec" . "github.com/onsi/gomega" ) diff --git a/pkg/installers/uv/build_test.go b/pkg/installers/uv/build_test.go index b44f346..1da2313 100644 --- a/pkg/installers/uv/build_test.go +++ b/pkg/installers/uv/build_test.go @@ -14,6 +14,7 @@ import ( "github.com/paketo-buildpacks/packit/v2" "github.com/paketo-buildpacks/packit/v2/chronos" + pythoninstallers "github.com/paketo-buildpacks/python-installers/pkg/installers/common" "github.com/paketo-buildpacks/python-installers/pkg/installers/uv" "github.com/paketo-buildpacks/python-installers/pkg/installers/uv/fakes" diff --git a/pkg/installers/uv/detect.go b/pkg/installers/uv/detect.go index dc9001f..b571b39 100644 --- a/pkg/installers/uv/detect.go +++ b/pkg/installers/uv/detect.go @@ -11,13 +11,9 @@ import ( "github.com/paketo-buildpacks/packit/v2" "github.com/paketo-buildpacks/packit/v2/fs" -) -type BuildPlanMetadata struct { - VersionSource string `toml:"version-source"` - Build bool `toml:"build"` - Version string `toml:"version"` -} + pythoninstallers "github.com/paketo-buildpacks/python-installers/pkg/installers/common" +) // Detect will return a packit.DetectFunc that will be invoked during the // detect phase of the buildpack lifecycle. @@ -53,7 +49,7 @@ func Detect() packit.DetectFunc { plan.Requires = []packit.BuildPlanRequirement{ { Name: Uv, - Metadata: BuildPlanMetadata{ + Metadata: pythoninstallers.BuildPlanMetadata{ VersionSource: "BP_UV_VERSION", Version: version, }, diff --git a/pkg/installers/uv/detect_test.go b/pkg/installers/uv/detect_test.go index aee8d2a..f2fc12c 100644 --- a/pkg/installers/uv/detect_test.go +++ b/pkg/installers/uv/detect_test.go @@ -11,9 +11,11 @@ import ( "testing" "github.com/paketo-buildpacks/packit/v2" - "github.com/paketo-buildpacks/python-installers/pkg/installers/uv" "github.com/sclevine/spec" + pythoninstallers "github.com/paketo-buildpacks/python-installers/pkg/installers/common" + "github.com/paketo-buildpacks/python-installers/pkg/installers/uv" + . "github.com/onsi/gomega" ) @@ -74,7 +76,7 @@ func testDetect(t *testing.T, context spec.G, it spec.S) { Requires: []packit.BuildPlanRequirement{ { Name: uv.Uv, - Metadata: uv.BuildPlanMetadata{ + Metadata: pythoninstallers.BuildPlanMetadata{ VersionSource: "BP_UV_VERSION", Version: "some-version", }, diff --git a/pkg/installers/uv/install_process_test.go b/pkg/installers/uv/install_process_test.go index 5e7e4c5..0b0e662 100644 --- a/pkg/installers/uv/install_process_test.go +++ b/pkg/installers/uv/install_process_test.go @@ -12,8 +12,9 @@ import ( "testing" . "github.com/onsi/gomega" - "github.com/paketo-buildpacks/python-installers/pkg/installers/uv" "github.com/sclevine/spec" + + "github.com/paketo-buildpacks/python-installers/pkg/installers/uv" ) func testUvInstallProcess(t *testing.T, context spec.G, it spec.S) { diff --git a/pkg/installers/uv/uvlock_parser_test.go b/pkg/installers/uv/uvlock_parser_test.go index bb5c0bc..433026f 100644 --- a/pkg/installers/uv/uvlock_parser_test.go +++ b/pkg/installers/uv/uvlock_parser_test.go @@ -11,8 +11,9 @@ import ( "testing" . "github.com/onsi/gomega" - "github.com/paketo-buildpacks/python-installers/pkg/installers/uv" "github.com/sclevine/spec" + + "github.com/paketo-buildpacks/python-installers/pkg/installers/uv" ) func testUvLockParser(t *testing.T, context spec.G, it spec.S) { diff --git a/run/main.go b/run/main.go index 51e0f38..23742f7 100644 --- a/run/main.go +++ b/run/main.go @@ -14,6 +14,7 @@ import ( "github.com/paketo-buildpacks/packit/v2/pexec" "github.com/paketo-buildpacks/packit/v2/postal" "github.com/paketo-buildpacks/packit/v2/scribe" + pythoninstallers "github.com/paketo-buildpacks/python-installers" pkgcommon "github.com/paketo-buildpacks/python-installers/pkg/installers/common" miniconda "github.com/paketo-buildpacks/python-installers/pkg/installers/miniconda"