Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for buildpack API v0.5 #121

Merged
merged 1 commit into from
Mar 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 98 additions & 13 deletions build.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"

"github.com/BurntSushi/toml"
"github.com/Masterminds/semver/v3"
"github.com/paketo-buildpacks/packit/internal"
)

Expand Down Expand Up @@ -54,6 +55,9 @@ type BuildFunc func(BuildContext) (BuildResult, error)
type BuildResult struct {
// Plan is the set of refinements to the Buildpack Plan that were performed
// during the build phase.
//
// Deprecated: Use LaunchMetadata or BuildMetadata instead. For more information
// see https://buildpacks.io/docs/reference/spec/migration/buildpack-api-0.4-0.5/
Plan BuildpackPlan

// Layers is a list of layers that will be persisted by the lifecycle at the
Expand All @@ -65,6 +69,26 @@ type BuildResult struct {
// the buildpack lifecycle specification:
// https://github.com/buildpacks/spec/blob/main/buildpack.md#launchtoml-toml
Launch LaunchMetadata

// Build is the metadata that will be persisted as build.toml according to
// the buildpack lifecycle specification:
// https://github.com/buildpacks/spec/blob/main/buildpack.md#buildtoml-toml
Build BuildMetadata
}

// BOMEntry contains a bill of materials entry.
type BOMEntry struct {
// Name represents the name of the entry.
Name string `toml:"name"`

// Metadata is the metadata of the entry. Optional.
Metadata map[string]interface{} `toml:"metadata,omitempty"`
}

// UnmetEntry contains the name of an unmet dependency from the build process
type UnmetEntry struct {
// Name represents the name of the entry.
Name string `toml:"name"`
}

// LaunchMetadata represents the launch metadata details persisted in the
Expand All @@ -82,6 +106,35 @@ type LaunchMetadata struct {
// Labels is a map of key-value pairs that will be returned to the lifecycle to be
// added as config label on the image metadata. Keys must be unique.
Labels map[string]string

// BOM is the Bill-of-Material entries containing information about the
// dependencies provided to the launch environment.
BOM []BOMEntry
}

func (l LaunchMetadata) isEmpty() bool {
return (len(l.Processes) == 0 &&
len(l.Slices) == 0 &&
len(l.Labels) == 0 &&
len(l.BOM) == 0)
}

func (b BuildMetadata) isEmpty() bool {
return (len(b.BOM) == 0 &&
len(b.Unmet) == 0)
}

// BuildMetadata represents the build metadata details persisted in the
// build.toml file according to the buildpack lifecycle specification:
// https://github.com/buildpacks/spec/blob/main/buildpack.md#buildtoml-toml.
type BuildMetadata struct {
// BOM is the Bill-of-Material entries containing information about the
// dependencies provided to the build environment.
BOM []BOMEntry `toml:"bom"`

// Unmet is a list of unmet entries from the build process that it was unable
// to provide.
Unmet []UnmetEntry `toml:"unmet"`
}

// Process represents a process to be run during the launch phase as described
Expand Down Expand Up @@ -193,14 +246,23 @@ func Build(f BuildFunc, options ...Option) {
}

var buildpackInfo struct {
Buildpack BuildpackInfo `toml:"buildpack"`
APIVersion string `toml:"api"`
Buildpack BuildpackInfo `toml:"buildpack"`
}

_, err = toml.DecodeFile(filepath.Join(cnbPath, "buildpack.toml"), &buildpackInfo)
if err != nil {
config.exitHandler.Error(err)
return
}

apiV05, _ := semver.NewVersion("0.5")
apiVersion, err := semver.NewVersion(buildpackInfo.APIVersion)
if err != nil {
config.exitHandler.Error(err)
return
}

result, err := f(BuildContext{
CNBPath: cnbPath,
Stack: os.Getenv("CNB_STACK_ID"),
Expand All @@ -216,10 +278,17 @@ func Build(f BuildFunc, options ...Option) {
return
}

err = config.tomlWriter.Write(planPath, result.Plan)
if err != nil {
config.exitHandler.Error(err)
return
if len(result.Plan.Entries) > 0 {
if apiVersion.GreaterThan(apiV05) || apiVersion.Equal(apiV05) {
config.exitHandler.Error(fmt.Errorf(`buildpack plan is read only since BuildPack API v0.5`))
return
}

err = config.tomlWriter.Write(planPath, result.Plan)
if err != nil {
config.exitHandler.Error(err)
return
}
}

layerTomls, err := filepath.Glob(filepath.Join(layersPath, "*.toml"))
Expand All @@ -229,7 +298,7 @@ func Build(f BuildFunc, options ...Option) {
}

for _, file := range layerTomls {
if filepath.Base(file) != "launch.toml" && filepath.Base(file) != "store.toml" {
if filepath.Base(file) != "launch.toml" && filepath.Base(file) != "store.toml" && filepath.Base(file) != "build.toml" {
err = os.Remove(file)
if err != nil {
config.exitHandler.Error(fmt.Errorf("failed to remove layer toml: %w", err))
Expand Down Expand Up @@ -264,24 +333,27 @@ func Build(f BuildFunc, options ...Option) {
}
}

if len(result.Launch.Processes) > 0 ||
len(result.Launch.Slices) > 0 ||
len(result.Launch.Labels) > 0 {
if !result.Launch.isEmpty() {
if apiVersion.LessThan(apiV05) && len(result.Launch.BOM) > 0 {
config.exitHandler.Error(fmt.Errorf("BOM entries in launch.toml is only supported with Buildpack API v0.5 or higher"))
return
}

type label struct {
Key string `toml:"key"`
Value string `toml:"value"`
}

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

launch.Processes = result.Launch.Processes
launch.Slices = result.Launch.Slices

launch.BOM = result.Launch.BOM
if len(result.Launch.Labels) > 0 {
launch.Labels = []label{}
for k, v := range result.Launch.Labels {
Expand All @@ -299,4 +371,17 @@ func Build(f BuildFunc, options ...Option) {
return
}
}

if !result.Build.isEmpty() {
if apiVersion.LessThan(apiV05) {
config.exitHandler.Error(fmt.Errorf("build.toml is only supported with Buildpack API v0.5 or higher"))
return

}
err = config.tomlWriter.Write(filepath.Join(layersPath, "build.toml"), result.Build)
if err != nil {
config.exitHandler.Error(err)
return
}
}
}
Loading