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

Adds a convenience method for getting the build, launch and layer BOM file paths #96

Merged
merged 1 commit into from
Nov 18, 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
32 changes: 2 additions & 30 deletions application.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,12 @@ package libcnb

// Application is the user contributed application to build.
type Application struct {

// Path is the path to the application.
Path string
}

// Label represents an image label.
type Label struct {

// Key is the key of the label.
Key string `toml:"key"`

Expand All @@ -35,7 +33,6 @@ type Label struct {

// Process represents metadata about a type of command that can be run.
type Process struct {

// Type is the type of the process.
Type string `toml:"type"`

Expand All @@ -62,7 +59,6 @@ type Slice struct {

// LaunchTOML represents the contents of launch.toml.
type LaunchTOML struct {

// Labels is the collection of image labels contributed by the buildpack.
Labels []Label `toml:"labels"`

Expand All @@ -71,44 +67,20 @@ type LaunchTOML struct {

// Slices is the collection of slices contributed by the buildpack.
Slices []Slice `toml:"slices"`

// BOM is a collection of entries for the bill of materials.
BOM []BOMEntry `toml:"bom"`
}

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

// BuildTOML represents the contents of build.toml.
type BuildTOML struct {
// BOM contains the build-time bill of materials.
BOM []BOMEntry `toml:"bom"`

// Unmet is a collection of buildpack plan entries that should be passed through to subsequent providers.
Unmet []UnmetPlanEntry
}

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

// 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"`

// Launch indicates whether the given entry is included in app image. If launch is true the entry
// will be added to the app image Bill of Materials. Launch should be true if the entry describes
// the contents of a launch layer or app layer.
Launch bool `toml:"-"`

// Build indicates whether the given entry is available at build time. If build is true the entry
// will be added to the build Bill of Materials.
Build bool `toml:"-"`
return len(b.Unmet) == 0
}

// Store represents the contents of store.toml
Expand Down
28 changes: 2 additions & 26 deletions build.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import (

// BuildContext contains the inputs to build.
type BuildContext struct {

// Application is application to build.
Application Application

Expand All @@ -57,9 +56,6 @@ type BuildContext struct {

// BuildResult contains the results of detection.
type BuildResult struct {
// BOM contains entries to be appended to the app image Bill of Materials and/or build Bill of Materials.
BOM *BOM

// Labels are the image labels contributed by the buildpack.
Labels []Label

Expand All @@ -80,16 +76,10 @@ type BuildResult struct {
Unmet []UnmetPlanEntry
}

// BOM contains all Bill of Materials entries
type BOM struct {
Entries []BOMEntry
}

// NewBuildResult creates a new BuildResult instance, initializing empty fields.
func NewBuildResult() BuildResult {
return BuildResult{
PersistentMetadata: make(map[string]interface{}),
BOM: &BOM{},
}
}

Expand All @@ -100,8 +90,8 @@ func (b BuildResult) String() string {
}

return fmt.Sprintf(
"{BOM: %+v, Labels:%+v Layers:%s PersistentMetadata:%+v Processes:%+v Slices:%+v, Unmet:%+v}",
b.BOM, b.Labels, l, b.PersistentMetadata, b.PersistentMetadata, b.Slices, b.Unmet,
"{Labels:%+v Layers:%s PersistentMetadata:%+v Processes:%+v Slices:%+v, Unmet:%+v}",
b.Labels, l, b.PersistentMetadata, b.PersistentMetadata, b.Slices, b.Unmet,
)
}

Expand Down Expand Up @@ -283,23 +273,10 @@ func Build(build BuildFunc, options ...Option) {
}
}

var launchBOM, buildBOM []BOMEntry
if result.BOM != nil {
for _, entry := range result.BOM.Entries {
if entry.Launch {
launchBOM = append(launchBOM, entry)
}
if entry.Build {
buildBOM = append(buildBOM, entry)
}
}
}

launch := LaunchTOML{
Labels: result.Labels,
Processes: result.Processes,
Slices: result.Slices,
BOM: launchBOM,
}

if !launch.isEmpty() {
Expand All @@ -322,7 +299,6 @@ func Build(build BuildFunc, options ...Option) {

buildTOML := BuildTOML{
Unmet: result.Unmet,
BOM: buildBOM,
}

if !buildTOML.isEmpty() {
Expand Down
37 changes: 0 additions & 37 deletions build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -451,17 +451,6 @@ version = "1.1.1"
it("writes launch.toml", func() {
buildFunc = func(libcnb.BuildContext) (libcnb.BuildResult, error) {
return libcnb.BuildResult{
BOM: &libcnb.BOM{Entries: []libcnb.BOMEntry{
{
Name: "test-launch-bom-entry",
Metadata: map[string]interface{}{"test-key": "test-value"},
Launch: true,
},
{
Name: "test-build-bom-entry",
Metadata: map[string]interface{}{"test-key": "test-value"},
},
}},
Labels: []libcnb.Label{
{
Key: "test-key",
Expand Down Expand Up @@ -508,13 +497,6 @@ version = "1.1.1"
Paths: []string{"test-path"},
},
},
BOM: []libcnb.BOMEntry{
{
Name: "test-launch-bom-entry",
Metadata: map[string]interface{}{"test-key": "test-value"},
Launch: true,
},
},
}))
})

Expand Down Expand Up @@ -568,18 +550,6 @@ version = "1.1.1"
it("writes build.toml", func() {
buildFunc = func(libcnb.BuildContext) (libcnb.BuildResult, error) {
return libcnb.BuildResult{
BOM: &libcnb.BOM{Entries: []libcnb.BOMEntry{
{
Name: "test-build-bom-entry",
Metadata: map[string]interface{}{"test-key": "test-value"},
Build: true,
},
{
Name: "test-launch-bom-entry",
Metadata: map[string]interface{}{"test-key": "test-value"},
Build: false,
},
}},
Unmet: []libcnb.UnmetPlanEntry{
{
Name: "test-entry",
Expand All @@ -595,13 +565,6 @@ version = "1.1.1"

Expect(tomlWriter.Calls[0].Arguments[0]).To(Equal(filepath.Join(layersPath, "build.toml")))
Expect(tomlWriter.Calls[0].Arguments[1]).To(Equal(libcnb.BuildTOML{
BOM: []libcnb.BOMEntry{
{
Name: "test-build-bom-entry",
Metadata: map[string]interface{}{"test-key": "test-value"},
Build: true,
},
},
Unmet: []libcnb.UnmetPlanEntry{
{
Name: "test-entry",
Expand Down
31 changes: 28 additions & 3 deletions layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import (

// Exec represents the exec.d layer location
type Exec struct {

// Path is the path to the exec.d directory.
Path string
}
Expand Down Expand Up @@ -68,9 +67,21 @@ func (p Profile) ProcessAddf(processType string, name string, format string, a .
p.Addf(filepath.Join(processType, name), format, a...)
}

// BOMFormat indicates the format of the SBOM entry
type SBOMFormat int

const (
CycloneDXJSON SBOMFormat = iota
SPDXJSON
SyftJSON
)

func (b SBOMFormat) String() string {
return []string{"cdx.json", "spdx.json", "syft.json"}[b]
}

// Contribute represents a layer managed by the buildpack.
type Layer struct {

// LayerTypes indicates the type of layer
LayerTypes `toml:"types"`

Expand Down Expand Up @@ -99,6 +110,11 @@ type Layer struct {
Exec Exec `toml:"-"`
}

// SBOMPath returns the path to the layer specific SBOM File
func (l Layer) SBOMPath(bt SBOMFormat) string {
return filepath.Join(filepath.Dir(l.Path), fmt.Sprintf("%s.sbom.%s", l.Name, bt))
}

// LayerTypes describes which types apply to a given layer. A layer may have any combination of Launch, Build, and
// Cache types.
type LayerTypes struct {
Expand All @@ -114,7 +130,6 @@ type LayerTypes struct {

// Layers represents the layers part of the specification.
type Layers struct {

// Path is the layers filesystem location.
Path string
}
Expand Down Expand Up @@ -150,3 +165,13 @@ func (l *Layers) Layer(name string) (Layer, error) {

return layer, nil
}

// BOMBuildPath returns the full path to the build SBoM file for the buildpack
func (l Layers) BuildSBOMPath(bt SBOMFormat) string {
return filepath.Join(l.Path, fmt.Sprintf("build.sbom.%s", bt))
}

// BOMLaunchPath returns the full path to the launch SBoM file for the buildpack
func (l Layers) LaunchSBOMPath(bt SBOMFormat) string {
return filepath.Join(l.Path, fmt.Sprintf("launch.sbom.%s", bt))
}
12 changes: 12 additions & 0 deletions layer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,18 @@ func testLayer(t *testing.T, context spec.G, it spec.S) {
Expect(l.Profile).To(Equal(libcnb.Profile{}))
})

it("generates BOM paths", func() {
l, err := layers.Layer("test-name")
Expect(err).NotTo(HaveOccurred())

Expect(l.Path).To(Equal(filepath.Join(path, "test-name")))
Expect(layers.BuildSBOMPath(libcnb.CycloneDXJSON)).To(Equal(filepath.Join(path, "build.sbom.cdx.json")))
Expect(layers.BuildSBOMPath(libcnb.SPDXJSON)).To(Equal(filepath.Join(path, "build.sbom.spdx.json")))
Expect(layers.BuildSBOMPath(libcnb.SyftJSON)).To(Equal(filepath.Join(path, "build.sbom.syft.json")))
Expect(layers.LaunchSBOMPath(libcnb.SyftJSON)).To(Equal(filepath.Join(path, "launch.sbom.syft.json")))
Expect(l.SBOMPath(libcnb.SyftJSON)).To(Equal(filepath.Join(path, "test-name.sbom.syft.json")))
})

it("reads existing 0.5 metadata", func() {
Expect(ioutil.WriteFile(
filepath.Join(path, "test-name.toml"),
Expand Down