diff --git a/README.md b/README.md index 6f9e5b7..451f14b 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,15 @@ start command that is generated and may not exist yet. e.g. If `BP_LAUNCHPOINT=./gen/launchpoint.js` and If `BP_VERIFY_LAUNCHPOINT=false`, the buildpack will not verify that the file exists and then set the start command using that file `node gen/launchpoint.js` +## BP_NODE_LAUNCH_REQUIRES_MODULES + +The `BP_NODE_LAUNCH_REQUIRES_MODULES` environment variable configures whether or not the `node_modules` directory is +required at launch. + +This is the case for "bundled" applications, where all dependencies are included in the output folder. + +The `node_modules` folder is required by default, this can be disabled with `BP_NODE_LAUNCH_REQUIRES_MODULES=false`. + ## Enabling reloadable process types You can configure this buildpack to wrap the entrypoint process of your app diff --git a/detect.go b/detect.go index 15ce86e..39b2a4c 100644 --- a/detect.go +++ b/detect.go @@ -35,7 +35,7 @@ func Detect(reloader Reloader) packit.DetectFunc { if !os.IsNotExist(err) { return packit.DetectResult{}, err } - } else { + } else if os.Getenv("BP_NODE_LAUNCH_REQUIRES_MODULES") != "false" { requirements = append(requirements, newLaunchRequirement("node_modules")) } diff --git a/integration.json b/integration.json index a71d587..46f3473 100644 --- a/integration.json +++ b/integration.json @@ -7,5 +7,7 @@ "ubi-nodejs-extension": "github.com/paketo-buildpacks/ubi-nodejs-extension", "node-engine": "github.com/paketo-buildpacks/node-engine", "npm-install": "github.com/paketo-buildpacks/npm-install", - "watchexec": "github.com/paketo-buildpacks/watchexec" + "watchexec": "github.com/paketo-buildpacks/watchexec", + "node-run-script": "github.com/paketo-buildpacks/node-run-script", + "source-removal": "github.com/paketo-buildpacks/source-removal" } diff --git a/integration/bundled_test.go b/integration/bundled_test.go new file mode 100644 index 0000000..fe8138f --- /dev/null +++ b/integration/bundled_test.go @@ -0,0 +1,90 @@ +package integration_test + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/paketo-buildpacks/occam" + "github.com/sclevine/spec" + + . "github.com/onsi/gomega" + . "github.com/paketo-buildpacks/occam/matchers" +) + +func testBundledModules(t *testing.T, context spec.G, it spec.S) { + var ( + Expect = NewWithT(t).Expect + Eventually = NewWithT(t).Eventually + + pack occam.Pack + docker occam.Docker + + image occam.Image + container occam.Container + + name string + source string + + pullPolicy = "never" + ) + + it.Before(func() { + pack = occam.NewPack().WithVerbose().WithNoColor() + docker = occam.NewDocker() + + var err error + name, err = occam.RandomName() + Expect(err).NotTo(HaveOccurred()) + + if settings.Extensions.UbiNodejsExtension.Online != "" { + pullPolicy = "always" + } + }) + + it.After(func() { + Expect(docker.Container.Remove.Execute(container.ID)).To(Succeed()) + Expect(docker.Volume.Remove.Execute(occam.CacheVolumeNames(name))).To(Succeed()) + Expect(docker.Image.Remove.Execute(image.ID)).To(Succeed()) + Expect(os.RemoveAll(source)).To(Succeed()) + }) + + it("builds and runs successfully", func() { + var err error + source, err = occam.Source(filepath.Join("testdata", "bundled")) + Expect(err).NotTo(HaveOccurred()) + + var logs fmt.Stringer + image, logs, err = pack.Build. + WithPullPolicy(pullPolicy). + WithExtensions( + settings.Extensions.UbiNodejsExtension.Online, + ). + WithBuildpacks( + settings.Buildpacks.NodeEngine.Online, + settings.Buildpacks.NPMInstall.Online, + settings.Buildpacks.NodeStart.Online, + settings.Buildpacks.NodeRunScript.Online, + settings.Buildpacks.SourceRemoval.Online, + ). + WithEnv(map[string]string{ + "BP_NODE_RUN_SCRIPTS": "build", + "BP_VERIFY_LAUNCHPOINT": "false", + "BP_NPM_LAUNCH_REQUIRES_MODULES": "false", + "BP_LAUNCHPOINT": ".output/server/index.mjs", + "BP_INCLUDE_FILES": ".output/**/*", + }). + Execute(name, source) + Expect(err).ToNot(HaveOccurred(), logs.String) + + container, err = docker.Container.Run. + WithEnv(map[string]string{"NITRO_PORT": "8080"}). + WithPublish("8080"). + WithPublishAll(). + Execute(image.ID) + Expect(err).NotTo(HaveOccurred()) + + Eventually(container).Should(Serve(ContainSubstring("hello world"))) + }) +} diff --git a/integration/init_test.go b/integration/init_test.go index cdb7220..626ccf5 100644 --- a/integration/init_test.go +++ b/integration/init_test.go @@ -31,6 +31,12 @@ var settings struct { Watchexec struct { Online string } + NodeRunScript struct { + Online string + } + SourceRemoval struct { + Online string + } } Extensions struct { @@ -49,6 +55,8 @@ var settings struct { NPMInstall string `json:"npm-install"` Watchexec string `json:"watchexec"` UbiNodejsExtension string `json:"ubi-nodejs-extension"` + NodeRunScript string `json:"node-run-script"` + SourceRemoval string `json:"source-removal"` } } @@ -104,10 +112,19 @@ func TestIntegration(t *testing.T) { Execute(settings.Config.Watchexec) Expect(err).ToNot(HaveOccurred()) + settings.Buildpacks.NodeRunScript.Online, err = libpakBuildpackStore.Get. + Execute(settings.Config.NodeRunScript) + Expect(err).ToNot(HaveOccurred()) + + settings.Buildpacks.SourceRemoval.Online, err = libpakBuildpackStore.Get. + Execute(settings.Config.SourceRemoval) + Expect(err).ToNot(HaveOccurred()) + suite := spec.New("Integration", spec.Report(report.Terminal{}), spec.Parallel()) suite("Default", testDefault) suite("Launchpoint", testLaunchpoint) suite("ProjectPath", testProjectPath) suite("WithNodeModules", testNodeModules) + suite("BundledModules", testBundledModules) suite.Run(t) } diff --git a/integration/testdata/bundled/nitro.config.js b/integration/testdata/bundled/nitro.config.js new file mode 100644 index 0000000..939b084 --- /dev/null +++ b/integration/testdata/bundled/nitro.config.js @@ -0,0 +1,7 @@ +import { defineNitroConfig } from "nitropack/config" + +export default defineNitroConfig({ + compatibilityDate: "latest", + srcDir: "server", + imports: false +}); \ No newline at end of file diff --git a/integration/testdata/bundled/package.json b/integration/testdata/bundled/package.json new file mode 100644 index 0000000..40b5685 --- /dev/null +++ b/integration/testdata/bundled/package.json @@ -0,0 +1,25 @@ +{ + "name": "bundled_app", + "version": "0.0.0", + "description": "some app", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "nitro build" + }, + "author": "", + "license": "", + "dependencies": { + "leftpad": "~0.0.1" + }, + "devDependencies": { + "h3": "^1.15.4", + "nitropack": "^2.12.4" + }, + "repository": { + "type": "git", + "url": "" + }, + "engines": { + "node": "~22" + } +} diff --git a/integration/testdata/bundled/server/routes/index.js b/integration/testdata/bundled/server/routes/index.js new file mode 100644 index 0000000..b66b692 --- /dev/null +++ b/integration/testdata/bundled/server/routes/index.js @@ -0,0 +1,6 @@ +import { eventHandler } from "h3" +import "leftpad" + +export default eventHandler((event) => { + return "hello world"; +}); \ No newline at end of file