From d08707460743450fd904c40dcf4dc0ad9d3ec687 Mon Sep 17 00:00:00 2001 From: Jesse Brown Date: Thu, 22 Sep 2022 14:40:41 -0500 Subject: [PATCH 01/15] Allow storing multiple commands instead of a single string (#900) * Allow storing multiple commands instead of a single string This is a first step to implementing #322. This PR is updating our internal structs to allow for a slice of commands while keeping the external API and behavior the same. A future PR will implement handling multiple commands depending on the API version and changing the behavior of the launcher and metadat file output changes. Signed-off-by: Jesse Brown * Comment on usage of cmp.Option Signed-off-by: Jesse Brown * Add comment on Matches interface usage for testing Signed-off-by: Jesse Brown * Added test for launch.toml decoding branching logic Signed-off-by: Jesse Brown * Apply suggestions from code review Signed-off-by: Jesse Brown Co-authored-by: Natalie Arellano Signed-off-by: Jesse Brown * fixup! Apply suggestions from code review * fixup! Apply suggestions from code review * Fix editing daemon settings Signed-off-by: Natalie Arellano Signed-off-by: Jesse Brown Signed-off-by: Natalie Arellano Co-authored-by: Natalie Arellano --- builder_test.go | 74 +++++++++++++++++-------------- buildpack/build_test.go | 24 ++++++---- buildpack/files.go | 10 +---- cmd/launcher/cli/launcher.go | 3 +- exporter.go | 3 +- exporter_test.go | 4 +- launch/decode_test.go | 86 ++++++++++++++++++++++++++++++++++++ launch/launch.go | 83 +++++++++++++++++++++++++++++++--- launch/launcher.go | 4 +- launch/launcher_test.go | 6 +-- launch/process.go | 4 +- launch/process_test.go | 51 ++++++++++++--------- launch/shell.go | 7 ++- platform/files.go | 58 +++++++++++++++++++++++- testhelpers/testhelpers.go | 4 +- 15 files changed, 322 insertions(+), 99 deletions(-) create mode 100644 launch/decode_test.go diff --git a/builder_test.go b/builder_test.go index 4b92711e4..f47c3636b 100644 --- a/builder_test.go +++ b/builder_test.go @@ -14,6 +14,7 @@ import ( "github.com/apex/log/handlers/memory" "github.com/golang/mock/gomock" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/pkg/errors" "github.com/sclevine/spec" "github.com/sclevine/spec/report" @@ -37,6 +38,11 @@ func TestBuilder(t *testing.T) { //go:generate mockgen -package testmock -destination testmock/dir_store.go github.com/buildpacks/lifecycle DirStore //go:generate mockgen -package testmock -destination testmock/build_module.go github.com/buildpacks/lifecycle/buildpack BuildModule +// RawCommandValue should be ignored because it is a toml.Primitive that has not been exported. +var processCmpOpts = []cmp.Option{ + cmpopts.IgnoreFields(launch.Process{}, "RawCommandValue"), +} + func testBuilder(t *testing.T, when spec.G, it spec.S) { var ( builder *lifecycle.Builder @@ -494,14 +500,14 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { Processes: []launch.Process{ { Type: "some-type", - Command: "some-command", + Command: []string{"some-command"}, Args: []string{"some-arg"}, Direct: true, BuildpackID: "A", }, { Type: "override-type", - Command: "bpA-command", + Command: []string{"bpA-command"}, Args: []string{"bpA-arg"}, Direct: true, BuildpackID: "A", @@ -514,14 +520,14 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { Processes: []launch.Process{ { Type: "some-other-type", - Command: "some-other-command", + Command: []string{"some-other-command"}, Args: []string{"some-other-arg"}, Direct: true, BuildpackID: "B", }, { Type: "override-type", - Command: "bpB-command", + Command: []string{"bpB-command"}, Args: []string{"bpB-arg"}, Direct: false, BuildpackID: "B", @@ -534,26 +540,26 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { if s := cmp.Diff(metadata.Processes, []launch.Process{ { Type: "override-type", - Command: "bpB-command", + Command: []string{"bpB-command"}, Args: []string{"bpB-arg"}, Direct: false, BuildpackID: "B", }, { Type: "some-other-type", - Command: "some-other-command", + Command: []string{"some-other-command"}, Args: []string{"some-other-arg"}, Direct: true, BuildpackID: "B", }, { Type: "some-type", - Command: "some-command", + Command: []string{"some-command"}, Args: []string{"some-arg"}, Direct: true, BuildpackID: "A", }, - }); s != "" { + }, processCmpOpts...); s != "" { t.Fatalf("Unexpected:\n%s\n", s) } h.AssertEq(t, metadata.BuildpackDefaultProcessType, "") @@ -575,7 +581,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { Processes: []launch.Process{ { Type: "override-type", - Command: "bpA-command", + Command: []string{"bpA-command"}, Args: []string{"bpA-arg"}, Direct: true, BuildpackID: "A", @@ -589,7 +595,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { Processes: []launch.Process{ { Type: "some-type", - Command: "bpB-command", + Command: []string{"bpB-command"}, Args: []string{"bpB-arg"}, Direct: false, BuildpackID: "B", @@ -604,7 +610,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { Processes: []launch.Process{ { Type: "override-type", - Command: "bpC-command", + Command: []string{"bpC-command"}, Args: []string{"bpC-arg"}, Direct: false, BuildpackID: "C", @@ -618,19 +624,19 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { if s := cmp.Diff(metadata.Processes, []launch.Process{ { Type: "override-type", - Command: "bpC-command", + Command: []string{"bpC-command"}, Args: []string{"bpC-arg"}, Direct: false, BuildpackID: "C", }, { Type: "some-type", - Command: "bpB-command", + Command: []string{"bpB-command"}, Args: []string{"bpB-arg"}, Direct: false, BuildpackID: "B", }, - }); s != "" { + }, processCmpOpts...); s != "" { t.Fatalf("Unexpected:\n%s\n", s) } h.AssertEq(t, metadata.BuildpackDefaultProcessType, "some-type") @@ -653,7 +659,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { Processes: []launch.Process{ { Type: "some-type", - Command: "bpA-command", + Command: []string{"bpA-command"}, Args: []string{"bpA-arg"}, Direct: false, BuildpackID: "A", @@ -668,7 +674,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { Processes: []launch.Process{ { Type: "override-type", - Command: "bpB-command", + Command: []string{"bpB-command"}, Args: []string{"bpB-arg"}, Direct: true, BuildpackID: "B", @@ -683,7 +689,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { Processes: []launch.Process{ { Type: "override-type", - Command: "bpC-command", + Command: []string{"bpC-command"}, Args: []string{"bpC-arg"}, Direct: false, BuildpackID: "C", @@ -696,19 +702,19 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { if s := cmp.Diff(metadata.Processes, []launch.Process{ { Type: "override-type", - Command: "bpC-command", + Command: []string{"bpC-command"}, Args: []string{"bpC-arg"}, Direct: false, BuildpackID: "C", }, { Type: "some-type", - Command: "bpA-command", + Command: []string{"bpA-command"}, Args: []string{"bpA-arg"}, Direct: false, BuildpackID: "A", }, - }); s != "" { + }, processCmpOpts...); s != "" { t.Fatalf("Unexpected:\n%s\n", s) } @@ -734,7 +740,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { Processes: []launch.Process{ { Type: "web", - Command: "web-cmd", + Command: []string{"web-cmd"}, Args: []string{"web-arg"}, Direct: false, BuildpackID: "A", @@ -749,13 +755,13 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { if s := cmp.Diff(metadata.Processes, []launch.Process{ { Type: "web", - Command: "web-cmd", + Command: []string{"web-cmd"}, Args: []string{"web-arg"}, Direct: false, BuildpackID: "A", Default: false, }, - }); s != "" { + }, processCmpOpts...); s != "" { t.Fatalf("Unexpected:\n%s\n", s) } h.AssertEq(t, metadata.BuildpackDefaultProcessType, "") @@ -776,7 +782,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { Processes: []launch.Process{ { Type: "web", - Command: "web-cmd", + Command: []string{"web-cmd"}, Args: []string{"web-arg"}, Direct: false, BuildpackID: "A", @@ -784,7 +790,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { }, { Type: "not-web", - Command: "not-web-cmd", + Command: []string{"not-web-cmd"}, Args: []string{"not-web-arg"}, Direct: true, BuildpackID: "A", @@ -799,19 +805,19 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { if s := cmp.Diff(metadata.Processes, []launch.Process{ { Type: "not-web", - Command: "not-web-cmd", + Command: []string{"not-web-cmd"}, Args: []string{"not-web-arg"}, Direct: true, BuildpackID: "A", }, { Type: "web", - Command: "web-cmd", + Command: []string{"web-cmd"}, Args: []string{"web-arg"}, Direct: false, BuildpackID: "A", }, - }); s != "" { + }, processCmpOpts...); s != "" { t.Fatalf("Unexpected:\n%s\n", s) } h.AssertEq(t, metadata.BuildpackDefaultProcessType, "web") @@ -953,7 +959,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { Processes: []launch.Process{ { Type: "web", - Command: "web-cmd", + Command: []string{"web-cmd"}, Args: []string{"web-arg"}, Direct: false, BuildpackID: "A", @@ -968,13 +974,13 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { if s := cmp.Diff(metadata.Processes, []launch.Process{ { Type: "web", - Command: "web-cmd", + Command: []string{"web-cmd"}, Args: []string{"web-arg"}, Direct: false, BuildpackID: "A", Default: false, }, - }); s != "" { + }, processCmpOpts...); s != "" { t.Fatalf("Unexpected:\n%s\n", s) } h.AssertEq(t, metadata.BuildpackDefaultProcessType, "") @@ -995,7 +1001,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { Processes: []launch.Process{ { Type: "web", - Command: "web-cmd", + Command: []string{"web-cmd"}, Args: []string{"web-arg"}, Direct: false, BuildpackID: "A", @@ -1010,13 +1016,13 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { if s := cmp.Diff(metadata.Processes, []launch.Process{ { Type: "web", - Command: "web-cmd", + Command: []string{"web-cmd"}, Args: []string{"web-arg"}, Direct: false, BuildpackID: "A", Default: false, }, - }); s != "" { + }, processCmpOpts...); s != "" { t.Fatalf("Unexpected:\n%s\n", s) } h.AssertEq(t, metadata.BuildpackDefaultProcessType, "") diff --git a/buildpack/build_test.go b/buildpack/build_test.go index a6b0f97e6..fe8a5ed7f 100644 --- a/buildpack/build_test.go +++ b/buildpack/build_test.go @@ -15,6 +15,7 @@ import ( "github.com/apex/log/handlers/memory" "github.com/golang/mock/gomock" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/sclevine/spec" "github.com/sclevine/spec/report" @@ -35,6 +36,11 @@ func TestBuild(t *testing.T) { } } +// RawCommandValue should be ignored because it is a toml.Primitive that has not been exported. +var processCmpOpts = []cmp.Option{ + cmpopts.IgnoreFields(launch.Process{}, "RawCommandValue"), +} + func testBuild(kind string) func(t *testing.T, when spec.G, it spec.S) { return func(t *testing.T, when spec.G, it spec.S) { var ( @@ -769,9 +775,9 @@ func testBuild(kind string) func(t *testing.T, when spec.G, it spec.S) { h.AssertNil(t, err) h.AssertEq(t, br.Processes, []launch.Process{ - {Type: "some-type", Command: "some-cmd", BuildpackID: "A", Default: true, Direct: true}, - {Type: "web", Command: "other-cmd", BuildpackID: "A", Default: false, Direct: true}, - }) + {Type: "some-type", Command: []string{"some-cmd"}, BuildpackID: "A", Default: true, Direct: true}, + {Type: "web", Command: []string{"other-cmd"}, BuildpackID: "A", Default: false, Direct: true}, + }, processCmpOpts...) }) when("there is more than one default=true process", func() { @@ -832,7 +838,7 @@ func testBuild(kind string) func(t *testing.T, when spec.G, it spec.S) { br, err := descriptor.Build(buildpack.Plan{}, config, mockEnv) h.AssertNil(t, err) h.AssertEq(t, len(br.Processes), 1) - h.AssertEq(t, br.Processes[0].Command, "some-cmd") + h.AssertEq(t, br.Processes[0].Command, []string{"some-cmd"}) h.AssertEq(t, br.Processes[0].Args[0], "cmd-arg") h.AssertEq(t, br.Processes[0].Args[1], "first-arg") }) @@ -847,7 +853,7 @@ func testBuild(kind string) func(t *testing.T, when spec.G, it spec.S) { br, err := descriptor.Build(buildpack.Plan{}, config, mockEnv) h.AssertNil(t, err) h.AssertEq(t, len(br.Processes), 1) - h.AssertEq(t, br.Processes[0].Command, "some-cmd") + h.AssertEq(t, br.Processes[0].Command, []string{"some-cmd"}) h.AssertEq(t, br.Processes[0].Direct, true) }) @@ -890,7 +896,7 @@ func testBuild(kind string) func(t *testing.T, when spec.G, it spec.S) { br, err := descriptor.Build(buildpack.Plan{}, config, mockEnv) h.AssertNil(t, err) h.AssertEq(t, len(br.Processes), 1) - h.AssertEq(t, br.Processes[0].Command, "some-command") + h.AssertEq(t, br.Processes[0].Command, []string{"some-command"}) }) it("sets the working directory", func() { @@ -1254,9 +1260,9 @@ func testBuild(kind string) func(t *testing.T, when spec.G, it spec.S) { h.AssertNil(t, err) h.AssertEq(t, br.Processes, []launch.Process{ - {Type: "type-with-no-default", Command: "some-cmd", BuildpackID: "A", Default: false}, - {Type: "type-with-default", Command: "other-cmd", BuildpackID: "A", Default: false}, - }) + {Type: "type-with-no-default", Command: []string{"some-cmd"}, BuildpackID: "A", Default: false}, + {Type: "type-with-default", Command: []string{"other-cmd"}, BuildpackID: "A", Default: false}, + }, processCmpOpts...) expected := "Warning: default processes aren't supported in this buildpack api version. Overriding the default value to false for the following processes: [type-with-default]" assertLogEntry(t, logHandler, expected) }) diff --git a/buildpack/files.go b/buildpack/files.go index b389fa39f..a84cebbf1 100644 --- a/buildpack/files.go +++ b/buildpack/files.go @@ -74,14 +74,6 @@ func DecodeLaunchTOML(launchPath string, bpAPI string, launchTOML *LaunchTOML) e // ToLaunchProcess converts a buildpack.ProcessEntry to a launch.Process func (p *ProcessEntry) ToLaunchProcess(bpID string) launch.Process { - // turn the command collection into a single command + args - // for the current platform API - // note: this will change once the platform API takes a collection of commands - var command string - if len(p.Command) > 0 { - command = p.Command[0] - } - var args []string if len(p.Command) > 1 { args = p.Command[1:] @@ -98,7 +90,7 @@ func (p *ProcessEntry) ToLaunchProcess(bpID string) launch.Process { return launch.Process{ Type: p.Type, - Command: command, + Command: []string{p.Command[0]}, Args: append(args, p.Args...), Direct: direct, // launch.Process requires a value Default: p.Default, diff --git a/cmd/launcher/cli/launcher.go b/cmd/launcher/cli/launcher.go index 706734fa0..a348b0ceb 100644 --- a/cmd/launcher/cli/launcher.go +++ b/cmd/launcher/cli/launcher.go @@ -6,7 +6,6 @@ import ( "strconv" "strings" - "github.com/BurntSushi/toml" "github.com/heroku/color" "github.com/buildpacks/lifecycle/api" @@ -27,7 +26,7 @@ func RunLaunch() error { p := platform.NewPlatform(platformAPI) var md launch.Metadata - if _, err := toml.DecodeFile(launch.GetMetadataFilePath(cmd.EnvOrDefault(platform.EnvLayersDir, platform.DefaultLayersDir)), &md); err != nil { + if err := launch.DecodeLaunchMetadataTOML(launch.GetMetadataFilePath(cmd.EnvOrDefault(platform.EnvLayersDir, platform.DefaultLayersDir)), &md); err != nil { return cmd.FailErr(err, "read metadata") } if err := verifyBuildpackAPIs(md.Buildpacks); err != nil { diff --git a/exporter.go b/exporter.go index 7e0c50c53..ba174fd1f 100644 --- a/exporter.go +++ b/exporter.go @@ -87,10 +87,9 @@ func (e *Exporter) Export(opts ExportOptions) (platform.ExportReport, error) { meta.Stack = opts.Stack buildMD := &platform.BuildMetadata{} - if _, err := toml.DecodeFile(launch.GetMetadataFilePath(opts.LayersDir), buildMD); err != nil { + if err := platform.DecodeBuildMetadataTOML(launch.GetMetadataFilePath(opts.LayersDir), e.PlatformAPI, buildMD); err != nil { return platform.ExportReport{}, errors.Wrap(err, "read build metadata") } - buildMD.PlatformAPI = e.PlatformAPI // buildpack-provided layers if err := e.addBuildpackLayers(opts, &meta); err != nil { diff --git a/exporter_test.go b/exporter_test.go index ee098f6c3..1baf1917a 100644 --- a/exporter_test.go +++ b/exporter_test.go @@ -109,7 +109,7 @@ func testExporter(t *testing.T, when spec.G, it spec.S) { Processes: []launch.Process{ { Type: "some-process-type", - Command: "/some/command", + Command: []string{"/some/command"}, Args: []string{"some", "command", "args"}, Direct: true, BuildpackID: "buildpack.id", @@ -1134,7 +1134,7 @@ version = "4.5.6" Processes: []launch.Process{ { Type: "some-process-type", - Command: "/some/command", + Command: []string{"/some/command"}, Args: []string{"some", "command", "args"}, Direct: true, BuildpackID: "buildpack.id", diff --git a/launch/decode_test.go b/launch/decode_test.go new file mode 100644 index 000000000..9c081efb5 --- /dev/null +++ b/launch/decode_test.go @@ -0,0 +1,86 @@ +package launch_test + +import ( + "io/ioutil" + "math/rand" + "os" + "path/filepath" + "testing" + "time" + + "github.com/sclevine/spec" + "github.com/sclevine/spec/report" + + "github.com/buildpacks/lifecycle/launch" + h "github.com/buildpacks/lifecycle/testhelpers" +) + +func TestDecodeMetadataTOML(t *testing.T) { + rand.Seed(time.Now().UTC().UnixNano()) + spec.Run(t, "DecodeMetadataTOML", testDecodeMetataTOML, spec.Sequential(), spec.Report(report.Terminal{})) +} + +func testDecodeMetataTOML(t *testing.T, when spec.G, it spec.S) { + when("DecodeLaunchMetadataTOML", func() { + var ( + tmpDir string + ) + + it.Before(func() { + var err error + tmpDir, err = ioutil.TempDir("", "test-decode-metadata-toml") + h.AssertNil(t, err) + }) + + it.After(func() { + h.AssertNil(t, os.RemoveAll(tmpDir)) + }) + + it("decodes array commands into command array", func() { + path := filepath.Join(tmpDir, "launch.toml") + h.Mkfile(t, + `[[processes]]`+"\n"+ + `type = "some-type"`+"\n"+ + `command = ["some-cmd", "more"]`+"\n"+ + `default = true`+"\n"+ + `[[processes]]`+"\n"+ + `type = "web"`+"\n"+ + `command = ["other cmd with spaces", "other more"]`+"\n", + // default is false and therefore doesn't appear + filepath.Join(tmpDir, "launch.toml"), + ) + + metadata := launch.Metadata{} + + h.AssertNil(t, launch.DecodeLaunchMetadataTOML(path, &metadata)) + h.AssertEq(t, metadata.Processes[0].Command[0], "some-cmd") + h.AssertEq(t, metadata.Processes[0].Command[1], "more") + + h.AssertEq(t, metadata.Processes[1].Command[0], "other cmd with spaces") + h.AssertEq(t, metadata.Processes[1].Command[1], "other more") + }) + + when("string commands", func() { + it("decodes string commands into command array", func() { + path := filepath.Join(tmpDir, "launch.toml") + h.Mkfile(t, + `[[processes]]`+"\n"+ + `type = "some-type"`+"\n"+ + `command = "some-cmd"`+"\n"+ + `default = true`+"\n"+ + `[[processes]]`+"\n"+ + `type = "web"`+"\n"+ + `command = "other cmd with spaces"`+"\n", + // default is false and therefore doesn't appear + filepath.Join(tmpDir, "launch.toml"), + ) + + metadata := launch.Metadata{} + + h.AssertNil(t, launch.DecodeLaunchMetadataTOML(path, &metadata)) + h.AssertEq(t, metadata.Processes[0].Command[0], "some-cmd") + h.AssertEq(t, metadata.Processes[1].Command[0], "other cmd with spaces") + }) + }) + }) +} diff --git a/launch/launch.go b/launch/launch.go index 9a05a1b02..ae43f89e5 100644 --- a/launch/launch.go +++ b/launch/launch.go @@ -1,19 +1,25 @@ package launch import ( + "fmt" "path" "path/filepath" "strings" + + "github.com/BurntSushi/toml" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" ) type Process struct { - Type string `toml:"type" json:"type"` - Command string `toml:"command" json:"command"` - Args []string `toml:"args" json:"args"` - Direct bool `toml:"direct" json:"direct"` - Default bool `toml:"default,omitempty" json:"default,omitempty"` - BuildpackID string `toml:"buildpack-id" json:"buildpackID"` - WorkingDirectory string `toml:"working-dir,omitempty" json:"working-dir,omitempty"` + Type string `toml:"type" json:"type"` + Command []string `toml:"-" json:"-"` // ignored + RawCommandValue toml.Primitive `toml:"command" json:"command"` + Args []string `toml:"args" json:"args"` + Direct bool `toml:"direct" json:"direct"` + Default bool `toml:"default,omitempty" json:"default,omitempty"` + BuildpackID string `toml:"buildpack-id" json:"buildpackID"` + WorkingDirectory string `toml:"working-dir,omitempty" json:"working-dir,omitempty"` } func (p Process) NoDefault() Process { @@ -31,6 +37,69 @@ type Metadata struct { Buildpacks []Buildpack `toml:"buildpacks" json:"buildpacks"` } +// DecodeLaunchMetadataTOML reads a launch.toml file +func DecodeLaunchMetadataTOML(path string, launchmd *Metadata) error { + // decode the common bits + md, err := toml.DecodeFile(path, &launchmd) + if err != nil { + return err + } + + if err = DecodeProcesses(launchmd.Processes, md); err != nil { + return err + } + + return nil +} + +func DecodeProcesses(processes []Process, md toml.MetaData) error { + // decode the process.commands, which will differ based on APIs + // processes are defined differently depending on API version + // and will be decoded into different values + for i, process := range processes { + var commandString string + if err := md.PrimitiveDecode(process.RawCommandValue, &commandString); err == nil { + processes[i].Command = []string{commandString} + continue + } + + var command []string + if err := md.PrimitiveDecode(process.RawCommandValue, &command); err != nil { + return err + } + processes[i].Command = command + } + + return nil +} + +// Matches is used by goMock to compare two Metadata objects in tests +// when matching expected calls to methods containing Metadata objects +func (m Metadata) Matches(x interface{}) bool { + metadatax, ok := x.(Metadata) + if !ok { + return false + } + + // don't compare Processes directly, we will compare those individually next + if s := cmp.Diff(metadatax, m, cmpopts.IgnoreFields(Metadata{}, "Processes")); s != "" { + return false + } + + // we need to ignore the RawCommandValue field because it is a toml.Primitive and is not part of our equality + for i, p := range m.Processes { + if s := cmp.Diff(metadatax.Processes[i], p, cmpopts.IgnoreFields(Process{}, "RawCommandValue")); s != "" { + return false + } + } + + return true +} + +func (m Metadata) String() string { + return fmt.Sprintf("%+v %+v", m.Processes, m.Buildpacks) +} + func (m Metadata) FindProcessType(pType string) (Process, bool) { for _, p := range m.Processes { if p.Type == pType { diff --git a/launch/launcher.go b/launch/launcher.go index 548229e37..7bd420f69 100644 --- a/launch/launcher.go +++ b/launch/launcher.go @@ -80,7 +80,7 @@ func (l *Launcher) launchDirect(proc Process) error { if err := l.Setenv("PATH", l.Env.Get("PATH")); err != nil { return errors.Wrap(err, "set path") } - binary, err := exec.LookPath(proc.Command) + binary, err := exec.LookPath(proc.Command[0]) if err != nil { return errors.Wrap(err, "path lookup") } @@ -88,7 +88,7 @@ func (l *Launcher) launchDirect(proc Process) error { return errors.Wrap(err, "change directory") } if err := l.Exec(binary, - append([]string{proc.Command}, proc.Args...), + append(proc.Command, proc.Args...), l.Env.List(), ); err != nil { return errors.Wrap(err, "direct exec") diff --git a/launch/launcher_test.go b/launch/launcher_test.go index 25debdbc5..a2cae0c22 100644 --- a/launch/launcher_test.go +++ b/launch/launcher_test.go @@ -116,7 +116,7 @@ func testLauncher(t *testing.T, when spec.G, it spec.S) { it.Before(func() { process = launch.Process{ - Command: "command", + Command: []string{"command"}, Args: []string{"arg1", "arg2"}, } }) @@ -129,9 +129,9 @@ func testLauncher(t *testing.T, when spec.G, it spec.S) { // set command to something on the real path so exec.LookPath succeeds if runtime.GOOS == "windows" { - process.Command = "notepad" + process.Command = []string{"notepad"} } else { - process.Command = "sh" + process.Command = []string{"sh"} } mockEnv.EXPECT().Get("PATH").Return("some-path").AnyTimes() diff --git a/launch/process.go b/launch/process.go index 4f38bcb98..0215007bb 100644 --- a/launch/process.go +++ b/launch/process.go @@ -70,10 +70,10 @@ func (l *Launcher) userProvidedProcess(cmd []string) (Process, error) { return Process{}, errors.New("when there is no default process a command is required") } if len(cmd) > 1 && cmd[0] == "--" { - return Process{Command: cmd[1], Args: cmd[2:], Direct: true}, nil + return Process{Command: []string{cmd[1]}, Args: cmd[2:], Direct: true}, nil } - return Process{Command: cmd[0], Args: cmd[1:]}, nil + return Process{Command: []string{cmd[0]}, Args: cmd[1:]}, nil } func getProcessWorkingDirectory(process Process, appDir string) string { diff --git a/launch/process_test.go b/launch/process_test.go index 78526019c..0ccc166da 100644 --- a/launch/process_test.go +++ b/launch/process_test.go @@ -3,6 +3,8 @@ package launch_test import ( "testing" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/sclevine/spec" "github.com/sclevine/spec/report" @@ -15,6 +17,11 @@ func TestProcess(t *testing.T) { spec.Run(t, "Process", testProcess, spec.Report(report.Terminal{})) } +// RawCommandValue should be ignored because it is a toml.Primitive that has not been exported. +var processCmpOpts = []cmp.Option{ + cmpopts.IgnoreFields(launch.Process{}, "RawCommandValue"), +} + func testProcess(t *testing.T, when spec.G, it spec.S) { var ( launcher *launch.Launcher @@ -23,13 +30,13 @@ func testProcess(t *testing.T, when spec.G, it spec.S) { launcher = &launch.Launcher{Processes: []launch.Process{ { Type: "some-type", - Command: "some-command", + Command: []string{"some-command"}, Args: []string{"some-arg1", "some-arg2"}, BuildpackID: "some-buildpack", }, { Type: "other-type", - Command: "other-command", + Command: []string{"other-command"}, Args: []string{"other-arg1", "other-arg2"}, BuildpackID: "some-buildpack", }, @@ -49,10 +56,10 @@ func testProcess(t *testing.T, when spec.G, it spec.S) { proc, err := launcher.ProcessFor([]string{"--", "user-command", "user-arg1", "user-arg2"}) h.AssertNil(t, err) h.AssertEq(t, proc, launch.Process{ - Command: "user-command", + Command: []string{"user-command"}, Args: []string{"user-arg1", "user-arg2"}, Direct: true, - }) + }, processCmpOpts...) }) }) @@ -61,9 +68,9 @@ func testProcess(t *testing.T, when spec.G, it spec.S) { proc, err := launcher.ProcessFor([]string{"user-command", "user-arg1", "user-arg2"}) h.AssertNil(t, err) h.AssertEq(t, proc, launch.Process{ - Command: "user-command", + Command: []string{"user-command"}, Args: []string{"user-arg1", "user-arg2"}, - }) + }, processCmpOpts...) }) }) @@ -85,10 +92,10 @@ func testProcess(t *testing.T, when spec.G, it spec.S) { h.AssertNil(t, err) h.AssertEq(t, proc, launch.Process{ Type: "some-type", - Command: "some-command", + Command: []string{"some-command"}, Args: []string{"some-arg1", "some-arg2", "user-arg1", "user-arg1"}, BuildpackID: "some-buildpack", - }) + }, processCmpOpts...) }) }) @@ -125,10 +132,10 @@ func testProcess(t *testing.T, when spec.G, it spec.S) { h.AssertNil(t, err) h.AssertEq(t, proc, launch.Process{ Type: "other-type", - Command: "other-command", + Command: []string{"other-command"}, Args: []string{"other-arg1", "other-arg2"}, BuildpackID: "some-buildpack", - }) + }, processCmpOpts...) }) }) @@ -137,10 +144,10 @@ func testProcess(t *testing.T, when spec.G, it spec.S) { proc, err := launcher.ProcessFor([]string{"--", "user-command", "user-arg1", "user-arg2"}) h.AssertNil(t, err) h.AssertEq(t, proc, launch.Process{ - Command: "user-command", + Command: []string{"user-command"}, Args: []string{"user-arg1", "user-arg2"}, Direct: true, - }) + }, processCmpOpts...) }) }) @@ -149,9 +156,9 @@ func testProcess(t *testing.T, when spec.G, it spec.S) { proc, err := launcher.ProcessFor([]string{"user-command", "user-arg1", "user-arg2"}) h.AssertNil(t, err) h.AssertEq(t, proc, launch.Process{ - Command: "user-command", + Command: []string{"user-command"}, Args: []string{"user-arg1", "user-arg2"}, - }) + }, processCmpOpts...) }) }) }) @@ -167,10 +174,10 @@ func testProcess(t *testing.T, when spec.G, it spec.S) { h.AssertNil(t, err) h.AssertEq(t, proc, launch.Process{ Type: "some-type", - Command: "some-command", + Command: []string{"some-command"}, Args: []string{"some-arg1", "some-arg2"}, BuildpackID: "some-buildpack", - }) + }, processCmpOpts...) }) }) @@ -180,10 +187,10 @@ func testProcess(t *testing.T, when spec.G, it spec.S) { h.AssertNil(t, err) h.AssertEq(t, proc, launch.Process{ Type: "other-type", - Command: "other-command", + Command: []string{"other-command"}, Args: []string{"other-arg1", "other-arg2"}, BuildpackID: "some-buildpack", - }) + }, processCmpOpts...) }) }) @@ -192,10 +199,10 @@ func testProcess(t *testing.T, when spec.G, it spec.S) { proc, err := launcher.ProcessFor([]string{"--", "user-command", "user-arg1", "user-arg2"}) h.AssertNil(t, err) h.AssertEq(t, proc, launch.Process{ - Command: "user-command", + Command: []string{"user-command"}, Args: []string{"user-arg1", "user-arg2"}, Direct: true, - }) + }, processCmpOpts...) }) }) @@ -204,9 +211,9 @@ func testProcess(t *testing.T, when spec.G, it spec.S) { proc, err := launcher.ProcessFor([]string{"user-command", "user-arg1", "user-arg2"}) h.AssertNil(t, err) h.AssertEq(t, proc, launch.Process{ - Command: "user-command", + Command: []string{"user-command"}, Args: []string{"user-arg1", "user-arg2"}, - }) + }, processCmpOpts...) }) }) }) diff --git a/launch/shell.go b/launch/shell.go index 7f79a3479..c9676a20c 100644 --- a/launch/shell.go +++ b/launch/shell.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "runtime" + "strings" "github.com/pkg/errors" @@ -34,10 +35,14 @@ func (l *Launcher) launchWithShell(self string, proc Process) error { if err != nil { return err } + command := "" + if len(proc.Command) > 0 { + command = strings.Join(proc.Command, " ") + } return l.Shell.Launch(ShellProcess{ Script: script, Caller: self, - Command: proc.Command, + Command: command, // TODO: support multiple commands Args: proc.Args, Profiles: profs, Env: l.Env.List(), diff --git a/platform/files.go b/platform/files.go index 0cc28922d..f908a7beb 100644 --- a/platform/files.go +++ b/platform/files.go @@ -5,6 +5,7 @@ package platform import ( "encoding/json" + "github.com/BurntSushi/toml" "github.com/google/go-containerregistry/pkg/name" "github.com/pkg/errors" @@ -85,17 +86,70 @@ type BuildMetadata struct { PlatformAPI *api.Version `toml:"-" json:"-"` } +// DecodeBuildMetadataTOML reads a metadata.toml file +func DecodeBuildMetadataTOML(path string, platformAPI *api.Version, buildmd *BuildMetadata) error { + // decode the common bits + md, err := toml.DecodeFile(path, &buildmd) + if err != nil { + return err + } + + if err = launch.DecodeProcesses(buildmd.Processes, md); err != nil { + return err + } + + buildmd.PlatformAPI = platformAPI + + return nil +} + func (md *BuildMetadata) MarshalJSON() ([]byte, error) { + type BuildMetadataProcessSerializer struct { + Type string `toml:"type" json:"type"` + Command string `toml:"command" json:"command"` + Args []string `toml:"args" json:"args"` + Direct bool `toml:"direct" json:"direct"` + Default bool `toml:"default,omitempty" json:"default,omitempty"` + BuildpackID string `toml:"buildpack-id" json:"buildpackID"` + WorkingDirectory string `toml:"working-dir,omitempty" json:"working-dir,omitempty"` + } + var processes []BuildMetadataProcessSerializer + + if md.Processes != nil { + processes = []BuildMetadataProcessSerializer{} + + for _, process := range md.Processes { + processes = append(processes, BuildMetadataProcessSerializer{ + Type: process.Type, + Command: process.Command[0], + Args: append(process.Command[1:], process.Args[0:]...), + Direct: process.Direct, + Default: process.Default, + BuildpackID: process.BuildpackID, + WorkingDirectory: process.WorkingDirectory, + }) + } + } + if md.PlatformAPI == nil || md.PlatformAPI.LessThan("0.9") { - return json.Marshal(*md) + type BuildMetadataSerializer BuildMetadata // prevent infinite recursion when serializing + return json.Marshal(&struct { + *BuildMetadataSerializer + Processes []BuildMetadataProcessSerializer `toml:"processes" json:"processes"` + }{ + BuildMetadataSerializer: (*BuildMetadataSerializer)(md), + Processes: processes, + }) } type BuildMetadataSerializer BuildMetadata // prevent infinite recursion when serializing return json.Marshal(&struct { *BuildMetadataSerializer - BOM []buildpack.BOMEntry `json:"bom,omitempty"` + Processes []BuildMetadataProcessSerializer `toml:"processes" json:"processes"` + BOM []buildpack.BOMEntry `json:"bom,omitempty"` }{ BuildMetadataSerializer: (*BuildMetadataSerializer)(md), BOM: []buildpack.BOMEntry{}, + Processes: processes, }) } diff --git a/testhelpers/testhelpers.go b/testhelpers/testhelpers.go index 82a7171f7..a2d61d58f 100644 --- a/testhelpers/testhelpers.go +++ b/testhelpers/testhelpers.go @@ -55,9 +55,9 @@ func AssertSameInstance(t *testing.T, actual, expected interface{}) { } // Assert deep equality (and provide useful difference as a test failure) -func AssertEq(t *testing.T, actual, expected interface{}) { +func AssertEq(t *testing.T, actual, expected interface{}, opts ...cmp.Option) { t.Helper() - if diff := cmp.Diff(actual, expected); diff != "" { + if diff := cmp.Diff(actual, expected, opts...); diff != "" { t.Fatal(diff) } } From 8e1f3a6259333c882fbceda0fbd09764035d8210 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Thu, 29 Sep 2022 11:09:25 -0400 Subject: [PATCH 02/15] WIP Signed-off-by: Natalie Arellano --- acceptance/builder_test.go | 4 +- .../buildpacks/hello_world/0.0.1/bin/build | 8 ++ internal/encoding/utils.go | 9 +++ launch/launch.go | 62 +++++++++++++++ launch/launch_test.go | 76 +++++++++++++++++++ platform/files.go | 2 +- 6 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 launch/launch_test.go diff --git a/acceptance/builder_test.go b/acceptance/builder_test.go index bf11982c0..df270fd99 100644 --- a/acceptance/builder_test.go +++ b/acceptance/builder_test.go @@ -118,7 +118,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { }) when("correct and full group.toml and plan.toml", func() { - it("succeeds", func() { + it.Focus("succeeds", func() { h.DockerRunAndCopy(t, containerName, copyDir, @@ -136,6 +136,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { h.AssertStringContains(t, md.Buildpacks[0].API, "0.2") h.AssertStringContains(t, md.Buildpacks[0].ID, "hello_world") h.AssertStringContains(t, md.Buildpacks[0].Version, "0.0.1") + h.AssertStringContains(t, md.Processes[0].Type, "hello") }) }) @@ -445,6 +446,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { } func getBuilderMetadata(t *testing.T, path string) *platform.BuildMetadata { + t.Helper() contents, _ := ioutil.ReadFile(path) h.AssertEq(t, len(contents) > 0, true) diff --git a/acceptance/testdata/builder/container/cnb/buildpacks/hello_world/0.0.1/bin/build b/acceptance/testdata/builder/container/cnb/buildpacks/hello_world/0.0.1/bin/build index e544f7fa4..eeb3f256f 100755 --- a/acceptance/testdata/builder/container/cnb/buildpacks/hello_world/0.0.1/bin/build +++ b/acceptance/testdata/builder/container/cnb/buildpacks/hello_world/0.0.1/bin/build @@ -24,4 +24,12 @@ echo "plan contents:" cat ${plan_path} echo +# Set default start command +cat > "${layers_dir}/launch.toml" << EOL +[[processes]] +type = "hello" +command = "echo world" +default = true +EOL + echo "---> Done" diff --git a/internal/encoding/utils.go b/internal/encoding/utils.go index 446dd070f..b0f88f186 100644 --- a/internal/encoding/utils.go +++ b/internal/encoding/utils.go @@ -1,6 +1,7 @@ package encoding import ( + "bytes" "encoding/json" "os" "path/filepath" @@ -31,3 +32,11 @@ func WriteTOML(path string, data interface{}) error { defer f.Close() return toml.NewEncoder(f).Encode(data) } + +func MarshalTOML(v interface{}) ([]byte, error) { + buf := new(bytes.Buffer) + if err := toml.NewEncoder(buf).Encode(v); err != nil { + return nil, err + } + return buf.Bytes(), nil +} diff --git a/launch/launch.go b/launch/launch.go index ae43f89e5..5175de69a 100644 --- a/launch/launch.go +++ b/launch/launch.go @@ -1,7 +1,9 @@ package launch import ( + "errors" "fmt" + "github.com/buildpacks/lifecycle/internal/encoding" "path" "path/filepath" "strings" @@ -22,6 +24,66 @@ type Process struct { WorkingDirectory string `toml:"working-dir,omitempty" json:"working-dir,omitempty"` } +// TODO: add comment +type processSerializer struct { + Type string `toml:"type" json:"type"` + Command string `toml:"command" json:"command"` // command is string + Args []string `toml:"args" json:"args"` + Direct bool `toml:"direct" json:"direct"` + Default bool `toml:"default,omitempty" json:"default,omitempty"` + BuildpackID string `toml:"buildpack-id" json:"buildpackID"` + WorkingDirectory string `toml:"working-dir,omitempty" json:"working-dir,omitempty"` +} + +// TODO: create MarshalJSON + +func (p *Process) MarshalTOML() ([]byte, error) { + serializer := processSerializer{ + Type: p.Type, + Command: p.Command[0], + Args: append(p.Command[1:], p.Args[0:]...), + Direct: p.Direct, + Default: p.Default, + BuildpackID: p.BuildpackID, + WorkingDirectory: p.WorkingDirectory, + } + bytes, err := encoding.MarshalTOML(&struct { + *processSerializer + }{ + processSerializer: &serializer, + }) + return bytes, err +} + +func (p *Process) UnmarshalTOML(data interface{}) error { + bytes, ok := data.([]byte) + if !ok { + return errors.New("could not cast data to byte array") + } + + if _, err := toml.Decode(string(bytes), p); err != nil { + return err + } + fmt.Printf("%+v\n", p) + + //serializer := processSerializer{ + // Type: p.Type, + // Command: p.Command[0], + // Args: append(p.Command[1:], p.Args[0:]...), + // Direct: p.Direct, + // Default: p.Default, + // BuildpackID: p.BuildpackID, + // WorkingDirectory: p.WorkingDirectory, + //} + //bytes, err := encoding.MarshalTOML(&struct { + // *processSerializer + //}{ + // processSerializer: &serializer, + //}) + //return bytes, err + return nil +} + func (p Process) NoDefault() Process { p.Default = false return p diff --git a/launch/launch_test.go b/launch/launch_test.go new file mode 100644 index 000000000..ecb8cae6f --- /dev/null +++ b/launch/launch_test.go @@ -0,0 +1,76 @@ +package launch_test + +import ( + "testing" + + "github.com/sclevine/spec" + "github.com/sclevine/spec/report" + + "github.com/buildpacks/lifecycle/launch" + h "github.com/buildpacks/lifecycle/testhelpers" +) + +func TestLaunch(t *testing.T) { + spec.Run(t, "Launch", testLaunch, spec.Sequential(), spec.Report(report.Terminal{})) +} + +func testLaunch(t *testing.T, when spec.G, it spec.S) { + when("Process", func() { + when("MarshalTOML", func() { + it("command is string", func() { + process := launch.Process{ + Type: "some-type", + Command: []string{"some-command"}, + Args: []string{"some-arg"}, + Direct: true, + Default: true, + BuildpackID: "some-buildpack-id", + WorkingDirectory: "some-working-directory", + } + bytes, err := process.MarshalTOML() + h.AssertNil(t, err) + expected := `type = "some-type" +command = "some-command" +args = ["some-arg"] +direct = true +default = true +buildpack-id = "some-buildpack-id" +working-dir = "some-working-directory" +` + h.AssertEq(t, string(bytes), expected) + }) + }) + + when("UnmarshalTOML", func() { + when("provided command as string", func() { + it.Focus("populates a launch process", func() { + data := `type = "some-type" +command = "some-command" +args = ["some-arg"] +direct = true +default = true +buildpack-id = "some-buildpack-id" +working-dir = "some-working-directory" +` + process := &launch.Process{} + h.AssertNil(t, process.UnmarshalTOML([]byte(data))) + h.AssertEq(t, process, launch.Process{ + Type: "some-type", + Command: []string{"some-command"}, + Args: []string{"some-arg"}, + Direct: true, + Default: true, + BuildpackID: "some-buildpack-id", + WorkingDirectory: "some-working-directory", + }) + }) + }) + + when("provided command as array", func() { + it("populates a launch process", func() { + + }) + }) + }) + }) +} diff --git a/platform/files.go b/platform/files.go index f908a7beb..a28ee2298 100644 --- a/platform/files.go +++ b/platform/files.go @@ -4,7 +4,6 @@ package platform import ( "encoding/json" - "github.com/BurntSushi/toml" "github.com/google/go-containerregistry/pkg/name" "github.com/pkg/errors" @@ -141,6 +140,7 @@ func (md *BuildMetadata) MarshalJSON() ([]byte, error) { Processes: processes, }) } + type BuildMetadataSerializer BuildMetadata // prevent infinite recursion when serializing return json.Marshal(&struct { *BuildMetadataSerializer From 4149e8b57b32c9eb40e31bd9e38d640b5e7c89e6 Mon Sep 17 00:00:00 2001 From: Jesse Brown Date: Thu, 29 Sep 2022 11:25:43 -0500 Subject: [PATCH 03/15] Fixing up existing tests Signed-off-by: Jesse Brown --- acceptance/builder_test.go | 2 +- launch/launch.go | 67 ++++++++++++++++++++++++-------------- launch/launch_test.go | 41 ++++++++++++----------- 3 files changed, 64 insertions(+), 46 deletions(-) diff --git a/acceptance/builder_test.go b/acceptance/builder_test.go index df270fd99..7bd91a683 100644 --- a/acceptance/builder_test.go +++ b/acceptance/builder_test.go @@ -118,7 +118,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { }) when("correct and full group.toml and plan.toml", func() { - it.Focus("succeeds", func() { + it("succeeds", func() { h.DockerRunAndCopy(t, containerName, copyDir, diff --git a/launch/launch.go b/launch/launch.go index 5175de69a..7c3318acb 100644 --- a/launch/launch.go +++ b/launch/launch.go @@ -1,13 +1,14 @@ package launch import ( - "errors" "fmt" - "github.com/buildpacks/lifecycle/internal/encoding" "path" "path/filepath" "strings" + "github.com/buildpacks/lifecycle/internal/encoding" + "github.com/pkg/errors" + "github.com/BurntSushi/toml" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" @@ -35,9 +36,8 @@ type processSerializer struct { WorkingDirectory string `toml:"working-dir,omitempty" json:"working-dir,omitempty"` } -// TODO: create MarshalJSON - -func (p *Process) MarshalTOML() ([]byte, error) { +// TODO: create MarshalJSON? +func (p Process) MarshalText() ([]byte, error) { serializer := processSerializer{ Type: p.Type, Command: p.Command[0], @@ -56,31 +56,50 @@ func (p *Process) MarshalTOML() ([]byte, error) { } func (p *Process) UnmarshalTOML(data interface{}) error { - bytes, ok := data.([]byte) + tomlString, ok := data.(string) if !ok { - return errors.New("could not cast data to byte array") + return errors.New("could not cast data to string") } - if _, err := toml.Decode(string(bytes), p); err != nil { + // TODO: is there a better way to prevent recursion when unmarshalling? + // This is the same as launch.Process and exists to allow us to toml.Decode inside of UnmarshalTOML + type pProcess struct { + Type string `toml:"type" json:"type"` + Command []string `toml:"-" json:"-"` // ignored + RawCommandValue toml.Primitive `toml:"command" json:"command"` + Args []string `toml:"args" json:"args"` + Direct bool `toml:"direct" json:"direct"` + Default bool `toml:"default,omitempty" json:"default,omitempty"` + BuildpackID string `toml:"buildpack-id" json:"buildpackID"` + WorkingDirectory string `toml:"working-dir,omitempty" json:"working-dir,omitempty"` + } + + // unmarshal the common bits + newProcess := pProcess{} + md, err := toml.Decode(tomlString, &newProcess) + if err != nil { return err } - fmt.Printf("%+v\n", p) - //serializer := processSerializer{ - // Type: p.Type, - // Command: p.Command[0], - // Args: append(p.Command[1:], p.Args[0:]...), - // Direct: p.Direct, - // Default: p.Default, - // BuildpackID: p.BuildpackID, - // WorkingDirectory: p.WorkingDirectory, - //} - //bytes, err := encoding.MarshalTOML(&struct { - // *processSerializer - //}{ - // processSerializer: &serializer, - //}) - //return bytes, err + // handle the process.command, which will differ based on APIs + var commandWasString bool + var commandString string + if err := md.PrimitiveDecode(newProcess.RawCommandValue, &commandString); err == nil { + commandWasString = true + newProcess.Command = []string{commandString} + } + + if !commandWasString { + var command []string + if err := md.PrimitiveDecode(newProcess.RawCommandValue, &command); err != nil { + return err + } + newProcess.Command = command + } + + *p = Process(newProcess) + fmt.Printf("%+v\n", newProcess) + fmt.Printf("%+v\n", p) return nil } diff --git a/launch/launch_test.go b/launch/launch_test.go index ecb8cae6f..6ceca576f 100644 --- a/launch/launch_test.go +++ b/launch/launch_test.go @@ -3,6 +3,7 @@ package launch_test import ( "testing" + "github.com/google/go-cmp/cmp" "github.com/sclevine/spec" "github.com/sclevine/spec/report" @@ -27,22 +28,16 @@ func testLaunch(t *testing.T, when spec.G, it spec.S) { BuildpackID: "some-buildpack-id", WorkingDirectory: "some-working-directory", } - bytes, err := process.MarshalTOML() + + bytes, err := process.MarshalText() h.AssertNil(t, err) - expected := `type = "some-type" -command = "some-command" -args = ["some-arg"] -direct = true -default = true -buildpack-id = "some-buildpack-id" -working-dir = "some-working-directory" -` + expected := `type = "some-type"\ncommand = "some-command"\nargs = ["some-arg"]\ndirect = true\ndefault = true\nbuildpack-id = "some-buildpack-id"\nworking-dir = "some-working-directory"\n` h.AssertEq(t, string(bytes), expected) }) }) when("UnmarshalTOML", func() { - when("provided command as string", func() { + when.Focus("provided command as string", func() { it.Focus("populates a launch process", func() { data := `type = "some-type" command = "some-command" @@ -52,17 +47,21 @@ default = true buildpack-id = "some-buildpack-id" working-dir = "some-working-directory" ` - process := &launch.Process{} - h.AssertNil(t, process.UnmarshalTOML([]byte(data))) - h.AssertEq(t, process, launch.Process{ - Type: "some-type", - Command: []string{"some-command"}, - Args: []string{"some-arg"}, - Direct: true, - Default: true, - BuildpackID: "some-buildpack-id", - WorkingDirectory: "some-working-directory", - }) + process := launch.Process{} + h.AssertNil(t, process.UnmarshalTOML(data)) + if s := cmp.Diff([]launch.Process{process}, []launch.Process{ + { + Type: "some-type", + Command: []string{"some-command"}, + Args: []string{"some-arg"}, + Direct: true, + Default: true, + BuildpackID: "some-buildpack-id", + WorkingDirectory: "some-working-directory", + }, + }, processCmpOpts...); s != "" { + t.Fatalf("Unexpected:\n%s\n", s) + } }) }) From 9b1afa80cf332d3a89be8acc99ed4c2db73b7bcd Mon Sep 17 00:00:00 2001 From: Jesse Brown Date: Thu, 29 Sep 2022 12:00:08 -0500 Subject: [PATCH 04/15] Remove now unused process specific decode paths Signed-off-by: Jesse Brown --- launch/launch.go | 41 ++++++++++++----------------------------- platform/files.go | 7 ++----- 2 files changed, 14 insertions(+), 34 deletions(-) diff --git a/launch/launch.go b/launch/launch.go index 7c3318acb..6a21bc329 100644 --- a/launch/launch.go +++ b/launch/launch.go @@ -6,9 +6,10 @@ import ( "path/filepath" "strings" - "github.com/buildpacks/lifecycle/internal/encoding" "github.com/pkg/errors" + "github.com/buildpacks/lifecycle/internal/encoding" + "github.com/BurntSushi/toml" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" @@ -56,8 +57,15 @@ func (p Process) MarshalText() ([]byte, error) { } func (p *Process) UnmarshalTOML(data interface{}) error { - tomlString, ok := data.(string) - if !ok { + var tomlString string + switch v := data.(type) { + case string: + tomlString = v + case map[string]interface{}: + // turn back into a string + bytes, _ := encoding.MarshalTOML(v) + tomlString = string(bytes) + default: return errors.New("could not cast data to string") } @@ -121,36 +129,11 @@ type Metadata struct { // DecodeLaunchMetadataTOML reads a launch.toml file func DecodeLaunchMetadataTOML(path string, launchmd *Metadata) error { // decode the common bits - md, err := toml.DecodeFile(path, &launchmd) + _, err := toml.DecodeFile(path, &launchmd) if err != nil { return err } - if err = DecodeProcesses(launchmd.Processes, md); err != nil { - return err - } - - return nil -} - -func DecodeProcesses(processes []Process, md toml.MetaData) error { - // decode the process.commands, which will differ based on APIs - // processes are defined differently depending on API version - // and will be decoded into different values - for i, process := range processes { - var commandString string - if err := md.PrimitiveDecode(process.RawCommandValue, &commandString); err == nil { - processes[i].Command = []string{commandString} - continue - } - - var command []string - if err := md.PrimitiveDecode(process.RawCommandValue, &command); err != nil { - return err - } - processes[i].Command = command - } - return nil } diff --git a/platform/files.go b/platform/files.go index a28ee2298..a8ae4d07c 100644 --- a/platform/files.go +++ b/platform/files.go @@ -4,6 +4,7 @@ package platform import ( "encoding/json" + "github.com/BurntSushi/toml" "github.com/google/go-containerregistry/pkg/name" "github.com/pkg/errors" @@ -88,15 +89,11 @@ type BuildMetadata struct { // DecodeBuildMetadataTOML reads a metadata.toml file func DecodeBuildMetadataTOML(path string, platformAPI *api.Version, buildmd *BuildMetadata) error { // decode the common bits - md, err := toml.DecodeFile(path, &buildmd) + _, err := toml.DecodeFile(path, &buildmd) if err != nil { return err } - if err = launch.DecodeProcesses(buildmd.Processes, md); err != nil { - return err - } - buildmd.PlatformAPI = platformAPI return nil From 6f59c7308ab06481c8ee98dec289b838c84676ff Mon Sep 17 00:00:00 2001 From: Jesse Brown Date: Thu, 29 Sep 2022 12:04:11 -0500 Subject: [PATCH 05/15] fixup! Remove now unused process specific decode paths Signed-off-by: Jesse Brown --- launch/launch.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/launch/launch.go b/launch/launch.go index 6a21bc329..1b0d0507f 100644 --- a/launch/launch.go +++ b/launch/launch.go @@ -26,7 +26,7 @@ type Process struct { WorkingDirectory string `toml:"working-dir,omitempty" json:"working-dir,omitempty"` } -// TODO: add comment +// processSerializer is used to encode a process to toml. type processSerializer struct { Type string `toml:"type" json:"type"` Command string `toml:"command" json:"command"` // command is string @@ -37,7 +37,7 @@ type processSerializer struct { WorkingDirectory string `toml:"working-dir,omitempty" json:"working-dir,omitempty"` } -// TODO: create MarshalJSON? +// MarshalText implements the toml TextMarshaler interface to allow us more control when writing a Process to a toml file. func (p Process) MarshalText() ([]byte, error) { serializer := processSerializer{ Type: p.Type, @@ -56,6 +56,7 @@ func (p Process) MarshalText() ([]byte, error) { return bytes, err } +// UnmarshalTOML implements the toml Unmarshaler interface to allow us more control when reading a Process from toml. func (p *Process) UnmarshalTOML(data interface{}) error { var tomlString string switch v := data.(type) { @@ -69,7 +70,6 @@ func (p *Process) UnmarshalTOML(data interface{}) error { return errors.New("could not cast data to string") } - // TODO: is there a better way to prevent recursion when unmarshalling? // This is the same as launch.Process and exists to allow us to toml.Decode inside of UnmarshalTOML type pProcess struct { Type string `toml:"type" json:"type"` @@ -106,8 +106,6 @@ func (p *Process) UnmarshalTOML(data interface{}) error { } *p = Process(newProcess) - fmt.Printf("%+v\n", newProcess) - fmt.Printf("%+v\n", p) return nil } From 3617b6be299caf990804a89e03efbf760d3466da Mon Sep 17 00:00:00 2001 From: Jesse Brown Date: Thu, 29 Sep 2022 12:06:08 -0500 Subject: [PATCH 06/15] fixup! Remove now unused process specific decode paths Signed-off-by: Jesse Brown --- launch/launch.go | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/launch/launch.go b/launch/launch.go index 1b0d0507f..c488e9bf8 100644 --- a/launch/launch.go +++ b/launch/launch.go @@ -71,16 +71,7 @@ func (p *Process) UnmarshalTOML(data interface{}) error { } // This is the same as launch.Process and exists to allow us to toml.Decode inside of UnmarshalTOML - type pProcess struct { - Type string `toml:"type" json:"type"` - Command []string `toml:"-" json:"-"` // ignored - RawCommandValue toml.Primitive `toml:"command" json:"command"` - Args []string `toml:"args" json:"args"` - Direct bool `toml:"direct" json:"direct"` - Default bool `toml:"default,omitempty" json:"default,omitempty"` - BuildpackID string `toml:"buildpack-id" json:"buildpackID"` - WorkingDirectory string `toml:"working-dir,omitempty" json:"working-dir,omitempty"` - } + type pProcess Process // unmarshal the common bits newProcess := pProcess{} From 19c6bba506d7b85221cfb907e120ddf1da7f4bc1 Mon Sep 17 00:00:00 2001 From: Jesse Brown Date: Thu, 29 Sep 2022 12:13:06 -0500 Subject: [PATCH 07/15] fixup! Remove now unused process specific decode paths Signed-off-by: Jesse Brown --- launch/launch_test.go | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/launch/launch_test.go b/launch/launch_test.go index 6ceca576f..b0e33a7eb 100644 --- a/launch/launch_test.go +++ b/launch/launch_test.go @@ -17,7 +17,7 @@ func TestLaunch(t *testing.T) { func testLaunch(t *testing.T, when spec.G, it spec.S) { when("Process", func() { - when("MarshalTOML", func() { + when("MarshalText", func() { it("command is string", func() { process := launch.Process{ Type: "some-type", @@ -31,14 +31,21 @@ func testLaunch(t *testing.T, when spec.G, it spec.S) { bytes, err := process.MarshalText() h.AssertNil(t, err) - expected := `type = "some-type"\ncommand = "some-command"\nargs = ["some-arg"]\ndirect = true\ndefault = true\nbuildpack-id = "some-buildpack-id"\nworking-dir = "some-working-directory"\n` + expected := `type = "some-type" +command = "some-command" +args = ["some-arg"] +direct = true +default = true +buildpack-id = "some-buildpack-id" +working-dir = "some-working-directory" +` h.AssertEq(t, string(bytes), expected) }) }) when("UnmarshalTOML", func() { - when.Focus("provided command as string", func() { - it.Focus("populates a launch process", func() { + when("provided command as string", func() { + it("populates a launch process", func() { data := `type = "some-type" command = "some-command" args = ["some-arg"] @@ -67,7 +74,28 @@ working-dir = "some-working-directory" when("provided command as array", func() { it("populates a launch process", func() { - + data := `type = "some-type" +command = ["some-command", "some-command-arg"] +args = ["some-arg"] +direct = true +default = true +buildpack-id = "some-buildpack-id" +working-dir = "some-working-directory" +` + process := launch.Process{} + h.AssertNil(t, process.UnmarshalTOML(data)) + if s := cmp.Diff([]launch.Process{process}, []launch.Process{ + { + Type: "some-type", + Command: []string{"some-command", "some-command-arg"}, + Args: []string{"some-arg"}, + Direct: true, + Default: true, + BuildpackID: "some-buildpack-id", + WorkingDirectory: "some-working-directory", + }, + }, processCmpOpts...); s != "" { + } }) }) }) From a4ee722efe3a453ee212d4c79374de33b501c51b Mon Sep 17 00:00:00 2001 From: Jesse Brown Date: Thu, 29 Sep 2022 12:38:08 -0500 Subject: [PATCH 08/15] fixup! Remove now unused process specific decode paths Signed-off-by: Jesse Brown --- acceptance/builder_test.go | 9 ++++++++- .../container/cnb/buildpacks/hello_world/0.0.1/bin/build | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/acceptance/builder_test.go b/acceptance/builder_test.go index 7bd91a683..38e569aba 100644 --- a/acceptance/builder_test.go +++ b/acceptance/builder_test.go @@ -136,7 +136,14 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) { h.AssertStringContains(t, md.Buildpacks[0].API, "0.2") h.AssertStringContains(t, md.Buildpacks[0].ID, "hello_world") h.AssertStringContains(t, md.Buildpacks[0].Version, "0.0.1") - h.AssertStringContains(t, md.Processes[0].Type, "hello") + h.AssertEq(t, 1, len(md.Processes)) + h.AssertEq(t, "hello", md.Processes[0].Type) + h.AssertEq(t, "echo world", md.Processes[0].Command[0]) + h.AssertEq(t, 1, len(md.Processes[0].Args)) + h.AssertEq(t, "arg1", md.Processes[0].Args[0]) + h.AssertEq(t, false, md.Processes[0].Direct) + h.AssertEq(t, "", md.Processes[0].WorkingDirectory) + h.AssertEq(t, false, md.Processes[0].Default) }) }) diff --git a/acceptance/testdata/builder/container/cnb/buildpacks/hello_world/0.0.1/bin/build b/acceptance/testdata/builder/container/cnb/buildpacks/hello_world/0.0.1/bin/build index eeb3f256f..aba0644cc 100755 --- a/acceptance/testdata/builder/container/cnb/buildpacks/hello_world/0.0.1/bin/build +++ b/acceptance/testdata/builder/container/cnb/buildpacks/hello_world/0.0.1/bin/build @@ -29,7 +29,8 @@ cat > "${layers_dir}/launch.toml" << EOL [[processes]] type = "hello" command = "echo world" -default = true +args = ["arg1"] +direct = false EOL echo "---> Done" From 02e02bafe9fcdd7f8a921cb0fd24e0e558ba47d9 Mon Sep 17 00:00:00 2001 From: Jesse Brown Date: Thu, 29 Sep 2022 14:08:20 -0500 Subject: [PATCH 09/15] fixup! Remove now unused process specific decode paths Signed-off-by: Jesse Brown --- launch/launch_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/launch/launch_test.go b/launch/launch_test.go index b0e33a7eb..7643897b9 100644 --- a/launch/launch_test.go +++ b/launch/launch_test.go @@ -95,6 +95,7 @@ working-dir = "some-working-directory" WorkingDirectory: "some-working-directory", }, }, processCmpOpts...); s != "" { + t.Fatalf("Unexpected:\n%s\n", s) } }) }) From 51c44f50e2b57e794ea8fbbf9609d56033fee5db Mon Sep 17 00:00:00 2001 From: Jesse Brown Date: Fri, 30 Sep 2022 08:57:09 -0500 Subject: [PATCH 10/15] Clean up more serialization paths Signed-off-by: Jesse Brown --- launch/launch.go | 22 +++++++++++++++------- platform/files.go | 33 +-------------------------------- 2 files changed, 16 insertions(+), 39 deletions(-) diff --git a/launch/launch.go b/launch/launch.go index c488e9bf8..e663e5b1c 100644 --- a/launch/launch.go +++ b/launch/launch.go @@ -1,6 +1,7 @@ package launch import ( + "encoding/json" "fmt" "path" "path/filepath" @@ -39,7 +40,20 @@ type processSerializer struct { // MarshalText implements the toml TextMarshaler interface to allow us more control when writing a Process to a toml file. func (p Process) MarshalText() ([]byte, error) { - serializer := processSerializer{ + return encoding.MarshalTOML(processSerializer{ + Type: p.Type, + Command: p.Command[0], + Args: append(p.Command[1:], p.Args[0:]...), + Direct: p.Direct, + Default: p.Default, + BuildpackID: p.BuildpackID, + WorkingDirectory: p.WorkingDirectory, + }) +} + +// MarhsalJSON implements the json Marshaler interface to allow us more control when writing a Process to a json file. +func (p Process) MarshalJSON() ([]byte, error) { + return json.Marshal(processSerializer{ Type: p.Type, Command: p.Command[0], Args: append(p.Command[1:], p.Args[0:]...), @@ -47,13 +61,7 @@ func (p Process) MarshalText() ([]byte, error) { Default: p.Default, BuildpackID: p.BuildpackID, WorkingDirectory: p.WorkingDirectory, - } - bytes, err := encoding.MarshalTOML(&struct { - *processSerializer - }{ - processSerializer: &serializer, }) - return bytes, err } // UnmarshalTOML implements the toml Unmarshaler interface to allow us more control when reading a Process from toml. diff --git a/platform/files.go b/platform/files.go index a8ae4d07c..9142253e4 100644 --- a/platform/files.go +++ b/platform/files.go @@ -100,53 +100,22 @@ func DecodeBuildMetadataTOML(path string, platformAPI *api.Version, buildmd *Bui } func (md *BuildMetadata) MarshalJSON() ([]byte, error) { - type BuildMetadataProcessSerializer struct { - Type string `toml:"type" json:"type"` - Command string `toml:"command" json:"command"` - Args []string `toml:"args" json:"args"` - Direct bool `toml:"direct" json:"direct"` - Default bool `toml:"default,omitempty" json:"default,omitempty"` - BuildpackID string `toml:"buildpack-id" json:"buildpackID"` - WorkingDirectory string `toml:"working-dir,omitempty" json:"working-dir,omitempty"` - } - var processes []BuildMetadataProcessSerializer - - if md.Processes != nil { - processes = []BuildMetadataProcessSerializer{} - - for _, process := range md.Processes { - processes = append(processes, BuildMetadataProcessSerializer{ - Type: process.Type, - Command: process.Command[0], - Args: append(process.Command[1:], process.Args[0:]...), - Direct: process.Direct, - Default: process.Default, - BuildpackID: process.BuildpackID, - WorkingDirectory: process.WorkingDirectory, - }) - } - } - if md.PlatformAPI == nil || md.PlatformAPI.LessThan("0.9") { type BuildMetadataSerializer BuildMetadata // prevent infinite recursion when serializing return json.Marshal(&struct { *BuildMetadataSerializer - Processes []BuildMetadataProcessSerializer `toml:"processes" json:"processes"` }{ BuildMetadataSerializer: (*BuildMetadataSerializer)(md), - Processes: processes, }) } type BuildMetadataSerializer BuildMetadata // prevent infinite recursion when serializing return json.Marshal(&struct { *BuildMetadataSerializer - Processes []BuildMetadataProcessSerializer `toml:"processes" json:"processes"` - BOM []buildpack.BOMEntry `json:"bom,omitempty"` + BOM []buildpack.BOMEntry `json:"bom,omitempty"` }{ BuildMetadataSerializer: (*BuildMetadataSerializer)(md), BOM: []buildpack.BOMEntry{}, - Processes: processes, }) } From 6a2c81622023dff219e49e2a0c7542c78fe4b47c Mon Sep 17 00:00:00 2001 From: Jesse Brown Date: Fri, 30 Sep 2022 09:05:34 -0500 Subject: [PATCH 11/15] Remove toml wrapping code Signed-off-by: Jesse Brown --- cmd/launcher/cli/launcher.go | 3 ++- launch/decode_test.go | 9 ++++++--- launch/launch.go | 11 ----------- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/cmd/launcher/cli/launcher.go b/cmd/launcher/cli/launcher.go index a348b0ceb..706734fa0 100644 --- a/cmd/launcher/cli/launcher.go +++ b/cmd/launcher/cli/launcher.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" + "github.com/BurntSushi/toml" "github.com/heroku/color" "github.com/buildpacks/lifecycle/api" @@ -26,7 +27,7 @@ func RunLaunch() error { p := platform.NewPlatform(platformAPI) var md launch.Metadata - if err := launch.DecodeLaunchMetadataTOML(launch.GetMetadataFilePath(cmd.EnvOrDefault(platform.EnvLayersDir, platform.DefaultLayersDir)), &md); err != nil { + if _, err := toml.DecodeFile(launch.GetMetadataFilePath(cmd.EnvOrDefault(platform.EnvLayersDir, platform.DefaultLayersDir)), &md); err != nil { return cmd.FailErr(err, "read metadata") } if err := verifyBuildpackAPIs(md.Buildpacks); err != nil { diff --git a/launch/decode_test.go b/launch/decode_test.go index 9c081efb5..d163af2db 100644 --- a/launch/decode_test.go +++ b/launch/decode_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/BurntSushi/toml" "github.com/sclevine/spec" "github.com/sclevine/spec/report" @@ -21,7 +22,7 @@ func TestDecodeMetadataTOML(t *testing.T) { } func testDecodeMetataTOML(t *testing.T, when spec.G, it spec.S) { - when("DecodeLaunchMetadataTOML", func() { + when("decoding metadata.toml", func() { var ( tmpDir string ) @@ -52,7 +53,8 @@ func testDecodeMetataTOML(t *testing.T, when spec.G, it spec.S) { metadata := launch.Metadata{} - h.AssertNil(t, launch.DecodeLaunchMetadataTOML(path, &metadata)) + _, err := toml.DecodeFile(path, &metadata) + h.AssertNil(t, err) h.AssertEq(t, metadata.Processes[0].Command[0], "some-cmd") h.AssertEq(t, metadata.Processes[0].Command[1], "more") @@ -77,7 +79,8 @@ func testDecodeMetataTOML(t *testing.T, when spec.G, it spec.S) { metadata := launch.Metadata{} - h.AssertNil(t, launch.DecodeLaunchMetadataTOML(path, &metadata)) + _, err := toml.DecodeFile(path, &metadata) + h.AssertNil(t, err) h.AssertEq(t, metadata.Processes[0].Command[0], "some-cmd") h.AssertEq(t, metadata.Processes[1].Command[0], "other cmd with spaces") }) diff --git a/launch/launch.go b/launch/launch.go index e663e5b1c..406152286 100644 --- a/launch/launch.go +++ b/launch/launch.go @@ -123,17 +123,6 @@ type Metadata struct { Buildpacks []Buildpack `toml:"buildpacks" json:"buildpacks"` } -// DecodeLaunchMetadataTOML reads a launch.toml file -func DecodeLaunchMetadataTOML(path string, launchmd *Metadata) error { - // decode the common bits - _, err := toml.DecodeFile(path, &launchmd) - if err != nil { - return err - } - - return nil -} - // Matches is used by goMock to compare two Metadata objects in tests // when matching expected calls to methods containing Metadata objects func (m Metadata) Matches(x interface{}) bool { From 26dfd336d9becccdd2d844936b5cc945620cc7ad Mon Sep 17 00:00:00 2001 From: Jesse Brown Date: Fri, 30 Sep 2022 10:32:37 -0500 Subject: [PATCH 12/15] Added comment on UnmarshalTOML Signed-off-by: Jesse Brown --- launch/launch.go | 1 + 1 file changed, 1 insertion(+) diff --git a/launch/launch.go b/launch/launch.go index 406152286..e6acdc86f 100644 --- a/launch/launch.go +++ b/launch/launch.go @@ -71,6 +71,7 @@ func (p *Process) UnmarshalTOML(data interface{}) error { case string: tomlString = v case map[string]interface{}: + // when unmarshaling as part of a parent struct, the process is a map[string]interface{} // turn back into a string bytes, _ := encoding.MarshalTOML(v) tomlString = string(bytes) From 3151373fb4d81ce41cdb8f87dedbaa2d543b8967 Mon Sep 17 00:00:00 2001 From: Jesse Brown Date: Fri, 30 Sep 2022 11:02:30 -0500 Subject: [PATCH 13/15] Put back code I didn't mean to remove Signed-off-by: Jesse Brown --- platform/files.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/platform/files.go b/platform/files.go index 9142253e4..b343bf049 100644 --- a/platform/files.go +++ b/platform/files.go @@ -101,14 +101,8 @@ func DecodeBuildMetadataTOML(path string, platformAPI *api.Version, buildmd *Bui func (md *BuildMetadata) MarshalJSON() ([]byte, error) { if md.PlatformAPI == nil || md.PlatformAPI.LessThan("0.9") { - type BuildMetadataSerializer BuildMetadata // prevent infinite recursion when serializing - return json.Marshal(&struct { - *BuildMetadataSerializer - }{ - BuildMetadataSerializer: (*BuildMetadataSerializer)(md), - }) + return json.Marshal(*md) } - type BuildMetadataSerializer BuildMetadata // prevent infinite recursion when serializing return json.Marshal(&struct { *BuildMetadataSerializer From 9b9cff398d802f0dd1f8c905a59fb1e430d0307c Mon Sep 17 00:00:00 2001 From: Jesse Brown Date: Fri, 30 Sep 2022 11:04:10 -0500 Subject: [PATCH 14/15] Update launch/launch.go Co-authored-by: Natalie Arellano Signed-off-by: Jesse Brown --- launch/launch.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/launch/launch.go b/launch/launch.go index e6acdc86f..fa3218fab 100644 --- a/launch/launch.go +++ b/launch/launch.go @@ -7,13 +7,12 @@ import ( "path/filepath" "strings" - "github.com/pkg/errors" - - "github.com/buildpacks/lifecycle/internal/encoding" - "github.com/BurntSushi/toml" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/pkg/errors" + + "github.com/buildpacks/lifecycle/internal/encoding" ) type Process struct { From 462b86e07254ee2a0298bf3ed8cb9e183e670d6b Mon Sep 17 00:00:00 2001 From: Jesse Brown Date: Fri, 30 Sep 2022 11:56:13 -0500 Subject: [PATCH 15/15] removed line Signed-off-by: Jesse Brown --- launch/launch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launch/launch.go b/launch/launch.go index fa3218fab..50b8bdc9b 100644 --- a/launch/launch.go +++ b/launch/launch.go @@ -11,7 +11,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/pkg/errors" - + "github.com/buildpacks/lifecycle/internal/encoding" )