diff --git a/README.md b/README.md index 72550815..0c3b2b0a 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ ## `docker.io/paketobuildpacks/node-engine` -The Node Engine CNB provides the Node binary distribution. The buildpack +The Node Engine CNB provides the Node binary distribution. The buildpack installs the Node binary distribution onto the `$PATH` which makes it available -for subsequent buildpacks and in the final running container. Examples of +for subsequent buildpacks and in the final running container. Examples of buildpacks that might use the Node binary distribution are the [NPM CNB](https://github.com/paketo-buildpacks/npm) and [Yarn Install CNB](https://github.com/paketo-buildpacks/yarn-install) @@ -148,6 +148,13 @@ the `BP_NODE_PROJECT_PATH` environment variable at build time either directly file](https://github.com/buildpacks/spec/blob/main/extensions/project-descriptor.md). This could be useful if your app is a part of a monorepo. +### Exclude python installation during the build process + +To exclude the participation of the cpython during the build process, please use +the `BP_NODE_EXCLUDE_BUILD_PYTHON` environment variable at build time either directly (ex. `pack build my-app --env BP_NODE_EXCLUDE_BUILD_PYTHON`). + +This will skip the installation of python during the build process on the build base image, by not requiring the cpython buildpack through the build plan. + ### Enabling Inspector for Remote Debugging To enable the Inspector set the `BPL_DEBUG_ENABLED` environment variable at launch time. Optionally, you can specify the `BPL_DEBUG_PORT` environment variable to use a specific port. @@ -162,11 +169,13 @@ For more information on debugging, see [Official Documentation](https://nodejs.o ## Run Tests To run all unit tests, run: + ``` ./scripts/unit.sh ``` To run all integration tests, run: + ``` /scripts/integration.sh ``` diff --git a/detect.go b/detect.go index ecf2f04e..6c2a950d 100644 --- a/detect.go +++ b/detect.go @@ -83,10 +83,21 @@ func Detect(nvmrcParser, nodeVersionParser VersionParser) packit.DetectFunc { }) } + bpNodeExcludeBuildPython, bpNodeExcludeBuildPythonExists := os.LookupEnv("BP_NODE_EXCLUDE_BUILD_PYTHON") + excludePython := false + if bpNodeExcludeBuildPythonExists && (bpNodeExcludeBuildPython == "" || bpNodeExcludeBuildPython == "true") { + excludePython = true + } + + _, pythonNotFoundErr := exec.LookPath("python") + _, python2NotFoundErr := exec.LookPath("python2") + _, python3NotFoundErr := exec.LookPath("python3") + + pythonFound := (pythonNotFoundErr == nil || python2NotFoundErr == nil || python3NotFoundErr == nil) + targetOs := os.Getenv("CNB_TARGET_DISTRO_NAME") - _, pythonNotFound := exec.LookPath("python") - installPython := (targetOs != "rhel" && pythonNotFound != nil) + installPython := (targetOs != "rhel" && !pythonFound && !excludePython) if installPython { requirements = append(requirements, packit.BuildPlanRequirement{ Name: Cpython, diff --git a/detect_test.go b/detect_test.go index 405fa7a3..e56dd658 100644 --- a/detect_test.go +++ b/detect_test.go @@ -279,6 +279,161 @@ func testDetect(t *testing.T, context spec.G, it spec.S) { }) }) + context("when $BP_NODE_EXCLUDE_BUILD_PYTHON is NOT set", func() { + var pathEntries string + it.Before(func() { + pathEntries = os.Getenv("PATH") + os.Setenv("PATH", "") + }) + + it.After(func() { + os.Setenv("PATH", pathEntries) + }) + it("and python does NOT exist on the path, it includes cpython buildpack", func() { + + result, err := detect(packit.DetectContext{ + WorkingDir: "/working-dir", + }) + Expect(err).NotTo(HaveOccurred()) + Expect(result.Plan).To(Equal(packit.BuildPlan{ + Provides: []packit.BuildPlanProvision{ + {Name: nodeengine.Node}, + }, + Requires: []packit.BuildPlanRequirement{ + { + Name: nodeengine.Cpython, + Metadata: nodeengine.BuildPlanMetadata{ + Build: true, + Launch: false, + }, + }, + }, + Or: []packit.BuildPlan{ + { + Provides: []packit.BuildPlanProvision{ + {Name: nodeengine.Node}, + {Name: nodeengine.Npm}, + }, + Requires: []packit.BuildPlanRequirement{ + { + Name: nodeengine.Cpython, + Metadata: nodeengine.BuildPlanMetadata{ + Build: true, + Launch: false, + }, + }, + }, + }, + }, + })) + + }) + }) + + context("when $BP_NODE_EXCLUDE_BUILD_PYTHON is NOT set", func() { + + var pathEntries string + it.Before(func() { + pathEntries = os.Getenv("PATH") + os.Setenv("PATH", pathEntries+":/some/path/to/python") + }) + + it.After(func() { + os.Setenv("PATH", pathEntries) + }) + it("and python does exists on the path, it does NOT include on the plan", func() { + + result, err := detect(packit.DetectContext{ + WorkingDir: "/working-dir", + }) + Expect(err).NotTo(HaveOccurred()) + Expect(result.Plan).To(Equal(packit.BuildPlan{ + Provides: []packit.BuildPlanProvision{ + {Name: nodeengine.Node}, + }, + Or: []packit.BuildPlan{ + { + Provides: []packit.BuildPlanProvision{ + {Name: nodeengine.Node}, + {Name: nodeengine.Npm}, + }, + }, + }, + })) + }) + }) + + context("when $BP_NODE_EXCLUDE_BUILD_PYTHON is set", func() { + var pathEntries string + it.Before(func() { + pathEntries = os.Getenv("PATH") + os.Setenv("BP_NODE_EXCLUDE_BUILD_PYTHON", "true") + os.Setenv("PATH", "") + }) + + it.After(func() { + os.Setenv("PATH", pathEntries) + os.Unsetenv("BP_NODE_EXCLUDE_BUILD_PYTHON") + }) + + it("and python does NOT exist on the path, it does NOT include cpython buildpack", func() { + + result, err := detect(packit.DetectContext{ + WorkingDir: "/working-dir", + }) + Expect(err).NotTo(HaveOccurred()) + Expect(result.Plan).To(Equal(packit.BuildPlan{ + Provides: []packit.BuildPlanProvision{ + {Name: nodeengine.Node}, + }, + Or: []packit.BuildPlan{ + { + Provides: []packit.BuildPlanProvision{ + {Name: nodeengine.Node}, + {Name: nodeengine.Npm}, + }, + }, + }, + })) + }) + + }) + context("when $BP_NODE_EXCLUDE_BUILD_PYTHON is set", func() { + + var pathEntries string + it.Before(func() { + pathEntries = os.Getenv("PATH") + os.Setenv("BP_NODE_EXCLUDE_BUILD_PYTHON", "true") + os.Setenv("PATH", pathEntries+":/some/path/to/python") + }) + + it.After(func() { + os.Setenv("PATH", pathEntries) + os.Unsetenv("BP_NODE_EXCLUDE_BUILD_PYTHON") + }) + it("and python exists on the path, it does NOT include cpython buildpack", func() { + + result, err := detect(packit.DetectContext{ + WorkingDir: "/working-dir", + }) + Expect(err).NotTo(HaveOccurred()) + Expect(result.Plan).To(Equal(packit.BuildPlan{ + Provides: []packit.BuildPlanProvision{ + {Name: nodeengine.Node}, + }, + Or: []packit.BuildPlan{ + { + Provides: []packit.BuildPlanProvision{ + {Name: nodeengine.Node}, + {Name: nodeengine.Npm}, + }, + }, + }, + })) + + }) + }) + context("failure cases", func() { context("when the dir specified by BP_NODE_PROJECT_PATH does not exist", func() { var workingDir string diff --git a/integration/inspector_test.go b/integration/inspector_test.go index ad866eee..bf979c4f 100644 --- a/integration/inspector_test.go +++ b/integration/inspector_test.go @@ -52,10 +52,12 @@ func testInspector(t *testing.T, context spec.G, it spec.S) { image, logs, err = pack.WithNoColor().Build. WithPullPolicy("never"). WithBuildpacks( - settings.Buildpacks.Cpython.Online, settings.Buildpacks.NodeEngine.Online, settings.Buildpacks.Processes.Online, ). + WithEnv(map[string]string{ + "BP_NODE_EXCLUDE_BUILD_PYTHON": "", + }). Execute(name, source) Expect(err).NotTo(HaveOccurred()) diff --git a/integration/openssl_test.go b/integration/openssl_test.go index 4dee57f6..20c8df9d 100644 --- a/integration/openssl_test.go +++ b/integration/openssl_test.go @@ -67,13 +67,13 @@ func testOpenSSL(t *testing.T, context spec.G, it spec.S) { image, logs, err = pack.WithNoColor().Build. WithBuildpacks( - settings.Buildpacks.Cpython.Online, settings.Buildpacks.NodeEngine.Online, settings.Buildpacks.BuildPlan.Online, ). WithPullPolicy("never"). WithEnv(map[string]string{ - "BP_NODE_VERSION": "20.*.*", + "BP_NODE_VERSION": "20.*.*", + "BP_NODE_EXCLUDE_BUILD_PYTHON": "", }). Execute(name, source) Expect(err).ToNot(HaveOccurred(), logs.String) @@ -107,13 +107,13 @@ func testOpenSSL(t *testing.T, context spec.G, it spec.S) { image, logs, err = pack.WithNoColor().Build. WithBuildpacks( - settings.Buildpacks.Cpython.Online, settings.Buildpacks.NodeEngine.Online, settings.Buildpacks.BuildPlan.Online, ). WithPullPolicy("never"). WithEnv(map[string]string{ - "BP_NODE_VERSION": "20.*.*", + "BP_NODE_VERSION": "20.*.*", + "BP_NODE_EXCLUDE_BUILD_PYTHON": "", }). Execute(name, source) Expect(err).ToNot(HaveOccurred(), logs.String) diff --git a/integration/optimize_memory_test.go b/integration/optimize_memory_test.go index 237b0777..85bbe057 100644 --- a/integration/optimize_memory_test.go +++ b/integration/optimize_memory_test.go @@ -53,11 +53,13 @@ func testOptimizeMemory(t *testing.T, context spec.G, it spec.S) { image, logs, err = pack.WithNoColor().Build. WithPullPolicy("never"). WithBuildpacks( - settings.Buildpacks.Cpython.Online, settings.Buildpacks.NodeEngine.Online, settings.Buildpacks.Processes.Online, ). - WithEnv(map[string]string{"BP_NODE_OPTIMIZE_MEMORY": "true"}). + WithEnv(map[string]string{ + "BP_NODE_OPTIMIZE_MEMORY": "true", + "BP_NODE_EXCLUDE_BUILD_PYTHON": "", + }). Execute(name, source) Expect(err).NotTo(HaveOccurred()) diff --git a/integration/project_path_test.go b/integration/project_path_test.go index 9cf098f3..977c6f63 100644 --- a/integration/project_path_test.go +++ b/integration/project_path_test.go @@ -60,12 +60,12 @@ func testProjectPath(t *testing.T, context spec.G, it spec.S) { image, logs, err = pack.WithNoColor().Build. WithPullPolicy("never"). WithBuildpacks( - settings.Buildpacks.Cpython.Online, settings.Buildpacks.NodeEngine.Online, settings.Buildpacks.BuildPlan.Online, ). WithEnv(map[string]string{ - "BP_NODE_PROJECT_PATH": "hello_world_server", + "BP_NODE_PROJECT_PATH": "hello_world_server", + "BP_NODE_EXCLUDE_BUILD_PYTHON": "", }). Execute(name, source) Expect(err).ToNot(HaveOccurred(), logs.String) diff --git a/integration/provides_test.go b/integration/provides_test.go index 125d8e45..c2ca08c8 100644 --- a/integration/provides_test.go +++ b/integration/provides_test.go @@ -55,10 +55,12 @@ func testProvides(t *testing.T, context spec.G, it spec.S) { image, logs, err = pack.WithNoColor().Build. WithPullPolicy("never"). WithBuildpacks( - settings.Buildpacks.Cpython.Online, settings.Buildpacks.NodeEngine.Online, settings.Buildpacks.BuildPlan.Online, ). + WithEnv(map[string]string{ + "BP_NODE_EXCLUDE_BUILD_PYTHON": "", + }). Execute(name, source) Expect(err).ToNot(HaveOccurred(), logs.String) diff --git a/integration/reuse_layer_rebuild_test.go b/integration/reuse_layer_rebuild_test.go index 90aa0537..79378fbe 100644 --- a/integration/reuse_layer_rebuild_test.go +++ b/integration/reuse_layer_rebuild_test.go @@ -72,19 +72,19 @@ func testReusingLayerRebuild(t *testing.T, context spec.G, it spec.S) { firstImage, logs, err = pack.WithNoColor().Build. WithPullPolicy("never"). WithBuildpacks( - settings.Buildpacks.Cpython.Online, settings.Buildpacks.NodeEngine.Online, settings.Buildpacks.BuildPlan.Online, ). + WithEnv(map[string]string{"BP_NODE_EXCLUDE_BUILD_PYTHON": ""}). Execute(name, source) Expect(err).NotTo(HaveOccurred()) imageIDs[firstImage.ID] = struct{}{} - Expect(firstImage.Buildpacks).To(HaveLen(3)) - Expect(firstImage.Buildpacks[1].Key).To(Equal(settings.Buildpack.ID)) - Expect(firstImage.Buildpacks[1].Layers).To(HaveKey("node")) + Expect(firstImage.Buildpacks).To(HaveLen(2)) + Expect(firstImage.Buildpacks[0].Key).To(Equal(settings.Buildpack.ID)) + Expect(firstImage.Buildpacks[0].Layers).To(HaveKey("node")) Expect(logs).To(ContainLines( fmt.Sprintf("%s 1.2.3", settings.Buildpack.Name), @@ -139,18 +139,18 @@ func testReusingLayerRebuild(t *testing.T, context spec.G, it spec.S) { secondImage, logs, err = pack.WithNoColor().Build. WithPullPolicy("never"). WithBuildpacks( - settings.Buildpacks.Cpython.Online, settings.Buildpacks.NodeEngine.Online, settings.Buildpacks.BuildPlan.Online, ). + WithEnv(map[string]string{"BP_NODE_EXCLUDE_BUILD_PYTHON": ""}). Execute(name, source) Expect(err).NotTo(HaveOccurred()) imageIDs[secondImage.ID] = struct{}{} - Expect(secondImage.Buildpacks).To(HaveLen(3)) - Expect(secondImage.Buildpacks[1].Key).To(Equal(settings.Buildpack.ID)) - Expect(secondImage.Buildpacks[1].Layers).To(HaveKey("node")) + Expect(secondImage.Buildpacks).To(HaveLen(2)) + Expect(secondImage.Buildpacks[0].Key).To(Equal(settings.Buildpack.ID)) + Expect(secondImage.Buildpacks[0].Layers).To(HaveKey("node")) Expect(logs).To(ContainLines( fmt.Sprintf("%s 1.2.3", settings.Buildpack.Name), @@ -184,8 +184,7 @@ func testReusingLayerRebuild(t *testing.T, context spec.G, it spec.S) { Expect(err).NotTo(HaveOccurred()) Expect(content).To(ContainSubstring("hello world")) - Expect(secondImage.Buildpacks[0].Layers["cpython"].SHA).To(Equal(firstImage.Buildpacks[0].Layers["cpython"].SHA)) - Expect(secondImage.Buildpacks[1].Layers["node"].SHA).To(Equal(firstImage.Buildpacks[1].Layers["node"].SHA)) + Expect(secondImage.Buildpacks[0].Layers["node"].SHA).To(Equal(firstImage.Buildpacks[0].Layers["node"].SHA)) }) }) @@ -207,19 +206,21 @@ func testReusingLayerRebuild(t *testing.T, context spec.G, it spec.S) { firstImage, logs, err = pack.WithNoColor().Build. WithPullPolicy("never"). WithBuildpacks( - settings.Buildpacks.Cpython.Online, settings.Buildpacks.NodeEngine.Online, settings.Buildpacks.BuildPlan.Online, ). - WithEnv(map[string]string{"BP_NODE_VERSION": "~20"}). + WithEnv(map[string]string{ + "BP_NODE_VERSION": "~20", + "BP_NODE_EXCLUDE_BUILD_PYTHON": "", + }). Execute(name, source) Expect(err).NotTo(HaveOccurred()) imageIDs[firstImage.ID] = struct{}{} - Expect(firstImage.Buildpacks).To(HaveLen(3)) - Expect(firstImage.Buildpacks[1].Key).To(Equal(settings.Buildpack.ID)) - Expect(firstImage.Buildpacks[1].Layers).To(HaveKey("node")) + Expect(firstImage.Buildpacks).To(HaveLen(2)) + Expect(firstImage.Buildpacks[0].Key).To(Equal(settings.Buildpack.ID)) + Expect(firstImage.Buildpacks[0].Layers).To(HaveKey("node")) Expect(logs).To(ContainLines( fmt.Sprintf("%s 1.2.3", settings.Buildpack.Name), @@ -275,19 +276,21 @@ func testReusingLayerRebuild(t *testing.T, context spec.G, it spec.S) { secondImage, logs, err = pack.WithNoColor().Build. WithPullPolicy("never"). WithBuildpacks( - settings.Buildpacks.Cpython.Online, settings.Buildpacks.NodeEngine.Online, settings.Buildpacks.BuildPlan.Online, ). - WithEnv(map[string]string{"BP_NODE_VERSION": "~22"}). + WithEnv(map[string]string{ + "BP_NODE_VERSION": "~22", + "BP_NODE_EXCLUDE_BUILD_PYTHON": "", + }). Execute(name, source) Expect(err).NotTo(HaveOccurred()) imageIDs[secondImage.ID] = struct{}{} - Expect(secondImage.Buildpacks).To(HaveLen(3)) - Expect(secondImage.Buildpacks[1].Key).To(Equal(settings.Buildpack.ID)) - Expect(secondImage.Buildpacks[1].Layers).To(HaveKey("node")) + Expect(secondImage.Buildpacks).To(HaveLen(2)) + Expect(secondImage.Buildpacks[0].Key).To(Equal(settings.Buildpack.ID)) + Expect(secondImage.Buildpacks[0].Layers).To(HaveKey("node")) Expect(logs).To(ContainLines( fmt.Sprintf("%s 1.2.3", settings.Buildpack.Name), @@ -347,7 +350,7 @@ func testReusingLayerRebuild(t *testing.T, context spec.G, it spec.S) { Expect(err).NotTo(HaveOccurred()) Expect(content).To(ContainSubstring("hello world")) - Expect(secondImage.Buildpacks[1].Layers["node"].SHA).NotTo(Equal(firstImage.Buildpacks[1].Layers["node"].SHA)) + Expect(secondImage.Buildpacks[0].Layers["node"].SHA).NotTo(Equal(firstImage.Buildpacks[0].Layers["node"].SHA)) }) }) } diff --git a/integration/simple_app_test.go b/integration/simple_app_test.go index 869392c2..eddd8d4b 100644 --- a/integration/simple_app_test.go +++ b/integration/simple_app_test.go @@ -82,6 +82,19 @@ func testSimple(t *testing.T, context spec.G, it spec.S) { Execute(name, source) Expect(err).ToNot(HaveOccurred(), logs.String) + Expect(logs).To(ContainLines( + MatchRegexp(` Selected CPython version \(using \): \d+\.\d+\.\d+`), + )) + Expect(logs).To(ContainLines( + " Executing build process", + MatchRegexp(` Installing CPython \d+\.\d+\.\d+`), + MatchRegexp(` Completed in \d+(\.\d+)?`), + )) + + Expect(logs).To(ContainLines( + " Generating SBOM for /layers/paketo-buildpacks_cpython/cpython", + )) + Expect(logs).To(ContainLines( fmt.Sprintf("%s 1.2.3", settings.Buildpack.Name), " Resolving Node Engine version", @@ -184,10 +197,10 @@ func testSimple(t *testing.T, context spec.G, it spec.S) { WithPullPolicy("never"). WithEnv(map[string]string{"NODE_ENV": "development", "NODE_VERBOSE": "true"}). WithBuildpacks( - settings.Buildpacks.Cpython.Online, settings.Buildpacks.NodeEngine.Online, settings.Buildpacks.BuildPlan.Online, ). + WithEnv(map[string]string{"BP_NODE_EXCLUDE_BUILD_PYTHON": ""}). Execute(name, source) Expect(err).ToNot(HaveOccurred(), logs.String) @@ -250,10 +263,10 @@ func testSimple(t *testing.T, context spec.G, it spec.S) { image, logs, err = pack.WithNoColor().Build. WithPullPolicy("never"). WithBuildpacks( - settings.Buildpacks.Cpython.Online, settings.Buildpacks.NodeEngine.Online, settings.Buildpacks.BuildPlan.Online, ). + WithEnv(map[string]string{"BP_NODE_EXCLUDE_BUILD_PYTHON": ""}). Execute(name, source) Expect(err).ToNot(HaveOccurred(), logs.String) @@ -337,10 +350,10 @@ func testSimple(t *testing.T, context spec.G, it spec.S) { image, logs, err = pack.WithNoColor().Build. WithPullPolicy("never"). WithBuildpacks( - settings.Buildpacks.Cpython.Online, settings.Buildpacks.NodeEngine.Online, settings.Buildpacks.BuildPlan.Online, ). + WithEnv(map[string]string{"BP_NODE_EXCLUDE_BUILD_PYTHON": ""}). Execute(name, source) Expect(err).ToNot(HaveOccurred(), logs.String) @@ -419,10 +432,10 @@ func testSimple(t *testing.T, context spec.G, it spec.S) { image, logs, err = pack.WithNoColor().Build. WithPullPolicy("never"). WithBuildpacks( - settings.Buildpacks.Cpython.Online, settings.Buildpacks.NodeEngine.Deprecated, settings.Buildpacks.BuildPlan.Online, ). + WithEnv(map[string]string{"BP_NODE_EXCLUDE_BUILD_PYTHON": ""}). Execute(name, source) Expect(err).ToNot(HaveOccurred(), logs.String) @@ -448,14 +461,14 @@ func testSimple(t *testing.T, context spec.G, it spec.S) { image, logs, err = pack.WithNoColor().Build. WithPullPolicy("never"). WithBuildpacks( - settings.Buildpacks.Cpython.Online, settings.Buildpacks.NodeEngine.Online, settings.Buildpacks.BuildPlan.Online, ). WithSBOMOutputDir(sbomDir). WithEnv(map[string]string{ - "BP_LOG_LEVEL": "DEBUG", - "BP_DISABLE_SBOM": "true", + "BP_LOG_LEVEL": "DEBUG", + "BP_DISABLE_SBOM": "true", + "BP_NODE_EXCLUDE_BUILD_PYTHON": "", }). Execute(name, source) Expect(err).ToNot(HaveOccurred(), logs.String)