diff --git a/internal/cli/build.go b/internal/cli/build.go index 37d698424..2226f572b 100644 --- a/internal/cli/build.go +++ b/internal/cli/build.go @@ -23,7 +23,6 @@ import ( "path/filepath" "sync" - "github.com/chainguard-dev/go-apk/pkg/apk" "github.com/google/go-containerregistry/pkg/v1/layout" coci "github.com/sigstore/cosign/v2/pkg/oci" "github.com/sirupsen/logrus" @@ -43,7 +42,6 @@ import ( ) func buildCmd() *cobra.Command { - var useDockerMediaTypes bool var debugEnabled bool var quietEnabled bool var withVCS bool @@ -108,7 +106,6 @@ bill of materials) describing the image contents. logger, build.WithLogger(logger), build.WithConfig(args[0]), - build.WithDockerMediatypes(useDockerMediaTypes), build.WithBuildDate(buildDate), build.WithAssertions(build.RequireGroupFile(true), build.RequirePasswdFile(true)), build.WithSBOM(sbomPath), @@ -125,7 +122,6 @@ bill of materials) describing the image contents. }, } - cmd.Flags().BoolVar(&useDockerMediaTypes, "use-docker-mediatypes", false, "use Docker mediatypes for image layers/manifest") cmd.Flags().BoolVar(&debugEnabled, "debug", false, "enable debug logging") cmd.Flags().BoolVar(&quietEnabled, "quiet", false, "disable logging") cmd.Flags().BoolVar(&withVCS, "vcs", true, "detect and embed VCS URLs") @@ -154,7 +150,7 @@ func BuildCmd(ctx context.Context, imageRef, output string, archs []types.Archit defer os.RemoveAll(wd) // build all of the components in the working directory - idx, sboms, _, err := buildImageComponents(ctx, wd, archs, opts...) + idx, sboms, err := buildImageComponents(ctx, wd, archs, opts...) if err != nil { return err } @@ -185,13 +181,13 @@ func BuildCmd(ctx context.Context, imageRef, output string, archs []types.Archit // buildImage build all of the components of an image in a single working directory. // Each layer is a separate file, as are config, manifests, index and sbom. -func buildImageComponents(ctx context.Context, workDir string, archs []types.Architecture, opts ...build.Option) (idx coci.SignedImageIndex, sboms []types.SBOM, pkgs map[types.Architecture][]*apk.InstalledPackage, err error) { +func buildImageComponents(ctx context.Context, workDir string, archs []types.Architecture, opts ...build.Option) (idx coci.SignedImageIndex, sboms []types.SBOM, err error) { ctx, span := otel.Tracer("apko").Start(ctx, "buildImageComponents") defer span.End() o, ic, err := build.NewOptions(opts...) if err != nil { - return nil, nil, nil, err + return nil, nil, err } // cases: @@ -224,7 +220,7 @@ func buildImageComponents(ctx context.Context, workDir string, archs []types.Arc var errg errgroup.Group imageDir := filepath.Join(workDir, "image") if err := os.MkdirAll(imageDir, 0755); err != nil { - return nil, nil, nil, fmt.Errorf("unable to create working image directory %s: %w", imageDir, err) + return nil, nil, fmt.Errorf("unable to create working image directory %s: %w", imageDir, err) } imgs := map[types.Architecture]coci.SignedImage{} @@ -239,9 +235,6 @@ func buildImageComponents(ctx context.Context, workDir string, archs []types.Arc // computation. multiArchBDE := o.SourceDateEpoch - // pkgs will hold a reference to installed packages by architecture - pkgs = make(map[types.Architecture][]*apk.InstalledPackage) - for _, arch := range archs { arch := arch bopts := slices.Clone(opts) @@ -252,7 +245,7 @@ func buildImageComponents(ctx context.Context, workDir string, archs []types.Arc bc, err := build.New(ctx, tarfs.New(), bopts...) if err != nil { - return nil, nil, nil, err + return nil, nil, err } // save the build context for later @@ -264,15 +257,6 @@ func buildImageComponents(ctx context.Context, workDir string, archs []types.Arc return fmt.Errorf("failed to build layer image for %q: %w", arch, err) } - installed, err := bc.InstalledPackages() - if err != nil { - return fmt.Errorf("failed to get installed packages for %q: %w", arch, err) - } - // this should be unnecessary, as the arch list already is unique, - // but the race detector complains, so we can use it anyways - mtx.Lock() - pkgs[arch] = installed - mtx.Unlock() // Compute the "build date epoch" from the packages that were // installed. The "build date epoch" is the MAX of the builddate // embedded in the installed APKs. If SOURCE_DATE_EPOCH is @@ -304,13 +288,13 @@ func buildImageComponents(ctx context.Context, workDir string, archs []types.Arc }) } if err := errg.Wait(); err != nil { - return nil, nil, nil, err + return nil, nil, err } // generate the index finalDigest, idx, err := oci.GenerateIndex(ctx, *ic, imgs) if err != nil { - return nil, nil, nil, fmt.Errorf("failed to generate OCI index: %w", err) + return nil, nil, fmt.Errorf("failed to generate OCI index: %w", err) } opts = append(opts, @@ -321,11 +305,11 @@ func buildImageComponents(ctx context.Context, workDir string, archs []types.Arc o, ic, err = build.NewOptions(opts...) if err != nil { - return nil, nil, nil, err + return nil, nil, err } if _, err := build.WriteIndex(o, idx); err != nil { - return nil, nil, nil, fmt.Errorf("failed to write OCI index: %w", err) + return nil, nil, fmt.Errorf("failed to write OCI index: %w", err) } // the sboms are saved to the same working directory as the image components @@ -353,17 +337,17 @@ func buildImageComponents(ctx context.Context, workDir string, archs []types.Arc } if err := g.Wait(); err != nil { - return nil, nil, nil, err + return nil, nil, err } files, err := build.GenerateIndexSBOM(ctx, *o, *ic, finalDigest, imgs) if err != nil { - return nil, nil, nil, fmt.Errorf("generating index SBOM: %w", err) + return nil, nil, fmt.Errorf("generating index SBOM: %w", err) } sboms = append(sboms, files...) } - return idx, sboms, pkgs, nil + return idx, sboms, nil } // rename just like os.Rename, but does a copy and delete if the rename fails diff --git a/internal/cli/options.go b/internal/cli/options.go index 6b6682bec..62d9007fd 100644 --- a/internal/cli/options.go +++ b/internal/cli/options.go @@ -3,51 +3,14 @@ package cli import "chainguard.dev/apko/pkg/log" type publishOpt struct { - packageVersionTag string - packageVersionTagStem bool - packageVersionTagPrefix string - tagSuffix string - local bool - stageTags string - tags []string - logger log.Logger + local bool + tags []string + logger log.Logger } // PublishOption is an option for publishing type PublishOption func(*publishOpt) error -// WithPackageVersionTag sets a tag to use, e.g. `glibc-2.31`. -func WithPackageVersionTag(pvt string) PublishOption { - return func(p *publishOpt) error { - p.packageVersionTag = pvt - return nil - } -} - -// WithPackageVersionTagStem sets whether to use the package version tag stem, e.g. `glibc`. -func WithPackageVersionTagStem(packageVersionTagStem bool) PublishOption { - return func(p *publishOpt) error { - p.packageVersionTagStem = packageVersionTagStem - return nil - } -} - -// WithPackageVersionTagPrefix sets a tag prefix to use, e.g. `glibc-`. -func WithPackageVersionTagPrefix(packageVersionTagPrefix string) PublishOption { - return func(p *publishOpt) error { - p.packageVersionTagPrefix = packageVersionTagPrefix - return nil - } -} - -// WithTagSuffix sets a tag suffix to use, e.g. `-glibc`. -func WithTagSuffix(tagSuffix string) PublishOption { - return func(p *publishOpt) error { - p.tagSuffix = tagSuffix - return nil - } -} - // WithLocal sets whether to publish image to local Docker daemon. func WithLocal(local bool) PublishOption { return func(p *publishOpt) error { @@ -56,14 +19,6 @@ func WithLocal(local bool) PublishOption { } } -// WithStageTags prevents tagging, and innstead writes all tags to the filename provided. -func WithStageTags(stageTags string) PublishOption { - return func(p *publishOpt) error { - p.stageTags = stageTags - return nil - } -} - // WithTags tags to use func WithTags(tags ...string) PublishOption { return func(p *publishOpt) error { diff --git a/internal/cli/publish.go b/internal/cli/publish.go index db52edf81..ec3ab4287 100644 --- a/internal/cli/publish.go +++ b/internal/cli/publish.go @@ -21,7 +21,6 @@ import ( "os" "path/filepath" "regexp" - "sort" "strings" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login" @@ -35,7 +34,6 @@ import ( "go.opentelemetry.io/otel" "golang.org/x/sync/errgroup" - "chainguard.dev/apko/pkg/apk" "chainguard.dev/apko/pkg/build" "chainguard.dev/apko/pkg/build/oci" "chainguard.dev/apko/pkg/build/types" @@ -46,13 +44,8 @@ import ( func publish() *cobra.Command { var imageRefs string - var useDockerMediaTypes bool var buildDate string var sbomPath string - var packageVersionTag string - var packageVersionTagStem bool - var packageVersionTagPrefix string - var tagSuffix string var sbomFormats []string var archstrs []string var extraKeys []string @@ -65,7 +58,6 @@ func publish() *cobra.Command { var withVCS bool var writeSBOM bool var local bool - var stageTags string var cacheDir string var offline bool @@ -131,7 +123,6 @@ in a keychain.`, []build.Option{ build.WithLogger(logger), build.WithConfig(args[0]), - build.WithDockerMediatypes(useDockerMediaTypes), build.WithBuildDate(buildDate), build.WithAssertions(build.RequireGroupFile(true), build.RequirePasswdFile(true)), build.WithSBOM(sbomPath), @@ -148,12 +139,7 @@ in a keychain.`, []PublishOption{ // these are extra here just for publish; everything before is the same for BuildCmd as PublishCmd WithLogger(logger), - WithPackageVersionTag(packageVersionTag), - WithPackageVersionTagStem(packageVersionTagStem), - WithPackageVersionTagPrefix(packageVersionTagPrefix), - WithTagSuffix(tagSuffix), WithLocal(local), - WithStageTags(stageTags), WithTags(args[1:]...), }, ); err != nil { @@ -163,7 +149,6 @@ in a keychain.`, }, } - cmd.Flags().BoolVar(&useDockerMediaTypes, "use-docker-mediatypes", false, "use Docker mediatypes for image layers/manifest") cmd.Flags().BoolVar(&debugEnabled, "debug", false, "enable debug logging") cmd.Flags().BoolVar(&quietEnabled, "quiet", false, "disable logging") cmd.Flags().BoolVar(&withVCS, "vcs", true, "detect and embed VCS URLs") @@ -182,12 +167,7 @@ in a keychain.`, cmd.Flags().BoolVar(&offline, "offline", false, "do not use network to fetch packages (cache must be pre-populated)") // these are extra here just for publish; everything before is the same for BuildCmd as PublishCmd - cmd.Flags().StringVar(&packageVersionTag, "package-version-tag", "", "Tag the final image with the version of the package passed in") - cmd.Flags().BoolVar(&packageVersionTagStem, "package-version-tag-stem", false, "add additional tags by stemming the package version") - cmd.Flags().StringVar(&packageVersionTagPrefix, "package-version-tag-prefix", "", "prefix for package version tag(s)") - cmd.Flags().StringVar(&tagSuffix, "tag-suffix", "", "suffix to use for automatically generated tags") cmd.Flags().BoolVar(&local, "local", false, "publish image just to local Docker daemon") - cmd.Flags().StringVar(&stageTags, "stage-tags", "", "path to file to write list of tags to instead of publishing them") cmd.Flags().StringVar(&imageRefs, "image-refs", "", "path to file where a list of the published image references will be written") return cmd @@ -211,14 +191,12 @@ func PublishCmd(ctx context.Context, outputRefs string, archs []types.Architectu defer os.RemoveAll(wd) // build all of the components in the working directory - idx, sboms, pkgs, err := buildImageComponents(ctx, wd, archs, buildOpts...) + idx, sboms, err := buildImageComponents(ctx, wd, archs, buildOpts...) if err != nil { return fmt.Errorf("failed to build image components: %w", err) } var ( - stageTags = opts.stageTags - shouldPushTags = stageTags == "" local = opts.local logger = opts.logger tags = opts.tags @@ -242,42 +220,6 @@ func PublishCmd(ctx context.Context, outputRefs string, archs []types.Architectu return nil } - // generate additional tags from the package information per architecture - tagsByArch := make(map[types.Architecture][]string) - for arch, pkgList := range pkgs { - addTags, err := apk.AdditionalTags(pkgList, opts.logger, tags, opts.packageVersionTag, opts.packageVersionTagPrefix, opts.tagSuffix, opts.packageVersionTagStem) - if err != nil { - return fmt.Errorf("failed to generate additional tags for arch %s: %w", arch, err) - } - tagsByArch[arch] = append(tags, addTags...) - } - - // if the tags are not identical across arches, that is an error - allTagsMap := make(map[string]bool) - for arch, archTags := range tagsByArch { - tagSet := make(map[string]bool) - for _, tag := range archTags { - tagSet[tag] = true - } - if len(allTagsMap) == 0 { - allTagsMap = tagSet - continue - } - if len(tagSet) != len(allTagsMap) { - return fmt.Errorf("tags for arch %s are not identical to other arches", arch) - } - for tag := range tagSet { - if !allTagsMap[tag] { - return fmt.Errorf("tags for arch %s are not identical to other arches", arch) - } - } - } - // and now generate a slice - allTags := make([]string, 0, len(allTagsMap)) - for tag := range allTagsMap { - allTags = append(allTags, tag) - } - // publish each arch-specific image // TODO: This should just happen as part of PublishIndex. ref, err := name.ParseReference(tags[0]) @@ -293,7 +235,7 @@ func PublishCmd(ctx context.Context, outputRefs string, archs []types.Architectu } // publish the index - finalDigest, err := oci.PublishIndex(ctx, idx, logger, shouldPushTags, allTags, ropt...) + finalDigest, err := oci.PublishIndex(ctx, idx, logger, tags, ropt...) if err != nil { return fmt.Errorf("publishing image index: %w", err) } @@ -308,43 +250,22 @@ func PublishCmd(ctx context.Context, outputRefs string, archs []types.Architectu } } - if !shouldPushTags { - tmp := map[string]bool{} - for _, tag := range allTags { - if !strings.Contains(tag, ":") { - tag = fmt.Sprintf("%s:latest", tag) - } - tmp[tag] = true - } - sortedUniqueTags := make([]string, 0, len(tmp)) - for k := range tmp { - sortedUniqueTags = append(sortedUniqueTags, k) - } - sort.Strings(sortedUniqueTags) - logger.Printf("Writing list of tags to %s (%d total)", stageTags, len(sortedUniqueTags)) - - //nolint:gosec // Make tags file readable by non-root - if err := os.WriteFile(stageTags, []byte(strings.Join(sortedUniqueTags, "\n")+"\n"), 0o666); err != nil { - return fmt.Errorf("failed to write tags: %w", err) - } - } else { - // TODO: Why does this happen separately from PublishIndex? - skipLocalCopy := strings.HasPrefix(finalDigest.Name(), fmt.Sprintf("%s/", oci.LocalDomain)) - g, ctx := errgroup.WithContext(ctx) - for _, at := range additionalTags { - at := at - if skipLocalCopy { - // TODO: We probably don't need this now that we return early. - logger.Warnf("skipping local domain tag %s", at) - continue - } - g.Go(func() error { - return oci.Copy(ctx, finalDigest.Name(), at, ropt...) - }) - } - if err := g.Wait(); err != nil { - return err + // TODO: Why does this happen separately from PublishIndex? + skipLocalCopy := strings.HasPrefix(finalDigest.Name(), fmt.Sprintf("%s/", oci.LocalDomain)) + g, ctx := errgroup.WithContext(ctx) + for _, at := range additionalTags { + at := at + if skipLocalCopy { + // TODO: We probably don't need this now that we return early. + logger.Warnf("skipping local domain tag %s", at) + continue } + g.Go(func() error { + return oci.Copy(ctx, finalDigest.Name(), at, ropt...) + }) + } + if err := g.Wait(); err != nil { + return err } // publish each arch-specific sbom diff --git a/pkg/build/build.go b/pkg/build/build.go index d61f4bfb8..484a12bca 100644 --- a/pkg/build/build.go +++ b/pkg/build/build.go @@ -143,17 +143,12 @@ func (bc *Context) ImageLayoutToLayer(ctx context.Context) (string, v1.Layer, er Hex: hex.EncodeToString(digest.Sum(make([]byte, 0, digest.Size()))), } - mt := v1types.OCILayer - if bc.o.UseDockerMediaTypes { - mt = v1types.DockerLayer - } - l := &layer{ filename: layerTarGZ, desc: &v1.Descriptor{ Digest: h, Size: size, - MediaType: mt, + MediaType: v1types.OCILayer, }, diffid: &v1.Hash{ Algorithm: "sha256", diff --git a/pkg/build/build_implementation.go b/pkg/build/build_implementation.go index d30084021..d655aeb65 100644 --- a/pkg/build/build_implementation.go +++ b/pkg/build/build_implementation.go @@ -129,12 +129,10 @@ func (bc *Context) buildImage(ctx context.Context) error { return fmt.Errorf("failed to mutate paths: %w", err) } - if err := GenerateOSRelease(bc.fs, &bc.o, &bc.ic); err != nil { - if errors.Is(err, ErrOSReleaseAlreadyPresent) { - bc.Logger().Warnf("did not generate /etc/os-release: %v", err) - } else { - return fmt.Errorf("failed to generate /etc/os-release: %w", err) - } + if err := generateOSRelease(bc.fs, &bc.o, &bc.ic); errors.Is(err, ErrOSReleaseAlreadyPresent) { + bc.Logger().Infof("did not generate /etc/os-release: %v", err) + } else if err != nil { + return fmt.Errorf("failed to generate /etc/os-release: %w", err) } if err := bc.s6.WriteSupervisionTree(bc.ic.Entrypoint.Services); err != nil { diff --git a/pkg/build/oci/publish.go b/pkg/build/oci/publish.go index 6d2d92ef5..2ed7ca4a5 100644 --- a/pkg/build/oci/publish.go +++ b/pkg/build/oci/publish.go @@ -128,7 +128,7 @@ func LoadImage(ctx context.Context, image oci.SignedImage, logger log.Logger, ta // Note that docker, when provided with a multi-architecture index, will load just the image inside for the provided // platform, defaulting to the one on which the docker daemon is running. // PublishIndex will determine that platform and use it to publish the updated index. -func PublishIndex(ctx context.Context, idx oci.SignedImageIndex, logger log.Logger, shouldPushTags bool, tags []string, remoteOpts ...remote.Option) (name.Digest, error) { +func PublishIndex(ctx context.Context, idx oci.SignedImageIndex, logger log.Logger, tags []string, remoteOpts ...remote.Option) (name.Digest, error) { // TODO(jason): Also set annotations on the index. ggcr's // pkg/v1/mutate.Annotations will drop the interface methods from // oci.SignedImageIndex, so we may need to reimplement @@ -147,16 +147,10 @@ func PublishIndex(ctx context.Context, idx oci.SignedImageIndex, logger log.Logg dig := ref.Context().Digest(h.String()) toPublish := tags - msg := "publishing index tag" - - if !shouldPushTags { - toPublish = []string{dig.String()} - msg = "publishing index without tag (digest only)" - } g, ctx := errgroup.WithContext(ctx) for _, tag := range toPublish { - logger.Printf("%s %v", msg, tag) + logger.Printf("publishing index tag %v", tag) ref, err := name.ParseReference(tag) if err != nil { diff --git a/pkg/build/options.go b/pkg/build/options.go index ef7a1ccbd..9ae3e4d59 100644 --- a/pkg/build/options.go +++ b/pkg/build/options.go @@ -153,14 +153,6 @@ func WithArch(arch types.Architecture) Option { } } -// WithDockerMediatypes determine whether to use Docker mediatypes for the build context. -func WithDockerMediatypes(useDockerMediaTypes bool) Option { - return func(bc *Context) error { - bc.o.UseDockerMediaTypes = useDockerMediaTypes - return nil - } -} - // WithLogger sets the log.Logger implementation to be used by the build context. func WithLogger(logger log.Logger) Option { return func(bc *Context) error { diff --git a/pkg/build/os-release.go b/pkg/build/os-release.go index b33fb862c..0d3f14c6f 100644 --- a/pkg/build/os-release.go +++ b/pkg/build/os-release.go @@ -52,7 +52,7 @@ func maybeGenerateVendorReleaseFile(fsys apkfs.FullFS, ic *types.ImageConfigurat return nil } -func GenerateOSRelease(fsys apkfs.FullFS, o *options.Options, ic *types.ImageConfiguration) error { +func generateOSRelease(fsys apkfs.FullFS, o *options.Options, ic *types.ImageConfiguration) error { path := filepath.Join("etc", "os-release") osReleaseExists := true diff --git a/pkg/build/sbom.go b/pkg/build/sbom.go index baa6edc73..74c7e3c29 100644 --- a/pkg/build/sbom.go +++ b/pkg/build/sbom.go @@ -56,12 +56,7 @@ func newSBOM(fsys apkfs.FullFS, o options.Options, ic types.ImageConfiguration, sopt.ImageInfo.SourceDateEpoch = bde sopt.Formats = o.SBOMFormats sopt.ImageInfo.VCSUrl = ic.VCSUrl - - if o.UseDockerMediaTypes { - sopt.ImageInfo.ImageMediaType = ggcrtypes.DockerManifestSchema2 - } else { - sopt.ImageInfo.ImageMediaType = ggcrtypes.OCIManifestSchema1 - } + sopt.ImageInfo.ImageMediaType = ggcrtypes.OCIManifestSchema1 sopt.OutputDir = o.TempDir() if o.SBOMPath != "" { @@ -165,9 +160,6 @@ func GenerateIndexSBOM(ctx context.Context, o options.Options, ic types.ImageCon s.ImageInfo.IndexDigest = h s.ImageInfo.IndexMediaType = ggcrtypes.OCIImageIndex - if o.UseDockerMediaTypes { - s.ImageInfo.IndexMediaType = ggcrtypes.DockerManifestList - } // Make sure we have a determinstic for iterating over imgs. archs := make([]types.Architecture, 0, len(imgs)) diff --git a/pkg/options/options.go b/pkg/options/options.go index bebd54c2b..b94426b66 100644 --- a/pkg/options/options.go +++ b/pkg/options/options.go @@ -27,7 +27,6 @@ import ( ) type Options struct { - UseDockerMediaTypes bool `json:"useDockerMediaTypes,omitempty"` WithVCS bool `json:"withVCS,omitempty"` TarballPath string `json:"tarballPath,omitempty"` Tags []string `json:"tags,omitempty"` @@ -44,7 +43,6 @@ type Options struct { PackageVersionTagPrefix string `json:"packageVersionTagPrefix,omitempty"` TagSuffix string `json:"tagSuffix,omitempty"` Local bool `json:"local,omitempty"` - StageTags string `json:"stageTags,omitempty"` CacheDir string `json:"cacheDir,omitempty"` Offline bool `json:"offline,omitempty"`