diff --git a/build.go b/build.go index ef70dfec..b4273327 100644 --- a/build.go +++ b/build.go @@ -331,6 +331,14 @@ func Build(f BuildFunc, options ...Option) { config.exitHandler.Error(err) return } + + for process, processEnv := range layer.ProcessLaunchEnv { + err = config.envWriter.Write(filepath.Join(layer.Path, "env.launch", process), processEnv) + if err != nil { + config.exitHandler.Error(err) + return + } + } } if !result.Launch.isEmpty() { diff --git a/build_test.go b/build_test.go index 5f0020d5..3ca0421c 100644 --- a/build_test.go +++ b/build_test.go @@ -711,6 +711,41 @@ api = "0.4" Expect(string(contents)).To(Equal(fmt.Sprintf("%s-value", modifier))) } }) + it("writes env vars into env.launch/ directory", func() { + packit.Build(func(ctx packit.BuildContext) (packit.BuildResult, error) { + return packit.BuildResult{ + Layers: []packit.Layer{ + { + Path: filepath.Join(ctx.Layers.Path, "some-layer"), + ProcessLaunchEnv: map[string]packit.Environment{ + "process-name": { + "SOME_VAR.append": "append-value", + "SOME_VAR.default": "default-value", + "SOME_VAR.delim": "delim-value", + "SOME_VAR.prepend": "prepend-value", + "SOME_VAR.override": "override-value", + }, + "another-process-name": { + "SOME_VAR.append": "append-value", + "SOME_VAR.default": "default-value", + "SOME_VAR.delim": "delim-value", + "SOME_VAR.prepend": "prepend-value", + "SOME_VAR.override": "override-value", + }, + }, + }, + }, + }, nil + }, packit.WithArgs([]string{binaryPath, layersDir, "", planPath})) + + for _, process := range []string{"process-name", "another-process-name"} { + for _, modifier := range []string{"append", "default", "delim", "prepend", "override"} { + contents, err := ioutil.ReadFile(filepath.Join(layersDir, "some-layer", "env.launch", process, fmt.Sprintf("SOME_VAR.%s", modifier))) + Expect(err).NotTo(HaveOccurred()) + Expect(string(contents)).To(Equal(fmt.Sprintf("%s-value", modifier))) + } + } + }) }) context("writes to build folder", func() { diff --git a/layer.go b/layer.go index 1d1d4b39..06335d4d 100644 --- a/layer.go +++ b/layer.go @@ -48,6 +48,11 @@ type Layer struct { // https://github.com/buildpacks/spec/blob/main/buildpack.md#provided-by-the-buildpacks. LaunchEnv Environment `toml:"-"` + // ProcessLaunchEnv is a map of environment variables attached to the layer and + // made available to specified proccesses in the launch phase accoring to the specification: + // https://github.com/buildpacks/spec/blob/main/buildpack.md#provided-by-the-buildpacks + ProcessLaunchEnv map[string]Environment `toml:"-"` + // Metadata is an unspecified field allowing buildpacks to communicate extra // details about the layer. Examples of this type of metadata might include // details about what versions of software are included in the layer such @@ -69,7 +74,7 @@ func (l Layer) Reset() (Layer, error) { l.SharedEnv = Environment{} l.BuildEnv = Environment{} l.LaunchEnv = Environment{} - + l.ProcessLaunchEnv = make(map[string]Environment) l.Metadata = nil err := os.RemoveAll(l.Path) diff --git a/layer_test.go b/layer_test.go index be19bce3..44fc008e 100644 --- a/layer_test.go +++ b/layer_test.go @@ -49,14 +49,15 @@ func testLayer(t *testing.T, context spec.G, it spec.S) { Expect(err).NotTo(HaveOccurred()) Expect(layer).To(Equal(packit.Layer{ - Name: "some-layer", - Path: filepath.Join(layersDir, "some-layer"), - Launch: false, - Build: false, - Cache: false, - SharedEnv: packit.Environment{}, - BuildEnv: packit.Environment{}, - LaunchEnv: packit.Environment{}, + Name: "some-layer", + Path: filepath.Join(layersDir, "some-layer"), + Launch: false, + Build: false, + Cache: false, + SharedEnv: packit.Environment{}, + BuildEnv: packit.Environment{}, + LaunchEnv: packit.Environment{}, + ProcessLaunchEnv: map[string]packit.Environment{}, })) Expect(filepath.Join(layersDir, "some-layer")).To(BeADirectory()) @@ -118,14 +119,15 @@ func testLayer(t *testing.T, context spec.G, it spec.S) { Expect(err).NotTo(HaveOccurred()) Expect(layer).To(Equal(packit.Layer{ - Name: "some-layer", - Path: filepath.Join(layersDir, "some-layer"), - Launch: false, - Build: false, - Cache: false, - SharedEnv: packit.Environment{}, - BuildEnv: packit.Environment{}, - LaunchEnv: packit.Environment{}, + Name: "some-layer", + Path: filepath.Join(layersDir, "some-layer"), + Launch: false, + Build: false, + Cache: false, + SharedEnv: packit.Environment{}, + BuildEnv: packit.Environment{}, + LaunchEnv: packit.Environment{}, + ProcessLaunchEnv: map[string]packit.Environment{}, })) Expect(filepath.Join(layersDir, "some-layer")).To(BeADirectory()) diff --git a/layers.go b/layers.go index 9d2e61ec..ef4dd27b 100644 --- a/layers.go +++ b/layers.go @@ -2,6 +2,7 @@ package packit import ( "fmt" + "io/ioutil" "os" "path/filepath" @@ -20,11 +21,12 @@ type Layers struct { // disk and returned instead. func (l Layers) Get(name string) (Layer, error) { layer := Layer{ - Path: filepath.Join(l.Path, name), - Name: name, - SharedEnv: Environment{}, - BuildEnv: Environment{}, - LaunchEnv: Environment{}, + Path: filepath.Join(l.Path, name), + Name: name, + SharedEnv: Environment{}, + BuildEnv: Environment{}, + LaunchEnv: Environment{}, + ProcessLaunchEnv: make(map[string]Environment), } _, err := toml.DecodeFile(filepath.Join(l.Path, fmt.Sprintf("%s.toml", name)), &layer) @@ -49,5 +51,21 @@ func (l Layers) Get(name string) (Layer, error) { return Layer{}, err } + if _, err := os.Stat(filepath.Join(l.Path, name, "env.launch")); !os.IsNotExist(err) { + paths, err := ioutil.ReadDir(filepath.Join(l.Path, name, "env.launch")) + if err != nil { + return Layer{}, err + } + + for _, path := range paths { + if path.IsDir() { + layer.ProcessLaunchEnv[path.Name()], err = newEnvironmentFromPath(filepath.Join(l.Path, name, "env.launch", path.Name())) + if err != nil { + return Layer{}, err + } + } + } + } + return layer, nil } diff --git a/layers_test.go b/layers_test.go index e0e63ed5..d4739ec2 100644 --- a/layers_test.go +++ b/layers_test.go @@ -39,11 +39,12 @@ func testLayers(t *testing.T, context spec.G, it spec.S) { layer, err := layers.Get("some-layer") Expect(err).NotTo(HaveOccurred()) Expect(layer).To(Equal(packit.Layer{ - Name: "some-layer", - Path: filepath.Join(layersDir, "some-layer"), - SharedEnv: packit.Environment{}, - BuildEnv: packit.Environment{}, - LaunchEnv: packit.Environment{}, + Name: "some-layer", + Path: filepath.Join(layersDir, "some-layer"), + SharedEnv: packit.Environment{}, + BuildEnv: packit.Environment{}, + LaunchEnv: packit.Environment{}, + ProcessLaunchEnv: map[string]packit.Environment{}, })) }) @@ -63,14 +64,15 @@ some-key = "some-value"`), 0644) layer, err := layers.Get("some-layer") Expect(err).NotTo(HaveOccurred()) Expect(layer).To(Equal(packit.Layer{ - Name: "some-layer", - Path: filepath.Join(layersDir, "some-layer"), - Launch: true, - Build: true, - Cache: true, - SharedEnv: packit.Environment{}, - BuildEnv: packit.Environment{}, - LaunchEnv: packit.Environment{}, + Name: "some-layer", + Path: filepath.Join(layersDir, "some-layer"), + Launch: true, + Build: true, + Cache: true, + SharedEnv: packit.Environment{}, + BuildEnv: packit.Environment{}, + LaunchEnv: packit.Environment{}, + ProcessLaunchEnv: map[string]packit.Environment{}, Metadata: map[string]interface{}{ "some-key": "some-value", }, @@ -108,6 +110,35 @@ some-key = "some-value"`), 0644) err = ioutil.WriteFile(filepath.Join(launchEnvDir, "PREPEND_VAR.delim"), []byte("#"), 0644) Expect(err).NotTo(HaveOccurred()) + + processLaunchEnvDir := filepath.Join(layersDir, "some-layer", "env.launch", "process") + Expect(os.MkdirAll(processLaunchEnvDir, os.ModePerm)).To(Succeed()) + + err = ioutil.WriteFile(filepath.Join(processLaunchEnvDir, "APPEND_VAR.append"), []byte("append-value"), 0644) + Expect(err).NotTo(HaveOccurred()) + + err = ioutil.WriteFile(filepath.Join(processLaunchEnvDir, "APPEND_VAR.delim"), []byte("!"), 0644) + Expect(err).NotTo(HaveOccurred()) + + err = ioutil.WriteFile(filepath.Join(processLaunchEnvDir, "PREPEND_VAR.prepend"), []byte("prepend-value"), 0644) + Expect(err).NotTo(HaveOccurred()) + + err = ioutil.WriteFile(filepath.Join(processLaunchEnvDir, "PREPEND_VAR.delim"), []byte("#"), 0644) + Expect(err).NotTo(HaveOccurred()) + anotherProcessLaunchEnvDir := filepath.Join(layersDir, "some-layer", "env.launch", "another-process") + Expect(os.MkdirAll(anotherProcessLaunchEnvDir, os.ModePerm)).To(Succeed()) + + err = ioutil.WriteFile(filepath.Join(anotherProcessLaunchEnvDir, "APPEND_VAR.append"), []byte("append-value"), 0644) + Expect(err).NotTo(HaveOccurred()) + + err = ioutil.WriteFile(filepath.Join(anotherProcessLaunchEnvDir, "APPEND_VAR.delim"), []byte("!"), 0644) + Expect(err).NotTo(HaveOccurred()) + + err = ioutil.WriteFile(filepath.Join(anotherProcessLaunchEnvDir, "PREPEND_VAR.prepend"), []byte("prepend-value"), 0644) + Expect(err).NotTo(HaveOccurred()) + + err = ioutil.WriteFile(filepath.Join(anotherProcessLaunchEnvDir, "PREPEND_VAR.delim"), []byte("#"), 0644) + Expect(err).NotTo(HaveOccurred()) }) it("returns a layer with the existing metadata", func() { @@ -119,6 +150,20 @@ some-key = "some-value"`), 0644) Launch: true, Build: true, Cache: true, + ProcessLaunchEnv: map[string]packit.Environment{ + "process": { + "APPEND_VAR.append": "append-value", + "APPEND_VAR.delim": "!", + "PREPEND_VAR.prepend": "prepend-value", + "PREPEND_VAR.delim": "#", + }, + "another-process": { + "APPEND_VAR.append": "append-value", + "APPEND_VAR.delim": "!", + "PREPEND_VAR.prepend": "prepend-value", + "PREPEND_VAR.delim": "#", + }, + }, SharedEnv: packit.Environment{ "OVERRIDE_VAR.override": "override-value", },