Skip to content

Commit a319389

Browse files
authored
Respect BP_POETRY_VERSION when choosing version (#59)
* Install version of poetry provided by `BP_POETRY_VERSION`. - we install the specific version from the internet so we have removed various code paths pertaining to downloading a packaged dependency.
1 parent c515352 commit a319389

8 files changed

Lines changed: 151 additions & 85 deletions

File tree

build.go

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package poetry
22

33
import (
44
"fmt"
5-
"os"
65
"path/filepath"
76
"strings"
87
"time"
@@ -27,13 +26,12 @@ type EntryResolver interface {
2726

2827
type DependencyManager interface {
2928
Resolve(path, id, version, stack string) (postal.Dependency, error)
30-
Deliver(dependency postal.Dependency, cnbPath, destinationPath, platformPath string) error
3129
GenerateBillOfMaterials(dependencies ...postal.Dependency) []packit.BOMEntry
3230
}
3331

3432
// InstallProcess defines the interface for installing the poetry dependency into a layer.
3533
type InstallProcess interface {
36-
Execute(srcPath, targetLayerPath string) error
34+
Execute(version, targetLayerPath string) error
3735
}
3836

3937
// SitePackageProcess defines the interface for looking site packages within a layer.
@@ -105,31 +103,17 @@ func Build(
105103
}, nil
106104
}
107105

108-
logger.Process("Executing build process")
109-
110106
poetryLayer, err = poetryLayer.Reset()
111107
if err != nil {
112108
return packit.BuildResult{}, err
113109
}
114110

115111
poetryLayer.Launch, poetryLayer.Build, poetryLayer.Cache = launch, build, build
116112

113+
logger.Process("Executing build process")
117114
logger.Subprocess("Installing Poetry %s", dependency.Version)
118115
duration, err := clock.Measure(func() error {
119-
// Install the poetry source to a temporary dir, since we only need access to
120-
// it as an intermediate step when installing poetry.
121-
// It doesn't need to go into a layer, since we won't need it in future builds.
122-
poetrySrcDir, err := os.MkdirTemp("", "poetry-source")
123-
if err != nil {
124-
return fmt.Errorf("failed to create temp poetry-source dir: %w", err)
125-
}
126-
127-
err = dependencyManager.Deliver(dependency, context.CNBPath, poetrySrcDir, context.Platform.Path)
128-
if err != nil {
129-
return err
130-
}
131-
132-
err = installProcess.Execute(poetrySrcDir, poetryLayer.Path)
116+
err = installProcess.Execute(dependency.Version, poetryLayer.Path)
133117
if err != nil {
134118
return err
135119
}

build_test.go

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -174,18 +174,6 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
174174
Expect(dependencyManager.ResolveCall.Receives.Version).To(Equal("default"))
175175
Expect(dependencyManager.ResolveCall.Receives.Stack).To(Equal("some-stack"))
176176

177-
Expect(dependencyManager.DeliverCall.Receives.Dependency).To(Equal(postal.Dependency{
178-
ID: "poetry",
179-
Name: "poetry-dependency-name",
180-
SHA256: "poetry-dependency-sha",
181-
Stacks: []string{"some-stack"},
182-
URI: "poetry-dependency-uri",
183-
Version: "poetry-dependency-version",
184-
}))
185-
Expect(dependencyManager.DeliverCall.Receives.CnbPath).To(Equal(cnbDir))
186-
Expect(dependencyManager.DeliverCall.Receives.DestinationPath).To(ContainSubstring("poetry-source"))
187-
Expect(dependencyManager.DeliverCall.Receives.PlatformPath).To(Equal("platform"))
188-
189177
Expect(dependencyManager.GenerateBillOfMaterialsCall.CallCount).To(Equal(1))
190178
Expect(dependencyManager.GenerateBillOfMaterialsCall.Receives.Dependencies).To(Equal([]postal.Dependency{
191179
{
@@ -200,7 +188,7 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
200188

201189
Expect(sbomGenerator.GenerateFromDependencyCall.Receives.Dir).To(Equal(filepath.Join(layersDir, "poetry")))
202190

203-
Expect(installProcess.ExecuteCall.Receives.SrcPath).To(Equal(dependencyManager.DeliverCall.Receives.DestinationPath))
191+
Expect(installProcess.ExecuteCall.Receives.Version).To(ContainSubstring("poetry-dependency-version"))
204192
Expect(installProcess.ExecuteCall.Receives.TargetLayerPath).To(Equal(filepath.Join(layersDir, "poetry")))
205193

206194
Expect(buffer.String()).To(ContainSubstring("Some Buildpack some-version"))
@@ -304,17 +292,6 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
304292
})
305293
})
306294

307-
context("when the dependency cannot be installed", func() {
308-
it.Before(func() {
309-
dependencyManager.DeliverCall.Returns.Error = errors.New("failed to deliver dependency")
310-
})
311-
312-
it("returns an error", func() {
313-
_, err := build(buildContext)
314-
Expect(err).To(MatchError("failed to deliver dependency"))
315-
})
316-
})
317-
318295
context("when the install process returns an error", func() {
319296
it.Before(func() {
320297
installProcess.ExecuteCall.Returns.Error = errors.New("failed to run install process")

fakes/dependency_manager.go

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,6 @@ import (
88
)
99

1010
type DependencyManager struct {
11-
DeliverCall struct {
12-
mutex sync.Mutex
13-
CallCount int
14-
Receives struct {
15-
Dependency postal.Dependency
16-
CnbPath string
17-
DestinationPath string
18-
PlatformPath string
19-
}
20-
Returns struct {
21-
Error error
22-
}
23-
Stub func(postal.Dependency, string, string, string) error
24-
}
2511
GenerateBillOfMaterialsCall struct {
2612
mutex sync.Mutex
2713
CallCount int
@@ -50,19 +36,6 @@ type DependencyManager struct {
5036
}
5137
}
5238

53-
func (f *DependencyManager) Deliver(param1 postal.Dependency, param2 string, param3 string, param4 string) error {
54-
f.DeliverCall.mutex.Lock()
55-
defer f.DeliverCall.mutex.Unlock()
56-
f.DeliverCall.CallCount++
57-
f.DeliverCall.Receives.Dependency = param1
58-
f.DeliverCall.Receives.CnbPath = param2
59-
f.DeliverCall.Receives.DestinationPath = param3
60-
f.DeliverCall.Receives.PlatformPath = param4
61-
if f.DeliverCall.Stub != nil {
62-
return f.DeliverCall.Stub(param1, param2, param3, param4)
63-
}
64-
return f.DeliverCall.Returns.Error
65-
}
6639
func (f *DependencyManager) GenerateBillOfMaterials(param1 ...postal.Dependency) []packit.BOMEntry {
6740
f.GenerateBillOfMaterialsCall.mutex.Lock()
6841
defer f.GenerateBillOfMaterialsCall.mutex.Unlock()

fakes/install_process.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ type InstallProcess struct {
77
mutex sync.Mutex
88
CallCount int
99
Receives struct {
10-
SrcPath string
10+
Version string
1111
TargetLayerPath string
1212
}
1313
Returns struct {
@@ -21,7 +21,7 @@ func (f *InstallProcess) Execute(param1 string, param2 string) error {
2121
f.ExecuteCall.mutex.Lock()
2222
defer f.ExecuteCall.mutex.Unlock()
2323
f.ExecuteCall.CallCount++
24-
f.ExecuteCall.Receives.SrcPath = param1
24+
f.ExecuteCall.Receives.Version = param1
2525
f.ExecuteCall.Receives.TargetLayerPath = param2
2626
if f.ExecuteCall.Stub != nil {
2727
return f.ExecuteCall.Stub(param1, param2)

integration/init_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,11 @@ func TestIntegration(t *testing.T) {
111111
Execute(settings.Config.BuildPlan)
112112
Expect(err).NotTo(HaveOccurred())
113113

114-
SetDefaultEventuallyTimeout(5 * time.Second)
114+
SetDefaultEventuallyTimeout(10 * time.Second)
115115

116116
suite := spec.New("Integration", spec.Report(report.Terminal{}))
117117
suite("Default", testDefault, spec.Parallel())
118118
suite("LayerReuse", testLayerReuse, spec.Parallel())
119+
suite("Versions", testVersions, spec.Parallel())
119120
suite.Run(t)
120121
}

integration/versions_test.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package integration_test
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"testing"
8+
9+
"github.com/paketo-buildpacks/occam"
10+
"github.com/sclevine/spec"
11+
12+
. "github.com/onsi/gomega"
13+
. "github.com/paketo-buildpacks/occam/matchers"
14+
)
15+
16+
func testVersions(t *testing.T, context spec.G, it spec.S) {
17+
var (
18+
Expect = NewWithT(t).Expect
19+
Eventually = NewWithT(t).Eventually
20+
21+
pack occam.Pack
22+
docker occam.Docker
23+
)
24+
25+
it.Before(func() {
26+
pack = occam.NewPack()
27+
docker = occam.NewDocker()
28+
})
29+
30+
context("when the buildpack is run with pack build", func() {
31+
var (
32+
name string
33+
source string
34+
35+
containersMap map[string]interface{}
36+
imagesMap map[string]interface{}
37+
)
38+
39+
it.Before(func() {
40+
var err error
41+
name, err = occam.RandomName()
42+
Expect(err).NotTo(HaveOccurred())
43+
44+
containersMap = map[string]interface{}{}
45+
imagesMap = map[string]interface{}{}
46+
})
47+
48+
it.After(func() {
49+
for containerID := range containersMap {
50+
Expect(docker.Container.Remove.Execute(containerID)).To(Succeed())
51+
}
52+
for imageID := range imagesMap {
53+
Expect(docker.Image.Remove.Execute(imageID)).To(Succeed())
54+
}
55+
Expect(docker.Volume.Remove.Execute(occam.CacheVolumeNames(name))).To(Succeed())
56+
Expect(os.RemoveAll(source)).To(Succeed())
57+
})
58+
59+
it("builds and runs successfully with both provided dependency versions", func() {
60+
var err error
61+
62+
source, err = occam.Source(filepath.Join("testdata", "default_app"))
63+
Expect(err).NotTo(HaveOccurred())
64+
65+
firstPoetryVersion := buildpackInfo.Metadata.Dependencies[0].Version
66+
secondPoetryVersion := buildpackInfo.Metadata.Dependencies[1].Version
67+
68+
Expect(firstPoetryVersion).NotTo(Equal(secondPoetryVersion))
69+
70+
firstImage, firstLogs, err := pack.WithNoColor().Build.
71+
WithPullPolicy("never").
72+
WithBuildpacks(
73+
settings.Buildpacks.CPython.Online,
74+
settings.Buildpacks.Pip.Online,
75+
settings.Buildpacks.Poetry.Online,
76+
settings.Buildpacks.BuildPlan.Online,
77+
).
78+
WithEnv(map[string]string{"BP_POETRY_VERSION": firstPoetryVersion}).
79+
Execute(name, source)
80+
Expect(err).ToNot(HaveOccurred(), firstLogs.String)
81+
82+
imagesMap[firstImage.ID] = nil
83+
84+
Expect(firstLogs).To(ContainLines(
85+
ContainSubstring(fmt.Sprintf(`Selected Poetry version (using BP_POETRY_VERSION): %s`, firstPoetryVersion)),
86+
))
87+
88+
firstContainer, err := docker.Container.Run.
89+
WithCommand("poetry --version").
90+
Execute(firstImage.ID)
91+
Expect(err).ToNot(HaveOccurred())
92+
93+
containersMap[firstContainer.ID] = nil
94+
95+
Eventually(func() string {
96+
cLogs, err := docker.Container.Logs.Execute(firstContainer.ID)
97+
Expect(err).NotTo(HaveOccurred())
98+
return cLogs.String()
99+
}).Should(ContainSubstring(fmt.Sprintf(`Poetry version %s`, firstPoetryVersion)))
100+
101+
secondImage, secondLogs, err := pack.WithNoColor().Build.
102+
WithPullPolicy("never").
103+
WithBuildpacks(
104+
settings.Buildpacks.CPython.Online,
105+
settings.Buildpacks.Pip.Online,
106+
settings.Buildpacks.Poetry.Online,
107+
settings.Buildpacks.BuildPlan.Online,
108+
).
109+
WithEnv(map[string]string{"BP_POETRY_VERSION": secondPoetryVersion}).
110+
Execute(name, source)
111+
Expect(err).ToNot(HaveOccurred(), secondLogs.String)
112+
113+
imagesMap[secondImage.ID] = nil
114+
115+
Expect(secondLogs).To(ContainLines(
116+
ContainSubstring(fmt.Sprintf(`Selected Poetry version (using BP_POETRY_VERSION): %s`, secondPoetryVersion)),
117+
))
118+
119+
secondContainer, err := docker.Container.Run.
120+
WithCommand("poetry --version").
121+
Execute(secondImage.ID)
122+
Expect(err).ToNot(HaveOccurred())
123+
124+
containersMap[secondContainer.ID] = nil
125+
126+
Eventually(func() string {
127+
cLogs, err := docker.Container.Logs.Execute(secondContainer.ID)
128+
Expect(err).NotTo(HaveOccurred())
129+
return cLogs.String()
130+
}).Should(ContainSubstring(fmt.Sprintf(`Poetry version %s`, secondPoetryVersion)))
131+
})
132+
})
133+
}

poetry_install_process.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,13 @@ func NewPoetryInstallProcess(executable Executable) PoetryInstallProcess {
2626
}
2727
}
2828

29-
// Execute installs the poetry binary from source code located in the given
30-
// srcPath into the layer path designated by targetLayerPath.
31-
func (p PoetryInstallProcess) Execute(srcPath, targetLayerPath string) error {
29+
// Execute installs the provided version of pipenv from the internet into the
30+
// layer path designated by targetLayerPath
31+
func (p PoetryInstallProcess) Execute(version, targetLayerPath string) error {
3232
buffer := bytes.NewBuffer(nil)
3333

3434
err := p.executable.Execute(pexec.Execution{
35-
// Install poetry from source with the pip that comes from a previous buildpack
36-
Args: []string{"install", "poetry", "--user", fmt.Sprintf("--find-links=%s", srcPath)},
35+
Args: []string{"install", fmt.Sprintf("poetry==%s", version), "--user"},
3736
// Set the PYTHONUSERBASE to ensure that pip is installed to the newly created target layer.
3837
Env: append(os.Environ(), fmt.Sprintf("PYTHONUSERBASE=%s", targetLayerPath)),
3938
Stdout: buffer,

poetry_install_process_test.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func testPoetryInstallProcess(t *testing.T, context spec.G, it spec.S) {
1818
var (
1919
Expect = NewWithT(t).Expect
2020

21-
srcPath string
21+
version string
2222
destLayerPath string
2323
executable *fakes.Executable
2424

@@ -27,12 +27,11 @@ func testPoetryInstallProcess(t *testing.T, context spec.G, it spec.S) {
2727

2828
it.Before(func() {
2929
var err error
30-
srcPath, err = os.MkdirTemp("", "poetry-source")
31-
Expect(err).NotTo(HaveOccurred())
32-
3330
destLayerPath, err = os.MkdirTemp("", "poetry")
3431
Expect(err).NotTo(HaveOccurred())
3532

33+
version = "1.2.3-some.version"
34+
3635
executable = &fakes.Executable{}
3736

3837
poetryInstallProcess = poetry.NewPoetryInstallProcess(executable)
@@ -41,11 +40,11 @@ func testPoetryInstallProcess(t *testing.T, context spec.G, it spec.S) {
4140
context("Execute", func() {
4241
context("there is a poetry dependency to install", func() {
4342
it("installs it to the poetry layer", func() {
44-
err := poetryInstallProcess.Execute(srcPath, destLayerPath)
43+
err := poetryInstallProcess.Execute(version, destLayerPath)
4544
Expect(err).NotTo(HaveOccurred())
4645

4746
Expect(executable.ExecuteCall.Receives.Execution.Env).To(Equal(append(os.Environ(), fmt.Sprintf("PYTHONUSERBASE=%s", destLayerPath))))
48-
Expect(executable.ExecuteCall.Receives.Execution.Args).To(Equal([]string{"install", "poetry", "--user", fmt.Sprintf("--find-links=%s", srcPath)}))
47+
Expect(executable.ExecuteCall.Receives.Execution.Args).To(Equal([]string{"install", "poetry==1.2.3-some.version", "--user"}))
4948
})
5049
})
5150

@@ -60,7 +59,7 @@ func testPoetryInstallProcess(t *testing.T, context spec.G, it spec.S) {
6059
})
6160

6261
it("returns an error", func() {
63-
err := poetryInstallProcess.Execute(srcPath, destLayerPath)
62+
err := poetryInstallProcess.Execute(version, destLayerPath)
6463
Expect(err).To(MatchError(ContainSubstring("installing poetry failed")))
6564
Expect(err).To(MatchError(ContainSubstring("stdout output")))
6665
Expect(err).To(MatchError(ContainSubstring("stderr output")))

0 commit comments

Comments
 (0)