Skip to content

Commit ae8ee3e

Browse files
authored
feat: add opt-in to use this buildpack for all package managers (#171)
This will allow, in tandem with python-package-managers-run and python-start, to request the use of python-package-managers-install in place of the other set of buildpacks.
1 parent 51d31fa commit ae8ee3e

7 files changed

Lines changed: 204 additions & 0 deletions

File tree

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,22 @@ The buildpack will do the following:
3232
* At run time:
3333
- Does nothing
3434

35+
## Configuration
36+
37+
### `BP_ENABLE_PACKAGE_MANAGERS`
38+
39+
The `BP_ENABLE_PACKAGE_MANAGERS` environment variable allows you to force the use
40+
of this buildpack for all the supported package managers. It works in tandem
41+
with `python-start`. `python-start` will add a requirement that is fulfilled by
42+
this buildpack.
43+
44+
It is currently used as an opt-in to allow Paketo users to do tests before the
45+
old buildpacks get retired.
46+
47+
```shell
48+
BP_ENABLE_PACKAGE_MANAGERS=true
49+
```
50+
3551
## Usage
3652

3753
To package this buildpack for consumption:

constants.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// SPDX-FileCopyrightText: © 2026 Idiap Research Institute <contact@idiap.ch>
2+
// SPDX-FileContributor: Samuel Gaist <samuel.gaist@idiap.ch>
3+
//
4+
// SPDX-License-Identifier: Apache-2.0
5+
6+
package pythoninstallers
7+
8+
const (
9+
PackageManagersEnv = "BP_ENABLE_PACKAGE_MANAGERS"
10+
PackageManagersInstallPlanEntry = "package-managers-install"
11+
)

detect.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
package pythoninstallers
77

88
import (
9+
"fmt"
10+
"os"
11+
"strconv"
12+
913
"github.com/paketo-buildpacks/packit/v2"
1014
"github.com/paketo-buildpacks/packit/v2/scribe"
1115

@@ -78,6 +82,23 @@ func Detect(logger scribe.Emitter, pyProjectParser poetry.PyProjectParser) packi
7882
return packit.DetectResult{}, packit.Fail.WithMessage("No python packager manager related files found")
7983
}
8084

85+
shouldUsePackageManagers := false
86+
87+
if usePackageManagers, ok := os.LookupEnv(PackageManagersEnv); ok {
88+
shouldUsePackageManagers, err = strconv.ParseBool(usePackageManagers)
89+
if err != nil {
90+
return packit.DetectResult{}, fmt.Errorf("failed to parse %s value %s: %w", PackageManagersEnv, usePackageManagers, err)
91+
}
92+
}
93+
94+
if shouldUsePackageManagers {
95+
for i, plan := range plans {
96+
plans[i].Provides = append(plan.Provides, packit.BuildPlanProvision{
97+
Name: PackageManagersInstallPlanEntry,
98+
})
99+
}
100+
}
101+
81102
return packit.DetectResult{
82103
Plan: Or(plans...),
83104
}, nil

detect_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/sclevine/spec"
2929

3030
. "github.com/onsi/gomega"
31+
"github.com/onsi/gomega/gstruct"
3132
)
3233

3334
func testDetect(t *testing.T, context spec.G, it spec.S) {
@@ -274,5 +275,25 @@ func testDetect(t *testing.T, context spec.G, it spec.S) {
274275
Expect(result.Plan).To(Equal(pythoninstallers.Or(withUv...)))
275276
})
276277
})
278+
279+
context("When BP_ENABLE_PACKAGE_MANAGERS is set", func() {
280+
it.Before(func() {
281+
t.Setenv("BP_ENABLE_PACKAGE_MANAGERS", "true")
282+
Expect(os.WriteFile(filepath.Join(workingDir, "environment.yml"), []byte{}, os.ModePerm)).To(Succeed())
283+
})
284+
285+
it("passes detection", func() {
286+
result, err := detect(packit.DetectContext{
287+
WorkingDir: workingDir,
288+
})
289+
Expect(err).NotTo(HaveOccurred())
290+
Expect(result.Plan.Provides).To(ContainElement(
291+
gstruct.MatchAllFields(gstruct.Fields{
292+
"Name": Equal(pythoninstallers.PackageManagersInstallPlanEntry),
293+
}),
294+
))
295+
})
296+
})
297+
277298
})
278299
}

