Skip to content
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
17 changes: 9 additions & 8 deletions internal/cli/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import (
"chainguard.dev/apko/pkg/build"
"chainguard.dev/apko/pkg/build/oci"
"chainguard.dev/apko/pkg/build/types"
"chainguard.dev/apko/pkg/sbom"
"chainguard.dev/apko/pkg/sbom/generator"
"chainguard.dev/apko/pkg/tarfs"
)

Expand Down Expand Up @@ -85,8 +85,9 @@ Along the image, apko will generate SBOMs (software bill of materials) describin
return fmt.Errorf("parsing annotations from command line: %w", err)
}

if !writeSBOM {
sbomFormats = []string{}
var sbomGenerators []generator.Generator
if writeSBOM && len(sbomFormats) > 0 {
sbomGenerators = generator.Generators(sbomFormats...)
}

tmp, err := os.MkdirTemp(os.TempDir(), "apko-temp-*")
Expand All @@ -102,7 +103,7 @@ Along the image, apko will generate SBOMs (software bill of materials) describin
build.WithConfig(args[0], includePaths),
build.WithBuildDate(buildDate),
build.WithSBOM(sbomPath),
build.WithSBOMFormats(sbomFormats),
build.WithSBOMGenerators(sbomGenerators...),
build.WithExtraKeys(extraKeys),
build.WithExtraBuildRepos(extraBuildRepos),
build.WithExtraRepos(extraRepos),
Expand All @@ -125,7 +126,7 @@ Along the image, apko will generate SBOMs (software bill of materials) describin
cmd.Flags().StringVar(&sbomPath, "sbom-path", "", "generate SBOMs in dir (defaults to image directory)")
cmd.Flags().StringSliceVar(&archstrs, "arch", nil, "architectures to build for (e.g., x86_64,ppc64le,arm64) -- default is all, unless specified in config. Can also use 'host' to indicate arch of host this is running on")
cmd.Flags().StringSliceVarP(&extraKeys, "keyring-append", "k", []string{}, "path to extra keys to include in the keyring")
cmd.Flags().StringSliceVar(&sbomFormats, "sbom-formats", sbom.DefaultOptions.Formats, "SBOM formats to output")
cmd.Flags().StringSliceVar(&sbomFormats, "sbom-formats", []string{"spdx"}, "SBOM formats to output")
cmd.Flags().StringSliceVarP(&extraBuildRepos, "build-repository-append", "b", []string{}, "path to extra repositories to include")
cmd.Flags().StringSliceVarP(&extraRepos, "repository-append", "r", []string{}, "path to extra repositories to include")
cmd.Flags().StringSliceVarP(&extraPackages, "package-append", "p", []string{}, "extra packages to include")
Expand Down Expand Up @@ -285,7 +286,7 @@ func buildImageComponents(ctx context.Context, workDir string, archs []types.Arc
}

var outputs []types.SBOM
if len(o.SBOMFormats) != 0 {
if len(o.SBOMGenerators) != 0 {
outputs, err = bc.GenerateImageSBOM(ctx, arch, img)
if err != nil {
return fmt.Errorf("generating sbom for %s: %w", arch, err)
Expand All @@ -301,7 +302,7 @@ func buildImageComponents(ctx context.Context, workDir string, archs []types.Arc
multiArchBDE = bde
}

if len(o.SBOMFormats) != 0 {
if len(o.SBOMGenerators) != 0 {
sboms = append(sboms, outputs...)
}

Expand Down Expand Up @@ -333,7 +334,7 @@ func buildImageComponents(ctx context.Context, workDir string, archs []types.Arc
}

// the sboms are saved to the same working directory as the image components
if len(o.SBOMFormats) != 0 {
if len(o.SBOMGenerators) != 0 {
files, err := build.GenerateIndexSBOM(ctx, *o, *ic, finalDigest, imgs)
if err != nil {
return nil, nil, fmt.Errorf("generating index SBOM: %w", err)
Expand Down
5 changes: 3 additions & 2 deletions internal/cli/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"chainguard.dev/apko/internal/cli"
"chainguard.dev/apko/pkg/build"
"chainguard.dev/apko/pkg/build/types"
"chainguard.dev/apko/pkg/sbom/generator/spdx"
)

func TestBuild(t *testing.T) {
Expand All @@ -43,7 +44,7 @@ func TestBuild(t *testing.T) {
archs := types.ParseArchitectures([]string{"amd64", "arm64"})
opts := []build.Option{
build.WithConfig(config, []string{}),
build.WithSBOMFormats([]string{"spdx"}),
build.WithSBOMGenerators(spdx.New()),
build.WithTags("golden:latest"),
build.WithAnnotations(map[string]string{
"org.opencontainers.image.vendor": "Vendor",
Expand Down Expand Up @@ -126,7 +127,7 @@ func TestBuildWithBase(t *testing.T) {
lockfile := filepath.Join("testdata", "image_on_top.apko.lock.json")

archs := types.ParseArchitectures([]string{"amd64", "arm64"})
opts := []build.Option{build.WithConfig(config, []string{}), build.WithSBOMFormats([]string{"spdx"}), build.WithTags("golden_top:latest"), build.WithLockFile(lockfile), build.WithTempDir(apkoTempDir)}
opts := []build.Option{build.WithConfig(config, []string{}), build.WithSBOMGenerators(spdx.New()), build.WithTags("golden_top:latest"), build.WithLockFile(lockfile), build.WithTempDir(apkoTempDir)}

sbomPath := filepath.Join(tmp, "sboms")
err := os.MkdirAll(sbomPath, 0o750)
Expand Down
11 changes: 6 additions & 5 deletions internal/cli/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import (
"chainguard.dev/apko/pkg/build"
"chainguard.dev/apko/pkg/build/oci"
"chainguard.dev/apko/pkg/build/types"
"chainguard.dev/apko/pkg/sbom"
"chainguard.dev/apko/pkg/sbom/generator"
)

func publish() *cobra.Command {
Expand Down Expand Up @@ -70,8 +70,9 @@ in a keychain.`,
return fmt.Errorf("requires at least 2 arg(s), 1 config file and at least 1 tag for the image")
}

if !writeSBOM {
sbomFormats = []string{}
var sbomGenerators []generator.Generator
if writeSBOM && len(sbomFormats) > 0 {
sbomGenerators = generator.Generators(sbomFormats...)
}
archs := types.ParseArchitectures(archstrs)
annotations, err := parseAnnotations(rawAnnotations)
Expand Down Expand Up @@ -109,7 +110,7 @@ in a keychain.`,
build.WithConfig(args[0], []string{}),
build.WithBuildDate(buildDate),
build.WithSBOM(sbomPath),
build.WithSBOMFormats(sbomFormats),
build.WithSBOMGenerators(sbomGenerators...),
build.WithExtraKeys(extraKeys),
build.WithExtraBuildRepos(extraBuildRepos),
build.WithExtraRepos(extraRepos),
Expand Down Expand Up @@ -140,7 +141,7 @@ in a keychain.`,
cmd.Flags().StringVar(&sbomPath, "sbom-path", "", "path to write the SBOMs")
cmd.Flags().StringSliceVar(&archstrs, "arch", nil, "architectures to build for (e.g., x86_64,ppc64le,arm64) -- default is all, unless specified in config.")
cmd.Flags().StringSliceVarP(&extraKeys, "keyring-append", "k", []string{}, "path to extra keys to include in the keyring")
cmd.Flags().StringSliceVar(&sbomFormats, "sbom-formats", sbom.DefaultOptions.Formats, "SBOM formats to output")
cmd.Flags().StringSliceVar(&sbomFormats, "sbom-formats", []string{"spdx"}, "SBOM formats to output")
cmd.Flags().StringSliceVarP(&extraBuildRepos, "build-repository-append", "b", []string{}, "path to extra repositories to include")
cmd.Flags().StringSliceVarP(&extraRepos, "repository-append", "r", []string{}, "path to extra repositories to include")
cmd.Flags().StringSliceVarP(&extraPackages, "package-append", "p", []string{}, "extra packages to include")
Expand Down
6 changes: 3 additions & 3 deletions internal/cli/publish_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import (
"chainguard.dev/apko/internal/tarfs"
"chainguard.dev/apko/pkg/build"
"chainguard.dev/apko/pkg/build/types"
"chainguard.dev/apko/pkg/sbom"
"chainguard.dev/apko/pkg/sbom/generator/spdx"
)

func TestPublish(t *testing.T) {
Expand Down Expand Up @@ -72,7 +72,7 @@ func TestPublish(t *testing.T) {
opts := []build.Option{
build.WithConfig(config, []string{}),
build.WithTags(dst),
build.WithSBOMFormats(sbom.DefaultOptions.Formats),
build.WithSBOMGenerators(spdx.New()),
build.WithAnnotations(map[string]string{"foo": "bar"}),
}
publishOpts := []cli.PublishOption{cli.WithTags(dst)}
Expand Down Expand Up @@ -146,7 +146,7 @@ func TestPublishLayering(t *testing.T) {
opts := []build.Option{
build.WithConfig(config, []string{}),
build.WithTags(dst),
build.WithSBOMFormats(sbom.DefaultOptions.Formats),
build.WithSBOMGenerators(spdx.New()),
build.WithAnnotations(map[string]string{"foo": "bar"}),
}
publishOpts := []cli.PublishOption{cli.WithTags(dst)}
Expand Down
3 changes: 3 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import (
"os/signal"

"chainguard.dev/apko/internal/cli"

// Import spdx generator to register it.
_ "chainguard.dev/apko/pkg/sbom/generator/spdx"
)

func main() {
Expand Down
2 changes: 1 addition & 1 deletion pkg/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ func (bc *Context) Arch() types.Architecture {
}

func (bc *Context) WantSBOM() bool {
return len(bc.o.SBOMFormats) != 0
return len(bc.o.SBOMGenerators) != 0
}

func (bc *Context) APK() *apk.APK {
Expand Down
14 changes: 13 additions & 1 deletion pkg/build/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"chainguard.dev/apko/pkg/apk/apk"
"chainguard.dev/apko/pkg/apk/auth"
"chainguard.dev/apko/pkg/build/types"
"chainguard.dev/apko/pkg/sbom/generator"

"github.com/chainguard-dev/clog"
)
Expand Down Expand Up @@ -110,9 +111,20 @@ func WithSBOM(path string) Option {
}
}

// WithSBOMFormats sets the SBOM generators to use.
//
// Deprecated: use WithSBOMGenerators instead.
func WithSBOMFormats(formats []string) Option {
return func(bc *Context) error {
bc.o.SBOMFormats = formats
bc.o.SBOMGenerators = generator.Generators(formats...)
return nil
}
}

// WithSBOMGenerators sets the SBOM generators to use.
func WithSBOMGenerators(generators ...generator.Generator) Option {
return func(bc *Context) error {
bc.o.SBOMGenerators = generators
return nil
}
}
Expand Down
30 changes: 8 additions & 22 deletions pkg/build/sbom.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import (
"chainguard.dev/apko/pkg/build/types"
"chainguard.dev/apko/pkg/options"
"chainguard.dev/apko/pkg/sbom"
"chainguard.dev/apko/pkg/sbom/generator"
soptions "chainguard.dev/apko/pkg/sbom/options"
)

Expand All @@ -60,7 +59,6 @@ func newSBOM(ctx context.Context, fsys apkfs.FullFS, o options.Options, ic types
}

sopt.ImageInfo.SourceDateEpoch = bde
sopt.Formats = o.SBOMFormats
sopt.ImageInfo.VCSUrl = ic.VCSUrl
sopt.ImageInfo.ImageMediaType = ggcrtypes.OCIManifestSchema1

Expand Down Expand Up @@ -125,20 +123,14 @@ func (bc *Context) GenerateImageSBOM(ctx context.Context, arch types.Architectur
s.ImageInfo.Arch = arch

var sboms = make([]types.SBOM, 0)
generators := generator.Generators()
for _, format := range s.Formats {
gen, ok := generators[format]
if !ok {
return nil, fmt.Errorf("unable to generate sboms: no generator available for format %s", format)
}

for _, gen := range bc.o.SBOMGenerators {
filename := filepath.Join(s.OutputDir, s.FileName+"."+gen.Ext())
if err := gen.Generate(ctx, &s, filename); err != nil {
return nil, fmt.Errorf("generating %s sbom: %w", format, err)
return nil, fmt.Errorf("generating %s sbom: %w", gen.Key(), err)
}
sboms = append(sboms, types.SBOM{
Path: filename,
Format: format,
Format: gen.Key(),
Arch: arch.String(),
Digest: h,
})
Expand Down Expand Up @@ -212,7 +204,7 @@ func GenerateIndexSBOM(ctx context.Context, o options.Options, ic types.ImageCon
_, span := otel.Tracer("apko").Start(ctx, "GenerateIndexSBOM")
defer span.End()

if len(o.SBOMFormats) == 0 {
if len(o.SBOMGenerators) == 0 {
log.Warn("skipping SBOM generation")
return nil, nil
}
Expand All @@ -238,14 +230,8 @@ func GenerateIndexSBOM(ctx context.Context, o options.Options, ic types.ImageCon
return archs[i].String() < archs[j].String()
})

generators := generator.Generators()
var sboms = make([]types.SBOM, 0, len(generators))
for _, format := range s.Formats {
gen, ok := generators[format]
if !ok {
return nil, fmt.Errorf("unable to generate sboms: no generator available for format %s", format)
}

var sboms = make([]types.SBOM, 0, len(o.SBOMGenerators))
for _, gen := range o.SBOMGenerators {
archImageInfos := make([]soptions.ArchImageInfo, 0, len(archs))
for _, arch := range archs {
i := imgs[arch]
Expand All @@ -270,11 +256,11 @@ func GenerateIndexSBOM(ctx context.Context, o options.Options, ic types.ImageCon

filename := filepath.Join(s.OutputDir, "sbom-index."+gen.Ext())
if err := gen.GenerateIndex(&s, filename); err != nil {
return nil, fmt.Errorf("generating %s sbom: %w", format, err)
return nil, fmt.Errorf("generating %s sbom: %w", gen.Key(), err)
}
sboms = append(sboms, types.SBOM{
Path: filename,
Format: format,
Format: gen.Key(),
Digest: h,
})
}
Expand Down
51 changes: 26 additions & 25 deletions pkg/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,38 +25,39 @@ import (
"chainguard.dev/apko/pkg/apk/apk"
"chainguard.dev/apko/pkg/apk/auth"
"chainguard.dev/apko/pkg/build/types"
"chainguard.dev/apko/pkg/sbom/generator"
)

type Options struct {
WithVCS bool `json:"withVCS,omitempty"`
// ImageConfigFile might, but does not have to be a filename. It might be any abstract configuration identifier.
ImageConfigFile string `json:"imageConfigFile,omitempty"`
// ImageConfigChecksum (when set) allows to detect mismatch between configuration and the lockfile.
ImageConfigChecksum string `json:"configChecksum,omitempty"`
TarballPath string `json:"tarballPath,omitempty"`
Tags []string `json:"tags,omitempty"`
SourceDateEpoch time.Time `json:"sourceDateEpoch,omitempty"`
SBOMPath string `json:"sbomPath,omitempty"`
SBOMFormats []string `json:"sbomFormats,omitempty"`
ExtraKeyFiles []string `json:"extraKeyFiles,omitempty"`
ExtraBuildRepos []string `json:"extraBuildRepos,omitempty"`
ExtraRepos []string `json:"extraRepos,omitempty"`
ExtraPackages []string `json:"extraPackages,omitempty"`
Arch types.Architecture `json:"arch,omitempty"`
TempDirPath string `json:"tempDirPath,omitempty"`
PackageVersionTag string `json:"packageVersionTag,omitempty"`
PackageVersionTagStem bool `json:"packageVersionTagStem,omitempty"`
PackageVersionTagPrefix string `json:"packageVersionTagPrefix,omitempty"`
TagSuffix string `json:"tagSuffix,omitempty"`
Local bool `json:"local,omitempty"`
CacheDir string `json:"cacheDir,omitempty"`
Offline bool `json:"offline,omitempty"`
SharedCache *apk.Cache `json:"-"`
Lockfile string `json:"lockfile,omitempty"`
Auth auth.Authenticator `json:"-"`
IncludePaths []string `json:"includePaths,omitempty"`
IgnoreSignatures bool `json:"ignoreSignatures,omitempty"`
Transport http.RoundTripper `json:"-"`
ImageConfigChecksum string `json:"configChecksum,omitempty"`
TarballPath string `json:"tarballPath,omitempty"`
Tags []string `json:"tags,omitempty"`
SourceDateEpoch time.Time `json:"sourceDateEpoch,omitempty"`
SBOMPath string `json:"sbomPath,omitempty"`
SBOMGenerators []generator.Generator `json:"-"`
ExtraKeyFiles []string `json:"extraKeyFiles,omitempty"`
ExtraBuildRepos []string `json:"extraBuildRepos,omitempty"`
ExtraRepos []string `json:"extraRepos,omitempty"`
ExtraPackages []string `json:"extraPackages,omitempty"`
Arch types.Architecture `json:"arch,omitempty"`
TempDirPath string `json:"tempDirPath,omitempty"`
PackageVersionTag string `json:"packageVersionTag,omitempty"`
PackageVersionTagStem bool `json:"packageVersionTagStem,omitempty"`
PackageVersionTagPrefix string `json:"packageVersionTagPrefix,omitempty"`
TagSuffix string `json:"tagSuffix,omitempty"`
Local bool `json:"local,omitempty"`
CacheDir string `json:"cacheDir,omitempty"`
Offline bool `json:"offline,omitempty"`
SharedCache *apk.Cache `json:"-"`
Lockfile string `json:"lockfile,omitempty"`
Auth auth.Authenticator `json:"-"`
IncludePaths []string `json:"includePaths,omitempty"`
IgnoreSignatures bool `json:"ignoreSignatures,omitempty"`
Transport http.RoundTripper `json:"-"`
}

type Auth struct{ User, Pass string }
Expand Down
Loading
Loading