From c98cb1e05f74f705f172105d439816eebfc9af82 Mon Sep 17 00:00:00 2001 From: Sean Liao Date: Tue, 15 Oct 2024 23:21:36 +0100 Subject: [PATCH] allow setting annotations This adds a new build flag for setting annotations on image indexes and manifests. Annotations are no longer copied from the base image. `org.opencontainers.image.base.digest` and `org.opencontainers.image.base.name` are always set to the resolved values. Usage example: ```sh ko build --image-annotation foo=bar,fizz=buzz . ``` Fixes #1090 Fixes #1090 Fixes #1231 Fixes #1235 Fixes #1395 --- docs/reference/ko_apply.md | 45 ++++++++++++++++++----------------- docs/reference/ko_build.md | 39 +++++++++++++++--------------- docs/reference/ko_create.md | 45 ++++++++++++++++++----------------- docs/reference/ko_resolve.md | 45 ++++++++++++++++++----------------- docs/reference/ko_run.md | 39 +++++++++++++++--------------- pkg/build/gobuild.go | 40 ++++++++++++++++++++----------- pkg/build/gobuild_test.go | 23 ++++++++++++++++++ pkg/build/options.go | 11 +++++++++ pkg/commands/options/build.go | 5 +++- pkg/commands/resolver.go | 7 ++++++ 10 files changed, 180 insertions(+), 119 deletions(-) diff --git a/docs/reference/ko_apply.md b/docs/reference/ko_apply.md index bde260f9..99322e49 100644 --- a/docs/reference/ko_apply.md +++ b/docs/reference/ko_apply.md @@ -45,28 +45,29 @@ ko apply -f FILENAME [flags] ### Options ``` - --bare Whether to just use KO_DOCKER_REPO without additional context (may not work properly with --tags). - -B, --base-import-paths Whether to use the base path without MD5 hash after KO_DOCKER_REPO (may not work properly with --tags). - --debug Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000. - --disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container. - -f, --filename strings Filename, directory, or URL to files to use to create the resource - -h, --help help for apply - --image-label strings Which labels (key=value) to add to the image. - --image-refs string Path to file where a list of the published image references will be written. - --insecure-registry Whether to skip TLS verification on the registry - -j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS) - -L, --local Load into images to local docker daemon. - --oci-layout-path string Path to save the OCI image layout of the built images - --platform strings Which platform to use when pulling a multi-platform base. Format: all | [/[/]][,platform]* - -P, --preserve-import-paths Whether to preserve the full import path after KO_DOCKER_REPO. - --push Push images to KO_DOCKER_REPO (default true) - -R, --recursive Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory. - --sbom string The SBOM media type to use (none will disable SBOM synthesis and upload). (default "spdx") - --sbom-dir string Path to file where the SBOM will be written. - -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2) - --tag-only Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated. - -t, --tags strings Which tags to use for the produced image instead of the default 'latest' tag (may not work properly with --base-import-paths or --bare). (default [latest]) - --tarball string File to save images tarballs + --bare Whether to just use KO_DOCKER_REPO without additional context (may not work properly with --tags). + -B, --base-import-paths Whether to use the base path without MD5 hash after KO_DOCKER_REPO (may not work properly with --tags). + --debug Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000. + --disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container. + -f, --filename strings Filename, directory, or URL to files to use to create the resource + -h, --help help for apply + --image-annotation strings Which annotations (key=value[,key=value]) to add to the OCI manifest. + --image-label strings Which labels (key=value[,key=value]) to add to the image. + --image-refs string Path to file where a list of the published image references will be written. + --insecure-registry Whether to skip TLS verification on the registry + -j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS) + -L, --local Load into images to local docker daemon. + --oci-layout-path string Path to save the OCI image layout of the built images + --platform strings Which platform to use when pulling a multi-platform base. Format: all | [/[/]][,platform]* + -P, --preserve-import-paths Whether to preserve the full import path after KO_DOCKER_REPO. + --push Push images to KO_DOCKER_REPO (default true) + -R, --recursive Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory. + --sbom string The SBOM media type to use (none will disable SBOM synthesis and upload). (default "spdx") + --sbom-dir string Path to file where the SBOM will be written. + -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2) + --tag-only Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated. + -t, --tags strings Which tags to use for the produced image instead of the default 'latest' tag (may not work properly with --base-import-paths or --bare). (default [latest]) + --tarball string File to save images tarballs ``` ### Options inherited from parent commands diff --git a/docs/reference/ko_build.md b/docs/reference/ko_build.md index b27c2b1c..b1df64cf 100644 --- a/docs/reference/ko_build.md +++ b/docs/reference/ko_build.md @@ -42,25 +42,26 @@ ko build IMPORTPATH... [flags] ### Options ``` - --bare Whether to just use KO_DOCKER_REPO without additional context (may not work properly with --tags). - -B, --base-import-paths Whether to use the base path without MD5 hash after KO_DOCKER_REPO (may not work properly with --tags). - --debug Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000. - --disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container. - -h, --help help for build - --image-label strings Which labels (key=value) to add to the image. - --image-refs string Path to file where a list of the published image references will be written. - --insecure-registry Whether to skip TLS verification on the registry - -j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS) - -L, --local Load into images to local docker daemon. - --oci-layout-path string Path to save the OCI image layout of the built images - --platform strings Which platform to use when pulling a multi-platform base. Format: all | [/[/]][,platform]* - -P, --preserve-import-paths Whether to preserve the full import path after KO_DOCKER_REPO. - --push Push images to KO_DOCKER_REPO (default true) - --sbom string The SBOM media type to use (none will disable SBOM synthesis and upload). (default "spdx") - --sbom-dir string Path to file where the SBOM will be written. - --tag-only Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated. - -t, --tags strings Which tags to use for the produced image instead of the default 'latest' tag (may not work properly with --base-import-paths or --bare). (default [latest]) - --tarball string File to save images tarballs + --bare Whether to just use KO_DOCKER_REPO without additional context (may not work properly with --tags). + -B, --base-import-paths Whether to use the base path without MD5 hash after KO_DOCKER_REPO (may not work properly with --tags). + --debug Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000. + --disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container. + -h, --help help for build + --image-annotation strings Which annotations (key=value[,key=value]) to add to the OCI manifest. + --image-label strings Which labels (key=value[,key=value]) to add to the image. + --image-refs string Path to file where a list of the published image references will be written. + --insecure-registry Whether to skip TLS verification on the registry + -j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS) + -L, --local Load into images to local docker daemon. + --oci-layout-path string Path to save the OCI image layout of the built images + --platform strings Which platform to use when pulling a multi-platform base. Format: all | [/[/]][,platform]* + -P, --preserve-import-paths Whether to preserve the full import path after KO_DOCKER_REPO. + --push Push images to KO_DOCKER_REPO (default true) + --sbom string The SBOM media type to use (none will disable SBOM synthesis and upload). (default "spdx") + --sbom-dir string Path to file where the SBOM will be written. + --tag-only Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated. + -t, --tags strings Which tags to use for the produced image instead of the default 'latest' tag (may not work properly with --base-import-paths or --bare). (default [latest]) + --tarball string File to save images tarballs ``` ### Options inherited from parent commands diff --git a/docs/reference/ko_create.md b/docs/reference/ko_create.md index aa176b75..232f38a0 100644 --- a/docs/reference/ko_create.md +++ b/docs/reference/ko_create.md @@ -45,28 +45,29 @@ ko create -f FILENAME [flags] ### Options ``` - --bare Whether to just use KO_DOCKER_REPO without additional context (may not work properly with --tags). - -B, --base-import-paths Whether to use the base path without MD5 hash after KO_DOCKER_REPO (may not work properly with --tags). - --debug Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000. - --disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container. - -f, --filename strings Filename, directory, or URL to files to use to create the resource - -h, --help help for create - --image-label strings Which labels (key=value) to add to the image. - --image-refs string Path to file where a list of the published image references will be written. - --insecure-registry Whether to skip TLS verification on the registry - -j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS) - -L, --local Load into images to local docker daemon. - --oci-layout-path string Path to save the OCI image layout of the built images - --platform strings Which platform to use when pulling a multi-platform base. Format: all | [/[/]][,platform]* - -P, --preserve-import-paths Whether to preserve the full import path after KO_DOCKER_REPO. - --push Push images to KO_DOCKER_REPO (default true) - -R, --recursive Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory. - --sbom string The SBOM media type to use (none will disable SBOM synthesis and upload). (default "spdx") - --sbom-dir string Path to file where the SBOM will be written. - -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2) - --tag-only Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated. - -t, --tags strings Which tags to use for the produced image instead of the default 'latest' tag (may not work properly with --base-import-paths or --bare). (default [latest]) - --tarball string File to save images tarballs + --bare Whether to just use KO_DOCKER_REPO without additional context (may not work properly with --tags). + -B, --base-import-paths Whether to use the base path without MD5 hash after KO_DOCKER_REPO (may not work properly with --tags). + --debug Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000. + --disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container. + -f, --filename strings Filename, directory, or URL to files to use to create the resource + -h, --help help for create + --image-annotation strings Which annotations (key=value[,key=value]) to add to the OCI manifest. + --image-label strings Which labels (key=value[,key=value]) to add to the image. + --image-refs string Path to file where a list of the published image references will be written. + --insecure-registry Whether to skip TLS verification on the registry + -j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS) + -L, --local Load into images to local docker daemon. + --oci-layout-path string Path to save the OCI image layout of the built images + --platform strings Which platform to use when pulling a multi-platform base. Format: all | [/[/]][,platform]* + -P, --preserve-import-paths Whether to preserve the full import path after KO_DOCKER_REPO. + --push Push images to KO_DOCKER_REPO (default true) + -R, --recursive Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory. + --sbom string The SBOM media type to use (none will disable SBOM synthesis and upload). (default "spdx") + --sbom-dir string Path to file where the SBOM will be written. + -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2) + --tag-only Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated. + -t, --tags strings Which tags to use for the produced image instead of the default 'latest' tag (may not work properly with --base-import-paths or --bare). (default [latest]) + --tarball string File to save images tarballs ``` ### Options inherited from parent commands diff --git a/docs/reference/ko_resolve.md b/docs/reference/ko_resolve.md index 08ec7b3f..1f28e636 100644 --- a/docs/reference/ko_resolve.md +++ b/docs/reference/ko_resolve.md @@ -38,28 +38,29 @@ ko resolve -f FILENAME [flags] ### Options ``` - --bare Whether to just use KO_DOCKER_REPO without additional context (may not work properly with --tags). - -B, --base-import-paths Whether to use the base path without MD5 hash after KO_DOCKER_REPO (may not work properly with --tags). - --debug Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000. - --disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container. - -f, --filename strings Filename, directory, or URL to files to use to create the resource - -h, --help help for resolve - --image-label strings Which labels (key=value) to add to the image. - --image-refs string Path to file where a list of the published image references will be written. - --insecure-registry Whether to skip TLS verification on the registry - -j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS) - -L, --local Load into images to local docker daemon. - --oci-layout-path string Path to save the OCI image layout of the built images - --platform strings Which platform to use when pulling a multi-platform base. Format: all | [/[/]][,platform]* - -P, --preserve-import-paths Whether to preserve the full import path after KO_DOCKER_REPO. - --push Push images to KO_DOCKER_REPO (default true) - -R, --recursive Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory. - --sbom string The SBOM media type to use (none will disable SBOM synthesis and upload). (default "spdx") - --sbom-dir string Path to file where the SBOM will be written. - -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2) - --tag-only Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated. - -t, --tags strings Which tags to use for the produced image instead of the default 'latest' tag (may not work properly with --base-import-paths or --bare). (default [latest]) - --tarball string File to save images tarballs + --bare Whether to just use KO_DOCKER_REPO without additional context (may not work properly with --tags). + -B, --base-import-paths Whether to use the base path without MD5 hash after KO_DOCKER_REPO (may not work properly with --tags). + --debug Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000. + --disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container. + -f, --filename strings Filename, directory, or URL to files to use to create the resource + -h, --help help for resolve + --image-annotation strings Which annotations (key=value[,key=value]) to add to the OCI manifest. + --image-label strings Which labels (key=value[,key=value]) to add to the image. + --image-refs string Path to file where a list of the published image references will be written. + --insecure-registry Whether to skip TLS verification on the registry + -j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS) + -L, --local Load into images to local docker daemon. + --oci-layout-path string Path to save the OCI image layout of the built images + --platform strings Which platform to use when pulling a multi-platform base. Format: all | [/[/]][,platform]* + -P, --preserve-import-paths Whether to preserve the full import path after KO_DOCKER_REPO. + --push Push images to KO_DOCKER_REPO (default true) + -R, --recursive Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory. + --sbom string The SBOM media type to use (none will disable SBOM synthesis and upload). (default "spdx") + --sbom-dir string Path to file where the SBOM will be written. + -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2) + --tag-only Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated. + -t, --tags strings Which tags to use for the produced image instead of the default 'latest' tag (may not work properly with --base-import-paths or --bare). (default [latest]) + --tarball string File to save images tarballs ``` ### Options inherited from parent commands diff --git a/docs/reference/ko_run.md b/docs/reference/ko_run.md index aa28c5e8..1ae8dcf4 100644 --- a/docs/reference/ko_run.md +++ b/docs/reference/ko_run.md @@ -30,25 +30,26 @@ ko run IMPORTPATH [flags] ### Options ``` - --bare Whether to just use KO_DOCKER_REPO without additional context (may not work properly with --tags). - -B, --base-import-paths Whether to use the base path without MD5 hash after KO_DOCKER_REPO (may not work properly with --tags). - --debug Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000. - --disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container. - -h, --help help for run - --image-label strings Which labels (key=value) to add to the image. - --image-refs string Path to file where a list of the published image references will be written. - --insecure-registry Whether to skip TLS verification on the registry - -j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS) - -L, --local Load into images to local docker daemon. - --oci-layout-path string Path to save the OCI image layout of the built images - --platform strings Which platform to use when pulling a multi-platform base. Format: all | [/[/]][,platform]* - -P, --preserve-import-paths Whether to preserve the full import path after KO_DOCKER_REPO. - --push Push images to KO_DOCKER_REPO (default true) - --sbom string The SBOM media type to use (none will disable SBOM synthesis and upload). (default "spdx") - --sbom-dir string Path to file where the SBOM will be written. - --tag-only Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated. - -t, --tags strings Which tags to use for the produced image instead of the default 'latest' tag (may not work properly with --base-import-paths or --bare). (default [latest]) - --tarball string File to save images tarballs + --bare Whether to just use KO_DOCKER_REPO without additional context (may not work properly with --tags). + -B, --base-import-paths Whether to use the base path without MD5 hash after KO_DOCKER_REPO (may not work properly with --tags). + --debug Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000. + --disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container. + -h, --help help for run + --image-annotation strings Which annotations (key=value[,key=value]) to add to the OCI manifest. + --image-label strings Which labels (key=value[,key=value]) to add to the image. + --image-refs string Path to file where a list of the published image references will be written. + --insecure-registry Whether to skip TLS verification on the registry + -j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS) + -L, --local Load into images to local docker daemon. + --oci-layout-path string Path to save the OCI image layout of the built images + --platform strings Which platform to use when pulling a multi-platform base. Format: all | [/[/]][,platform]* + -P, --preserve-import-paths Whether to preserve the full import path after KO_DOCKER_REPO. + --push Push images to KO_DOCKER_REPO (default true) + --sbom string The SBOM media type to use (none will disable SBOM synthesis and upload). (default "spdx") + --sbom-dir string Path to file where the SBOM will be written. + --tag-only Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated. + -t, --tags strings Which tags to use for the produced image instead of the default 'latest' tag (may not work properly with --base-import-paths or --bare). (default [latest]) + --tarball string File to save images tarballs ``` ### Options inherited from parent commands diff --git a/pkg/build/gobuild.go b/pkg/build/gobuild.go index 7fd75f63..05a7a81e 100644 --- a/pkg/build/gobuild.go +++ b/pkg/build/gobuild.go @@ -24,6 +24,7 @@ import ( gb "go/build" "io" "log" + "maps" "os" "os/exec" "path" @@ -101,6 +102,7 @@ type gobuild struct { platformMatcher *platformMatcher dir string labels map[string]string + annotations map[string]string debug bool semaphore *semaphore.Weighted @@ -126,6 +128,7 @@ type gobuildOpener struct { defaultLdflags []string platforms []string labels map[string]string + annotations map[string]string dir string jobs int debug bool @@ -142,6 +145,9 @@ func (gbo *gobuildOpener) Open() (Interface, error) { if gbo.jobs == 0 { gbo.jobs = runtime.GOMAXPROCS(0) } + if gbo.annotations == nil { + gbo.annotations = map[string]string{} + } return &gobuild{ ctx: gbo.ctx, getBase: gbo.getBase, @@ -157,6 +163,7 @@ func (gbo *gobuildOpener) Open() (Interface, error) { defaultFlags: gbo.defaultFlags, defaultLdflags: gbo.defaultLdflags, labels: gbo.labels, + annotations: gbo.annotations, dir: gbo.dir, debug: gbo.debug, platformMatcher: matcher, @@ -1259,11 +1266,10 @@ func (g *gobuild) Build(ctx context.Context, s string) (Result, error) { return nil, err } - anns := map[string]string{ - specsv1.AnnotationBaseImageDigest: baseDigest.String(), - specsv1.AnnotationBaseImageName: baseRef.Name(), - } - base = mutate.Annotations(base, anns).(Result) + annotations := maps.Clone(g.annotations) + annotations[specsv1.AnnotationBaseImageDigest] = baseDigest.String() + annotations[specsv1.AnnotationBaseImageName] = baseRef.Name() + base = mutate.Annotations(base, annotations).(Result) } switch mt { @@ -1311,15 +1317,20 @@ func (g *gobuild) buildAll(ctx context.Context, ref string, baseRef name.Referen if err != nil { return nil, fmt.Errorf("error getting matching image from index: %w", err) } + + annotations := maps.Clone(g.annotations) // Decorate the image with the ref of the index, and the matching // platform's digest. - img = mutate.Annotations(img, map[string]string{ - specsv1.AnnotationBaseImageDigest: matches[0].Digest.String(), - specsv1.AnnotationBaseImageName: baseRef.Name(), - }).(v1.Image) + annotations[specsv1.AnnotationBaseImageDigest] = matches[0].Digest.String() + annotations[specsv1.AnnotationBaseImageName] = baseRef.Name() + img = mutate.Annotations(img, annotations).(v1.Image) return g.buildOne(ctx, ref, img, matches[0].Platform) } + g.annotations[specsv1.AnnotationBaseImageName] = baseRef.Name() + baseDigest, _ := baseIndex.Digest() + g.annotations[specsv1.AnnotationBaseImageDigest] = baseDigest.String() + // Build an image for each matching platform from the base and append // it to a new index to produce the result. We use the indices to // preserve the base image ordering here. @@ -1333,6 +1344,7 @@ func (g *gobuild) buildAll(ctx context.Context, ref string, baseRef name.Referen return err } + annotations := maps.Clone(g.annotations) // Decorate the image with the ref of the index, and the matching // platform's digest. The ref of the index encodes the critical // repository information for fetching the base image's digest, but @@ -1350,10 +1362,10 @@ func (g *gobuild) buildAll(ctx context.Context, ref string, baseRef name.Referen // no-ops for us because we didn't record the digest of the actual // image we used, and we would potentially end up doing Nx more work // than we really need to do. - baseImage = mutate.Annotations(baseImage, map[string]string{ - specsv1.AnnotationBaseImageDigest: desc.Digest.String(), - specsv1.AnnotationBaseImageName: baseRef.Name(), - }).(v1.Image) + annotations[specsv1.AnnotationBaseImageDigest] = desc.Digest.String() + annotations[specsv1.AnnotationBaseImageName] = baseRef.Name() + + baseImage = mutate.Annotations(baseImage, annotations).(v1.Image) img, err := g.buildOne(gctx, ref, baseImage, desc.Platform) if err != nil { @@ -1383,7 +1395,7 @@ func (g *gobuild) buildAll(ctx context.Context, ref string, baseRef name.Referen idx := ocimutate.AppendManifests( mutate.Annotations( mutate.IndexMediaType(empty.Index, baseType), - im.Annotations).(v1.ImageIndex), + g.annotations).(v1.ImageIndex), adds...) if g.sbom != nil { diff --git a/pkg/build/gobuild_test.go b/pkg/build/gobuild_test.go index 1211e928..5de8ee0a 100644 --- a/pkg/build/gobuild_test.go +++ b/pkg/build/gobuild_test.go @@ -844,6 +844,8 @@ func TestGoBuild(t *testing.T) { withSBOMber(fauxSBOM), WithLabel("foo", "bar"), WithLabel("hello", "world"), + WithAnnotation("fizz", "buzz"), + WithAnnotation("goodbye", "world"), WithPlatforms("all"), ) if err != nil { @@ -898,6 +900,27 @@ func TestGoBuild(t *testing.T) { t.Fatalf("Labels diff (-got,+want): %s", d) } }) + + t.Run("check annotations", func(t *testing.T) { + baseDigest, err := base.Digest() + if err != nil { + t.Fatalf("base.Digest() = %v", err) + } + man, err := img.Manifest() + if err != nil { + t.Fatalf("Manifest() = %v", err) + } + want := map[string]string{ + specsv1.AnnotationBaseImageName: baseRef.Name(), + specsv1.AnnotationBaseImageDigest: baseDigest.String(), + "fizz": "buzz", + "goodbye": "world", + } + got := man.Annotations + if d := cmp.Diff(got, want); d != "" { + t.Fatalf("Annotations diff (-got,+want): %s", d) + } + }) } func TestGoBuild_Defaults(t *testing.T) { diff --git a/pkg/build/options.go b/pkg/build/options.go index d773badc..19601a8f 100644 --- a/pkg/build/options.go +++ b/pkg/build/options.go @@ -142,6 +142,17 @@ func WithLabel(k, v string) Option { } } +// WithAnnotation is a functional option for adding annotations to built manifests +func WithAnnotation(k, v string) Option { + return func(gbo *gobuildOpener) error { + if gbo.annotations == nil { + gbo.annotations = map[string]string{} + } + gbo.annotations[k] = v + return nil + } +} + // withBuilder is a functional option for overriding the way go binaries // are built. func withBuilder(b builder) Option { diff --git a/pkg/commands/options/build.go b/pkg/commands/options/build.go index b6992938..0ec633d4 100644 --- a/pkg/commands/options/build.go +++ b/pkg/commands/options/build.go @@ -65,6 +65,7 @@ type BuildOptions struct { SBOMDir string Platforms []string Labels []string + Annotations []string Debug bool // UserAgent enables overriding the default value of the `User-Agent` HTTP // request header used when retrieving the base image. @@ -94,7 +95,9 @@ func AddBuildOptions(cmd *cobra.Command, bo *BuildOptions) { cmd.Flags().StringSliceVar(&bo.Platforms, "platform", []string{}, "Which platform to use when pulling a multi-platform base. Format: all | [/[/]][,platform]*") cmd.Flags().StringSliceVar(&bo.Labels, "image-label", []string{}, - "Which labels (key=value) to add to the image.") + "Which labels (key=value[,key=value]) to add to the image.") + cmd.Flags().StringSliceVar(&bo.Annotations, "image-annotation", []string{}, + "Which annotations (key=value[,key=value]) to add to the OCI manifest.") cmd.Flags().BoolVar(&bo.Debug, "debug", bo.Debug, "Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000.") bo.Trimpath = true diff --git a/pkg/commands/resolver.go b/pkg/commands/resolver.go index c55b961c..e8ea3414 100644 --- a/pkg/commands/resolver.go +++ b/pkg/commands/resolver.go @@ -119,6 +119,13 @@ func gobuildOptions(bo *options.BuildOptions) ([]build.Option, error) { } opts = append(opts, build.WithLabel(parts[0], parts[1])) } + for _, an := range bo.Annotations { + k, v, ok := strings.Cut(an, "=") + if !ok { + return nil, fmt.Errorf("missing '=' in annotation: %s", an) + } + opts = append(opts, build.WithAnnotation(k, v)) + } if bo.BuildConfigs != nil { opts = append(opts, build.WithConfig(bo.BuildConfigs))