integration/installers/init_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,5 +120,8 @@ func TestIntegration(t *testing.T) {
120120
suite("pixi LayerReuse", pixiTestLayerReuse, spec.Parallel())
121121
suite("pixi Offline", pixiTestOffline, spec.Parallel())
122122

123+
// Make package-managers mandatory
124+
suite("mandatory package managers", pmTestMandatory)
125+
123126
suite.Run(t)
124127
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// SPDX-FileCopyrightText: Copyright (c) 2013-Present CloudFoundry.org Foundation, Inc. All Rights Reserved.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
package integration_test
6+
7+
import (
8+
"fmt"
9+
"os"
10+
"path/filepath"
11+
"testing"
12+
13+
"github.com/paketo-buildpacks/occam"
14+
"github.com/sclevine/spec"
15+
16+
. "github.com/onsi/gomega"
17+
)
18+
19+
func pmTestMandatory(t *testing.T, context spec.G, it spec.S) {
20+
var (
21+
Expect = NewWithT(t).Expect
22+
Eventually = NewWithT(t).Eventually
23+
pack occam.Pack
24+
docker occam.Docker
25+
)
26+
27+
it.Before(func() {
28+
pack = occam.NewPack()
29+
docker = occam.NewDocker()
30+
})
31+
32+
context("when building a prepared app", func() {
33+
var (
34+
image occam.Image
35+
container occam.Container
36+
name string
37+
source string
38+
)
39+
40+
it.Before(func() {
41+
var err error
42+
name, err = occam.RandomName()
43+
Expect(err).NotTo(HaveOccurred())
44+
45+
source, err = occam.Source(filepath.Join("testdata", "conda", "pm_mandatory_app"))
46+
Expect(err).NotTo(HaveOccurred())
47+
})
48+
49+
it.After(func() {
50+
Expect(docker.Container.Remove.Execute(container.ID)).To(Succeed())
51+
Expect(docker.Image.Remove.Execute(image.ID)).To(Succeed())
52+
Expect(docker.Volume.Remove.Execute(occam.CacheVolumeNames(name))).To(Succeed())
53+
Expect(os.RemoveAll(source)).To(Succeed())
54+
})
55+
56+
it("builds an oci image that has the correct behavior", func() {
57+
var err error
58+
59+
var logs fmt.Stringer
60+
image, logs, err = pack.WithNoColor().Build.
61+
WithPullPolicy("never").
62+
WithBuildpacks(
63+
settings.Buildpacks.PythonInstallers.Online,
64+
settings.Buildpacks.BuildPlan.Online,
65+
).
66+
WithEnv(map[string]string{
67+
"BP_ENABLE_PACKAGE_MANAGERS": "true",
68+
}).
69+
Execute(name, source)
70+
Expect(err).NotTo(HaveOccurred(), logs.String())
71+
72+
container, err = docker.Container.Run.
73+
WithCommand("conda info").
74+
WithPublish("8080").
75+
WithPublishAll().
76+
Execute(image.ID)
77+
Expect(err).NotTo(HaveOccurred())
78+
79+
Eventually(func() string {
80+
cLogs, err := docker.Container.Logs.Execute(container.ID)
81+
Expect(err).NotTo(HaveOccurred())
82+
return cLogs.String()
83+
}).Should(MatchRegexp(`conda version : \d+\.\d+\.\d+`))
84+
})
85+
})
86+
87+
context("when building a default app", func() {
88+
var (
89+
name string
90+
source string
91+
)
92+
93+
it.Before(func() {
94+
var err error
95+
name, err = occam.RandomName()
96+
Expect(err).NotTo(HaveOccurred())
97+
98+
source, err = occam.Source(filepath.Join("testdata", "conda", "miniconda_app"))
99+
Expect(err).NotTo(HaveOccurred())
100+
})
101+
102+
it("fails to build an oci image", func() {
103+
var err error
104+
105+
var logs fmt.Stringer
106+
_, logs, err = pack.WithNoColor().Build.
107+
WithPullPolicy("never").
108+
WithBuildpacks(
109+
settings.Buildpacks.PythonInstallers.Online,
110+
settings.Buildpacks.BuildPlan.Online,
111+
).
112+
WithEnv(map[string]string{
113+
"BP_ENABLE_PACKAGE_MANAGERS": "true",
114+
}).
115+
Execute(name, source)
116+
Expect(err).To(HaveOccurred(), logs.String())
117+
})
118+
119+
})
120+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright (c) 2013-Present CloudFoundry.org Foundation, Inc. All Rights Reserved.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
[[requires]]
6+
name = "conda"
7+
8+
[requires.metadata]
9+
launch = true
10+
11+
[[requires]]
12+
name = "package-managers-install"

0 commit comments

Comments
 (0)