From 2a9ca61f35f63f8647599bd47edb947751b1d61c Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Thu, 17 Jul 2025 17:46:28 +0200 Subject: [PATCH] introduce provenance and sbom in build section Signed-off-by: Nicolas De Loof --- loader/loader_test.go | 16 ++++++++++++++++ schema/compose-spec.json | 2 ++ transform/canonical.go | 13 +++++++++++++ types/derived.gen.go | 2 ++ types/types.go | 2 ++ 5 files changed, 35 insertions(+) diff --git a/loader/loader_test.go b/loader/loader_test.go index 548f464b..3262869d 100644 --- a/loader/loader_test.go +++ b/loader/loader_test.go @@ -3900,3 +3900,19 @@ models: }, }) } + +func TestAttestations(t *testing.T) { + p, err := loadYAML(` +name: attestations +services: + test: + build: + context: . + provenance: mode=max + sbom: true +`) + assert.NilError(t, err) + build := p.Services["test"].Build + assert.Equal(t, build.Provenance, "mode=max") + assert.Equal(t, build.SBOM, "true") +} diff --git a/schema/compose-spec.json b/schema/compose-spec.json index ffe60a11..606993ef 100644 --- a/schema/compose-spec.json +++ b/schema/compose-spec.json @@ -123,6 +123,8 @@ "no_cache": {"type": ["boolean", "string"], "description": "Do not use cache when building the image."}, "additional_contexts": {"$ref": "#/definitions/list_or_dict", "description": "Additional build contexts to use, specified as a map of name to context path or URL."}, "network": {"type": "string", "description": "Network mode to use for the build. Options include 'default', 'none', 'host', or a network name."}, + "provenance": {"type": ["string","boolean"], "description": "Add a provenance attestation"}, + "sbom": {"type": ["string","boolean"], "description": "Add a SBOM attestation"}, "pull": {"type": ["boolean", "string"], "description": "Always attempt to pull a newer version of the image."}, "target": {"type": "string", "description": "Build stage to target in a multi-stage Dockerfile."}, "shm_size": {"type": ["integer", "string"], "description": "Size of /dev/shm for the build container. A string value can use suffix like '2g' for 2 gigabytes."}, diff --git a/transform/canonical.go b/transform/canonical.go index 2754ed3f..44484954 100644 --- a/transform/canonical.go +++ b/transform/canonical.go @@ -17,6 +17,8 @@ package transform import ( + "fmt" + "github.com/compose-spec/compose-go/v2/tree" ) @@ -27,6 +29,8 @@ var transformers = map[tree.Path]transformFunc{} func init() { transformers["services.*"] = transformService transformers["services.*.build.secrets.*"] = transformFileMount + transformers["services.*.build.provenance"] = transformStringOrX + transformers["services.*.build.sbom"] = transformStringOrX transformers["services.*.build.additional_contexts"] = transformKeyValue transformers["services.*.depends_on"] = transformDependsOn transformers["services.*.env_file"] = transformEnvFile @@ -121,3 +125,12 @@ func transformMapping(v map[string]any, p tree.Path, ignoreParseError bool) (map } return v, nil } + +func transformStringOrX(data any, _ tree.Path, _ bool) (any, error) { + switch v := data.(type) { + case string: + return v, nil + default: + return fmt.Sprint(v), nil + } +} diff --git a/types/derived.gen.go b/types/derived.gen.go index 93adf490..02251067 100644 --- a/types/derived.gen.go +++ b/types/derived.gen.go @@ -875,6 +875,8 @@ func deriveDeepCopy_6(dst, src *BuildConfig) { } else { dst.Args = nil } + dst.Provenance = src.Provenance + dst.SBOM = src.SBOM if src.SSH == nil { dst.SSH = nil } else { diff --git a/types/types.go b/types/types.go index e93d493e..d3f4d457 100644 --- a/types/types.go +++ b/types/types.go @@ -309,6 +309,8 @@ type BuildConfig struct { DockerfileInline string `yaml:"dockerfile_inline,omitempty" json:"dockerfile_inline,omitempty"` Entitlements []string `yaml:"entitlements,omitempty" json:"entitlements,omitempty"` Args MappingWithEquals `yaml:"args,omitempty" json:"args,omitempty"` + Provenance string `yaml:"provenance,omitempty" json:"provenance,omitempty"` + SBOM string `yaml:"sbom,omitempty" json:"sbom,omitempty"` SSH SSHConfig `yaml:"ssh,omitempty" json:"ssh,omitempty"` Labels Labels `yaml:"labels,omitempty" json:"labels,omitempty"` CacheFrom StringList `yaml:"cache_from,omitempty" json:"cache_from,omitempty"`