diff --git a/README.md b/README.md index 3cea9cc6..9dba82cd 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ You don't need to read this document unless you want to use the full-featured st - [Building a Dockerfile using external frontend:](#building-a-dockerfile-using-external-frontend) - [Building a Dockerfile with experimental features like `RUN --mount=type=(bind|cache|tmpfs|secret|ssh)`](#building-a-dockerfile-with-experimental-features-like-run---mounttypebindcachetmpfssecretssh) - [Output](#output) - - [Registry](#registry) + - [Image/Registry](#imageregistry) - [Local directory](#local-directory) - [Docker tarball](#docker-tarball) - [OCI tarball](#oci-tarball) @@ -199,7 +199,7 @@ See [`frontend/dockerfile/docs/experimental.md`](frontend/dockerfile/docs/experi By default, the build result and intermediate cache will only remain internally in BuildKit. An output needs to be specified to retrieve the result. -#### Registry +#### Image/Registry ```bash buildctl build ... --output type=image,name=docker.io/username/image,push=true @@ -215,6 +215,17 @@ buildctl build ...\ --import-cache type=registry,ref=docker.io/username/image ``` +Keys supported by image output: +* `name=[value]`: image name +* `push=true`: push after creating the image +* `push-by-digest=true`: push unnamed image +* `registry.insecure=true`: push to insecure HTTP registry +* `oci-mediatypes=true`: use OCI mediatypes in configuration JSON instead of Docker's +* `unpack=true`: unpack image after creation (for use with containerd) +* `dangling-name-prefix=[value]`: name image with `prefix@` , used for anonymous images +* `name-canonical=true`: add additional canonical name `name@` + + If credentials are required, `buildctl` will attempt to read Docker configuration file `$DOCKER_CONFIG/config.json`. `$DOCKER_CONFIG` defaults to `~/.docker`. diff --git a/exporter/containerimage/export.go b/exporter/containerimage/export.go index edc6673c..23da4cac 100644 --- a/exporter/containerimage/export.go +++ b/exporter/containerimage/export.go @@ -26,12 +26,14 @@ import ( ) const ( - keyImageName = "name" - keyPush = "push" - keyPushByDigest = "push-by-digest" - keyInsecure = "registry.insecure" - keyUnpack = "unpack" - ociTypes = "oci-mediatypes" + keyImageName = "name" + keyPush = "push" + keyPushByDigest = "push-by-digest" + keyInsecure = "registry.insecure" + keyUnpack = "unpack" + keyDanglingPrefix = "dangling-name-prefix" + keyNameCanonical = "name-canonical" + ociTypes = "oci-mediatypes" ) type Opt struct { @@ -111,6 +113,18 @@ func (e *imageExporter) Resolve(ctx context.Context, opt map[string]string) (exp return nil, errors.Wrapf(err, "non-bool value specified for %s", k) } i.ociTypes = b + case keyDanglingPrefix: + i.danglingPrefix = v + case keyNameCanonical: + if v == "" { + i.nameCanonical = true + continue + } + b, err := strconv.ParseBool(v) + if err != nil { + return nil, errors.Wrapf(err, "non-bool value specified for %s", k) + } + i.nameCanonical = b default: if i.meta == nil { i.meta = make(map[string][]byte) @@ -123,13 +137,15 @@ func (e *imageExporter) Resolve(ctx context.Context, opt map[string]string) (exp type imageExporterInstance struct { *imageExporter - targetName string - push bool - pushByDigest bool - unpack bool - insecure bool - ociTypes bool - meta map[string][]byte + targetName string + push bool + pushByDigest bool + unpack bool + insecure bool + ociTypes bool + nameCanonical bool + danglingPrefix string + meta map[string][]byte } func (e *imageExporterInstance) Name() string { @@ -165,24 +181,35 @@ func (e *imageExporterInstance) Export(ctx context.Context, src exporter.Source) e.targetName = string(n) } + nameCanonical := e.nameCanonical + if e.targetName == "" && e.danglingPrefix != "" { + e.targetName = e.danglingPrefix + "@" + desc.Digest.String() + nameCanonical = false + } + if e.targetName != "" { targetNames := strings.Split(e.targetName, ",") for _, targetName := range targetNames { if e.opt.Images != nil { tagDone := oneOffProgress(ctx, "naming to "+targetName) img := images.Image{ - Name: targetName, Target: *desc, CreatedAt: time.Now(), } + sfx := []string{""} + if nameCanonical { + sfx = append(sfx, "@"+desc.Digest.String()) + } + for _, sfx := range sfx { + img.Name = targetName + sfx + if _, err := e.opt.Images.Update(ctx, img); err != nil { + if !errdefs.IsNotFound(err) { + return nil, tagDone(err) + } - if _, err := e.opt.Images.Update(ctx, img); err != nil { - if !errdefs.IsNotFound(err) { - return nil, tagDone(err) - } - - if _, err := e.opt.Images.Create(ctx, img); err != nil { - return nil, tagDone(err) + if _, err := e.opt.Images.Create(ctx, img); err != nil { + return nil, tagDone(err) + } } } tagDone(nil)