Skip to content

Commit

Permalink
Add support for buildpack API v0.9 (#486)
Browse files Browse the repository at this point in the history
* Add support for buildpack API v0.9

* Move process-specific printing logic to emitter.

---------

Co-authored-by: Rob Dimsdale-Zucker <robert.dimsdale@gmail.com>
  • Loading branch information
modulo11 and robdimsdale authored May 11, 2023
1 parent a65afbb commit 4a226ae
Show file tree
Hide file tree
Showing 6 changed files with 332 additions and 23 deletions.
25 changes: 20 additions & 5 deletions build.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ func Build(f BuildFunc, options ...Option) {
apiV05, _ := semver.NewVersion("0.5")
apiV06, _ := semver.NewVersion("0.6")
apiV08, _ := semver.NewVersion("0.8")
apiV09, _ := semver.NewVersion("0.9")
apiVersion, err := semver.NewVersion(buildpackInfo.APIVersion)
if err != nil {
config.exitHandler.Error(err)
Expand Down Expand Up @@ -284,13 +285,27 @@ func Build(f BuildFunc, options ...Option) {
}

var launch struct {
Processes []Process `toml:"processes"`
Slices []Slice `toml:"slices"`
Labels []label `toml:"labels"`
BOM []BOMEntry `toml:"bom"`
Processes []Process `toml:"processes,omitempty"`
DirectProcesses []DirectProcess `toml:"processes,omitempty"`
Slices []Slice `toml:"slices"`
Labels []label `toml:"labels"`
BOM []BOMEntry `toml:"bom"`
}

if apiVersion.LessThan(apiV09) {
if result.Launch.DirectProcesses != nil {
config.exitHandler.Error(errors.New("direct processes can only be used with Buildpack API v0.9 or higher"))
return
}
launch.Processes = result.Launch.Processes
} else {
if result.Launch.Processes != nil {
config.exitHandler.Error(errors.New("non direct processes can only be used with Buildpack API v0.8 or lower"))
return
}
launch.DirectProcesses = result.Launch.DirectProcesses
}

launch.Processes = result.Launch.Processes
if apiVersion.LessThan(apiV06) {
for _, process := range launch.Processes {
if process.Default {
Expand Down
146 changes: 140 additions & 6 deletions build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ api = "0.4"

return packit.BuildResult{
Layers: []packit.Layer{
packit.Layer{
{
Path: layerPath,
Name: "some-layer",
Build: true,
Expand Down Expand Up @@ -256,7 +256,7 @@ api = "0.5"

return packit.BuildResult{
Layers: []packit.Layer{
packit.Layer{
{
Path: layerPath,
Name: "some-layer",
Build: true,
Expand Down Expand Up @@ -292,7 +292,7 @@ cache = true

return packit.BuildResult{
Layers: []packit.Layer{
packit.Layer{
{
Path: layerPath,
Name: "some-layer",
SBOM: packit.SBOMFormats{
Expand Down Expand Up @@ -339,7 +339,7 @@ api = "0.6"

return packit.BuildResult{
Layers: []packit.Layer{
packit.Layer{
{
Path: layerPath,
Name: "some-layer",
SBOM: packit.SBOMFormats{
Expand Down Expand Up @@ -1136,6 +1136,140 @@ api = "0.7"
Expect(exitHandler.ErrorCall.Receives.Error).To(MatchError(ContainSubstring("processes can only have a specific working directory with Buildpack API v0.8 or higher")))
})
})

context("when the api version is less than 0.9", func() {
it.Before(func() {
Expect(os.WriteFile(filepath.Join(cnbDir, "buildpack.toml"), []byte(`
api = "0.8"
[buildpack]
id = "some-id"
name = "some-name"
version = "some-version"
clear-env = false
`), 0600)).To(Succeed())
})

it("persists a launch.toml", func() {
packit.Build(func(ctx packit.BuildContext) (packit.BuildResult, error) {
return packit.BuildResult{
Launch: packit.LaunchMetadata{
Processes: []packit.Process{
{
Type: "some-type",
Command: "some-command",
Args: []string{"some-arg"},
Direct: false,
Default: true,
WorkingDirectory: "some-working-dir",
},
},
},
}, nil
}, packit.WithArgs([]string{binaryPath, layersDir, platformDir, planPath}), packit.WithExitHandler(exitHandler))

contents, err := os.ReadFile(filepath.Join(layersDir, "launch.toml"))
Expect(err).NotTo(HaveOccurred())

Expect(string(contents)).To(MatchTOML(`
[[processes]]
args = ["some-arg"]
command = "some-command"
direct = false
default = true
type = "some-type"
working-directory = "some-working-dir"
`))
})

context("failure cases", func() {
it("throws a specific error when new style proccesses are used", func() {

packit.Build(func(ctx packit.BuildContext) (packit.BuildResult, error) {
return packit.BuildResult{
Launch: packit.LaunchMetadata{
DirectProcesses: []packit.DirectProcess{
{
Type: "some-type",
Command: []string{"some-command"},
Args: []string{"some-arg"},
Default: false,
WorkingDirectory: workingDir,
},
},
},
}, nil
}, packit.WithArgs([]string{binaryPath, layersDir, platformDir, planPath}), packit.WithExitHandler(exitHandler))

Expect(exitHandler.ErrorCall.Receives.Error).To(MatchError("direct processes can only be used with Buildpack API v0.9 or higher"))
})
})
})

context("when the api version is 0.9", func() {
it.Before(func() {
Expect(os.WriteFile(filepath.Join(cnbDir, "buildpack.toml"), []byte(`
api = "0.9"
[buildpack]
id = "some-id"
name = "some-name"
version = "some-version"
clear-env = false
`), 0600)).To(Succeed())
})

it("persists a launch.toml", func() {
packit.Build(func(ctx packit.BuildContext) (packit.BuildResult, error) {
return packit.BuildResult{
Launch: packit.LaunchMetadata{
DirectProcesses: []packit.DirectProcess{
{
Type: "some-type",
Command: []string{"some-command"},
Args: []string{"some-arg"},
Default: true,
WorkingDirectory: "some-working-dir",
},
},
},
}, nil
}, packit.WithArgs([]string{binaryPath, layersDir, platformDir, planPath}), packit.WithExitHandler(exitHandler))

contents, err := os.ReadFile(filepath.Join(layersDir, "launch.toml"))
Expect(err).NotTo(HaveOccurred())

Expect(string(contents)).To(MatchTOML(`
[[processes]]
args = ["some-arg"]
command = ["some-command"]
default = true
type = "some-type"
working-directory = "some-working-dir"
`))
})
context("failure cases", func() {
it("throws a specific error when old style proccesses are used", func() {

packit.Build(func(ctx packit.BuildContext) (packit.BuildResult, error) {
return packit.BuildResult{
Launch: packit.LaunchMetadata{
Processes: []packit.Process{
{
Type: "some-type",
Command: "some-command",
Args: []string{"some-arg"},
Direct: false,
Default: false,
WorkingDirectory: workingDir,
},
},
},
}, nil
}, packit.WithArgs([]string{binaryPath, layersDir, platformDir, planPath}), packit.WithExitHandler(exitHandler))

Expect(exitHandler.ErrorCall.Receives.Error).To(MatchError("non direct processes can only be used with Buildpack API v0.8 or lower"))
})
})
})
})

context("when there are slices in the result", func() {
Expand Down Expand Up @@ -1527,7 +1661,7 @@ api = "0.4"
packit.Build(func(ctx packit.BuildContext) (packit.BuildResult, error) {
return packit.BuildResult{
Layers: []packit.Layer{
packit.Layer{
{
Path: filepath.Join(layersDir, "some-layer"),
Name: "some-layer",
},
Expand All @@ -1544,7 +1678,7 @@ api = "0.4"
packit.Build(func(ctx packit.BuildContext) (packit.BuildResult, error) {
return packit.BuildResult{
Layers: []packit.Layer{
packit.Layer{
{
Path: filepath.Join(layersDir, "some-layer"),
Name: "some-layer",
SBOM: packit.SBOMFormats{
Expand Down
6 changes: 5 additions & 1 deletion launch_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ type LaunchMetadata struct {
// be executed during the launch phase.
Processes []Process

// DirectProcesses is a list of processes that will be returned to the lifecycle to
// be executed directly during the launch phase.
DirectProcesses []DirectProcess

// Slices is a list of slices that will be returned to the lifecycle to be
// exported as separate layers during the export phase.
Slices []Slice
Expand All @@ -31,5 +35,5 @@ func (l LaunchMetadata) isEmpty() bool {
sbom = l.SBOM.Formats()
}

return len(sbom)+len(l.Processes)+len(l.Slices)+len(l.Labels)+len(l.BOM) == 0
return len(sbom)+len(l.Processes)+len(l.DirectProcesses)+len(l.Slices)+len(l.Labels)+len(l.BOM) == 0
}
28 changes: 27 additions & 1 deletion process.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package packit

// Process represents a process to be run during the launch phase as described
// in the specification:
// in the specification lower than v0.9:
// https://github.com/buildpacks/spec/blob/main/buildpack.md#launch. The
// fields of the process are describe in the specification of the launch.toml
// file:
Expand All @@ -28,3 +28,29 @@ type Process struct {
// absolute path or one relative to the default application directory.
WorkingDirectory string `toml:"working-directory,omitempty"`
}

// DirectProcess represents a process to be run during the launch phase as described
// in the specification higher or equal than v0.9:
// https://github.com/buildpacks/spec/blob/main/buildpack.md#launch. The
// fields of the process are describe in the specification of the launch.toml
// file:
// https://github.com/buildpacks/spec/blob/main/buildpack.md#launchtoml-toml.
type DirectProcess struct {
// Type is an identifier to describe the type of process to be executed, eg.
// "web".
Type string `toml:"type"`

// Command is the start command to be executed at launch.
Command []string `toml:"command"`

// Args is a list of arguments to be passed to the command at launch.
Args []string `toml:"args"`

// Default indicates if this process should be the default when launched.
Default bool `toml:"default,omitempty"`

// WorkingDirectory indicates if this process should be run in a working
// directory other than the application directory. This can either be an
// absolute path or one relative to the default application directory.
WorkingDirectory string `toml:"working-directory,omitempty"`
}
Loading

0 comments on commit 4a226ae

Please sign in to comment.