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
69 changes: 43 additions & 26 deletions bake/bake.go
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
// IMPORTANT: if you add more fields here, do not forget to update
// docs/reference/buildx_bake.md (--set) and https://docs.docker.com/build/bake/overrides/
switch keys[1] {
case "output", "cache-to", "cache-from", "tags", "platform", "secrets", "ssh", "attest", "entitlements", "network", "annotations":
case "output", "cache-to", "cache-from", "tags", "platform", "secrets", "ssh", "attest", "entitlements", "network", "annotations", "policy":
if len(parts) == 2 {
override.Append = appendTo
override.ArrValue = append(override.ArrValue, parts[1])
Expand Down Expand Up @@ -732,31 +732,32 @@ type Target struct {
// Inherits is the only field that cannot be overridden with --set
Inherits []string `json:"inherits,omitempty" hcl:"inherits,optional" cty:"inherits"`

Annotations []string `json:"annotations,omitempty" hcl:"annotations,optional" cty:"annotations"`
Attest buildflags.Attests `json:"attest,omitempty" hcl:"attest,optional" cty:"attest"`
Context *string `json:"context,omitempty" hcl:"context,optional" cty:"context"`
Contexts map[string]string `json:"contexts,omitempty" hcl:"contexts,optional" cty:"contexts"`
Dockerfile *string `json:"dockerfile,omitempty" hcl:"dockerfile,optional" cty:"dockerfile"`
DockerfileInline *string `json:"dockerfile-inline,omitempty" hcl:"dockerfile-inline,optional" cty:"dockerfile-inline"`
Args map[string]*string `json:"args,omitempty" hcl:"args,optional" cty:"args"`
Labels map[string]*string `json:"labels,omitempty" hcl:"labels,optional" cty:"labels"`
Tags []string `json:"tags,omitempty" hcl:"tags,optional" cty:"tags"`
CacheFrom buildflags.CacheOptions `json:"cache-from,omitempty" hcl:"cache-from,optional" cty:"cache-from"`
CacheTo buildflags.CacheOptions `json:"cache-to,omitempty" hcl:"cache-to,optional" cty:"cache-to"`
Target *string `json:"target,omitempty" hcl:"target,optional" cty:"target"`
Secrets buildflags.Secrets `json:"secret,omitempty" hcl:"secret,optional" cty:"secret"`
SSH buildflags.SSHKeys `json:"ssh,omitempty" hcl:"ssh,optional" cty:"ssh"`
Platforms []string `json:"platforms,omitempty" hcl:"platforms,optional" cty:"platforms"`
Outputs buildflags.Exports `json:"output,omitempty" hcl:"output,optional" cty:"output"`
Pull *bool `json:"pull,omitempty" hcl:"pull,optional" cty:"pull"`
NoCache *bool `json:"no-cache,omitempty" hcl:"no-cache,optional" cty:"no-cache"`
NetworkMode *string `json:"network,omitempty" hcl:"network,optional" cty:"network"`
NoCacheFilter []string `json:"no-cache-filter,omitempty" hcl:"no-cache-filter,optional" cty:"no-cache-filter"`
ShmSize *string `json:"shm-size,omitempty" hcl:"shm-size,optional" cty:"shm-size"`
Ulimits []string `json:"ulimits,omitempty" hcl:"ulimits,optional" cty:"ulimits"`
Call *string `json:"call,omitempty" hcl:"call,optional" cty:"call"`
Entitlements []string `json:"entitlements,omitempty" hcl:"entitlements,optional" cty:"entitlements"`
ExtraHosts map[string]*string `json:"extra-hosts,omitempty" hcl:"extra-hosts,optional" cty:"extra-hosts"`
Annotations []string `json:"annotations,omitempty" hcl:"annotations,optional" cty:"annotations"`
Attest buildflags.Attests `json:"attest,omitempty" hcl:"attest,optional" cty:"attest"`
Context *string `json:"context,omitempty" hcl:"context,optional" cty:"context"`
Contexts map[string]string `json:"contexts,omitempty" hcl:"contexts,optional" cty:"contexts"`
Dockerfile *string `json:"dockerfile,omitempty" hcl:"dockerfile,optional" cty:"dockerfile"`
DockerfileInline *string `json:"dockerfile-inline,omitempty" hcl:"dockerfile-inline,optional" cty:"dockerfile-inline"`
Args map[string]*string `json:"args,omitempty" hcl:"args,optional" cty:"args"`
Labels map[string]*string `json:"labels,omitempty" hcl:"labels,optional" cty:"labels"`
Tags []string `json:"tags,omitempty" hcl:"tags,optional" cty:"tags"`
CacheFrom buildflags.CacheOptions `json:"cache-from,omitempty" hcl:"cache-from,optional" cty:"cache-from"`
CacheTo buildflags.CacheOptions `json:"cache-to,omitempty" hcl:"cache-to,optional" cty:"cache-to"`
Target *string `json:"target,omitempty" hcl:"target,optional" cty:"target"`
Secrets buildflags.Secrets `json:"secret,omitempty" hcl:"secret,optional" cty:"secret"`
SSH buildflags.SSHKeys `json:"ssh,omitempty" hcl:"ssh,optional" cty:"ssh"`
Platforms []string `json:"platforms,omitempty" hcl:"platforms,optional" cty:"platforms"`
Outputs buildflags.Exports `json:"output,omitempty" hcl:"output,optional" cty:"output"`
Pull *bool `json:"pull,omitempty" hcl:"pull,optional" cty:"pull"`
NoCache *bool `json:"no-cache,omitempty" hcl:"no-cache,optional" cty:"no-cache"`
NetworkMode *string `json:"network,omitempty" hcl:"network,optional" cty:"network"`
NoCacheFilter []string `json:"no-cache-filter,omitempty" hcl:"no-cache-filter,optional" cty:"no-cache-filter"`
ShmSize *string `json:"shm-size,omitempty" hcl:"shm-size,optional" cty:"shm-size"`
Ulimits []string `json:"ulimits,omitempty" hcl:"ulimits,optional" cty:"ulimits"`
Call *string `json:"call,omitempty" hcl:"call,optional" cty:"call"`
Entitlements []string `json:"entitlements,omitempty" hcl:"entitlements,optional" cty:"entitlements"`
ExtraHosts map[string]*string `json:"extra-hosts,omitempty" hcl:"extra-hosts,optional" cty:"extra-hosts"`
Policy buildflags.PolicyConfigs `json:"policy,omitempty" hcl:"policy,optional" cty:"policy"`
// IMPORTANT: if you add more fields here, do not forget to update newOverrides/AddOverrides and docs/bake-reference.md.

// linked is a private field to mark a target used as a linked one
Expand Down Expand Up @@ -891,6 +892,9 @@ func (t *Target) Merge(t2 *Target) {
if t2.Attest != nil { // merge
t.Attest = t.Attest.Merge(t2.Attest)
}
if t2.Policy != nil { // merge
t.Policy = append(t.Policy, t2.Policy...)
}
if t2.Secrets != nil { // merge
t.Secrets = t.Secrets.Merge(t2.Secrets)
}
Expand Down Expand Up @@ -986,6 +990,17 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
} else {
t.Tags = o.ArrValue
}
case "policy":
if !o.Append {
t.Policy = nil
}
for _, v := range o.ArrValue {
cfg, err := buildflags.ParsePolicyConfig(v)
if err != nil {
return err
}
t.Policy = append(t.Policy, cfg)
}
case "cache-from":
cacheFrom, err := buildflags.ParseCacheEntry(o.ArrValue)
if err != nil {
Expand Down Expand Up @@ -1548,6 +1563,8 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {

bo.Attests = t.Attest.ToMap()

bo.Policy = []buildflags.PolicyConfig(t.Policy)

bo.SourcePolicy, err = build.ReadSourcePolicy()
if err != nil {
return nil, err
Expand Down
14 changes: 3 additions & 11 deletions build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,7 @@ type Options struct {
SourcePolicy *spb.Policy
GroupRef string
Annotations map[exptypes.AnnotationKey]string // Not used during build, annotations are already set in Exports. Just used to check for support with drivers.
Policy []PolicyConfig
}

type PolicyConfig struct {
Files []policy.File
Reset bool
Disabled bool
Strict *bool
LogLevel *logrus.Level
Policy []buildflags.PolicyConfig
}

type CallFunc struct {
Expand Down Expand Up @@ -135,7 +127,7 @@ type policyOpt struct {
LogLevel *logrus.Level
}

func withPolicyConfig(defaultPolicy policyOpt, configs []PolicyConfig) ([]policyOpt, error) {
func withPolicyConfig(defaultPolicy policyOpt, configs []buildflags.PolicyConfig) ([]policyOpt, error) {
if len(configs) == 0 {
if len(defaultPolicy.Files) == 0 {
return nil, nil
Expand All @@ -161,7 +153,7 @@ func withPolicyConfig(defaultPolicy policyOpt, configs []PolicyConfig) ([]policy
out = append(out, defaultPolicy)
}

var last PolicyConfig
var last buildflags.PolicyConfig

for _, cfg := range configs {
if cfg.Reset {
Expand Down
21 changes: 11 additions & 10 deletions build/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"

"github.com/docker/buildx/policy"
"github.com/docker/buildx/util/buildflags"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -39,33 +40,33 @@ func TestWithPolicyConfigDefaults(t *testing.T) {

// TestWithPolicyConfigDisabled validates disabled policy behavior across invalid and valid combinations.
func TestWithPolicyConfigDisabled(t *testing.T) {
_, err := withPolicyConfig(policyOpt{}, []PolicyConfig{
_, err := withPolicyConfig(policyOpt{}, []buildflags.PolicyConfig{
{Disabled: true, Files: []policy.File{{Filename: "x.rego"}}},
})
require.Error(t, err)

_, err = withPolicyConfig(policyOpt{}, []PolicyConfig{
_, err = withPolicyConfig(policyOpt{}, []buildflags.PolicyConfig{
{Disabled: true, Reset: true},
})
require.Error(t, err)

_, err = withPolicyConfig(policyOpt{}, []PolicyConfig{
_, err = withPolicyConfig(policyOpt{}, []buildflags.PolicyConfig{
{Disabled: true, Strict: boolPtr(true)},
})
require.Error(t, err)

_, err = withPolicyConfig(policyOpt{}, []PolicyConfig{
_, err = withPolicyConfig(policyOpt{}, []buildflags.PolicyConfig{
{Disabled: true, LogLevel: levelPtr(logrus.WarnLevel)},
})
require.Error(t, err)

_, err = withPolicyConfig(policyOpt{}, []PolicyConfig{
_, err = withPolicyConfig(policyOpt{}, []buildflags.PolicyConfig{
{Disabled: true},
{},
})
require.Error(t, err)

out, err := withPolicyConfig(policyOpt{}, []PolicyConfig{
out, err := withPolicyConfig(policyOpt{}, []buildflags.PolicyConfig{
{Disabled: true},
})
require.NoError(t, err)
Expand All @@ -81,7 +82,7 @@ func TestWithPolicyConfigResetAndFiles(t *testing.T) {
},
}

out, err := withPolicyConfig(defaultPolicy, []PolicyConfig{
out, err := withPolicyConfig(defaultPolicy, []buildflags.PolicyConfig{
{Reset: true},
{Files: []policy.File{{Filename: "a.rego"}}},
})
Expand All @@ -97,7 +98,7 @@ func TestWithPolicyConfigStrictAndLogLevel(t *testing.T) {
Files: []policy.File{{Filename: "default.rego"}},
}

out, err := withPolicyConfig(defaultPolicy, []PolicyConfig{
out, err := withPolicyConfig(defaultPolicy, []buildflags.PolicyConfig{
{Strict: boolPtr(true), LogLevel: levelPtr(logrus.WarnLevel)},
})
require.NoError(t, err)
Expand All @@ -109,7 +110,7 @@ func TestWithPolicyConfigStrictAndLogLevel(t *testing.T) {

// TestWithPolicyConfigStrictIgnoredWithoutPolicy ensures strict without any policy produces no entries.
func TestWithPolicyConfigStrictIgnoredWithoutPolicy(t *testing.T) {
out, err := withPolicyConfig(policyOpt{}, []PolicyConfig{
out, err := withPolicyConfig(policyOpt{}, []buildflags.PolicyConfig{
{Strict: boolPtr(true)},
})
require.NoError(t, err)
Expand All @@ -125,7 +126,7 @@ func TestWithPolicyConfigMultipleFilesAndOverrides(t *testing.T) {
},
}

out, err := withPolicyConfig(defaultPolicy, []PolicyConfig{
out, err := withPolicyConfig(defaultPolicy, []buildflags.PolicyConfig{
{Files: []policy.File{{Filename: "a.rego"}}},
{Strict: boolPtr(true), LogLevel: levelPtr(logrus.WarnLevel)},
{Files: []policy.File{{Filename: "b.rego"}}, Strict: boolPtr(true)},
Expand Down
68 changes: 2 additions & 66 deletions commands/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"github.com/containerd/console"
"github.com/docker/buildx/build"
"github.com/docker/buildx/builder"
"github.com/docker/buildx/policy"
"github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/buildx/util/buildflags"
Expand Down Expand Up @@ -57,7 +56,6 @@ import (
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/tonistiigi/go-csvvalue"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"google.golang.org/grpc/codes"
Expand Down Expand Up @@ -153,7 +151,7 @@ func (o *buildOptions) toOptions() (*BuildOptions, error) {
return nil, err
}

opts.Policy, err = parsePolicyConfigs(o.policy)
opts.Policy, err = buildflags.ParsePolicyConfigs(o.policy)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -236,68 +234,6 @@ func (o *buildOptions) toDisplayMode() (progressui.DisplayMode, error) {
return progress, nil
}

func parsePolicyConfigs(in []string) ([]build.PolicyConfig, error) {
if len(in) == 0 {
return nil, nil
}

out := make([]build.PolicyConfig, 0, len(in))
for _, s := range in {
fields, err := csvvalue.Fields(s, nil)
if err != nil {
return nil, err
}

cfg := build.PolicyConfig{}
for _, field := range fields {
key, value, ok := strings.Cut(field, "=")
if !ok {
return nil, errors.Errorf("invalid value %s", field)
}
key = strings.TrimSpace(strings.ToLower(key))
switch key {
case "filename":
if value == "" {
return nil, errors.Errorf("invalid value %s", field)
}
dt, err := os.ReadFile(value)
if err != nil {
return nil, errors.Wrapf(err, "failed to read policy file %s", value)
}
cfg.Files = append(cfg.Files, policy.File{Filename: value, Data: dt})
case "reset":
b, err := strconv.ParseBool(value)
if err != nil {
return nil, errors.Wrapf(err, "invalid value %s", field)
}
cfg.Reset = b
case "disabled":
b, err := strconv.ParseBool(value)
if err != nil {
return nil, errors.Wrapf(err, "invalid value %s", field)
}
cfg.Disabled = b
case "strict":
b, err := strconv.ParseBool(value)
if err != nil {
return nil, errors.Wrapf(err, "invalid value %s", field)
}
cfg.Strict = &b
case "log-level":
lvl, err := logrus.ParseLevel(value)
if err != nil {
return nil, errors.Wrapf(err, "invalid value %s", field)
}
cfg.LogLevel = &lvl
default:
return nil, errors.Errorf("invalid value %s", field)
}
}
out = append(out, cfg)
}
return out, nil
}

const (
commandNameAttribute = attribute.Key("command.name")
commandOptionsHash = attribute.Key("command.options.hash")
Expand Down Expand Up @@ -1048,7 +984,7 @@ type BuildOptions struct {
GroupRef string
Annotations []string
ProvenanceResponseMode string
Policy []build.PolicyConfig
Policy []buildflags.PolicyConfig
}

// RunBuild runs the specified build and returns the result.
Expand Down
3 changes: 2 additions & 1 deletion commands/policy/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"slices"
"strings"

"github.com/containerd/errdefs"
"github.com/distribution/reference"
"github.com/docker/buildx/builder"
"github.com/docker/buildx/policy"
Expand Down Expand Up @@ -408,7 +409,7 @@ func parseSource(input string) (*pb.SourceOp, error) {
},
}, nil
}
if err != nil {
if err != nil && !errors.Is(err, errdefs.ErrInvalidArgument) {
return nil, err
}
return &pb.SourceOp{Identifier: input}, nil
Expand Down
16 changes: 16 additions & 0 deletions docs/bake-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ The following table shows the complete list of attributes that you can assign to
| [`no-cache-filter`](#targetno-cache-filter) | List | Disable build cache for specific stages |
| [`no-cache`](#targetno-cache) | Boolean | Disable build cache completely |
| [`output`](#targetoutput) | List | Output destinations |
| [`policy`](#targetpolicy) | List | Policies to validate build sources and metadata |
| [`platforms`](#targetplatforms) | List | Target platforms |
| [`pull`](#targetpull) | Boolean | Always pull images |
| [`secret`](#targetsecret) | List | Secrets to expose to the build |
Expand Down Expand Up @@ -899,6 +900,21 @@ target "default" {
}
```

### `target.policy`

Policies to validate build sources and metadata. Each entry uses the same keys
as the `--policy` flag for `docker buildx build` (`filename`, `reset`,
`disabled`, `strict`, `log-level`). Bake also automatically loads
`Dockerfile.rego` alongside the target Dockerfile when present.

```hcl
target "default" {
policy = [
{ filename = "extra.rego" },
]
}
```

### `target.platforms`

Set target platforms for the build target.
Expand Down
Loading