diff --git a/.github/workflows/docs-upstream.yml b/.github/workflows/docs-upstream.yml
index f351233ef460..72cb3d1f64d2 100644
--- a/.github/workflows/docs-upstream.yml
+++ b/.github/workflows/docs-upstream.yml
@@ -65,7 +65,7 @@ jobs:
retention-days: 1
validate:
- uses: docker/docs/.github/workflows/validate-upstream.yml@6b73b05acb21edf7995cc5b3c6672d8e314cee7a # pin for artifact v4 support: https://github.com/docker/docs/pull/19220
+ uses: docker/docs/.github/workflows/validate-upstream.yml@main
needs:
- docs-yaml
with:
diff --git a/bake/bake.go b/bake/bake.go
index c7979dcf2b5b..f365b65beb8a 100644
--- a/bake/bake.go
+++ b/bake/bake.go
@@ -29,7 +29,6 @@ import (
"github.com/moby/buildkit/session/auth/authprovider"
"github.com/moby/buildkit/util/entitlements"
"github.com/pkg/errors"
- "github.com/tonistiigi/go-csvvalue"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/convert"
)
@@ -900,7 +899,7 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
case "tags":
t.Tags = o.ArrValue
case "cache-from":
- cacheFrom, err := parseCacheArrValues(o.ArrValue)
+ cacheFrom, err := buildflags.ParseCacheEntry(o.ArrValue)
if err != nil {
return err
}
@@ -913,7 +912,7 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
}
}
case "cache-to":
- cacheTo, err := parseCacheArrValues(o.ArrValue)
+ cacheTo, err := buildflags.ParseCacheEntry(o.ArrValue)
if err != nil {
return err
}
@@ -1585,37 +1584,3 @@ func parseArrValue[T any, PT arrValue[T]](s []string) ([]*T, error) {
}
return outputs, nil
}
-
-func parseCacheArrValues(s []string) (buildflags.CacheOptions, error) {
- var outs buildflags.CacheOptions
- for _, in := range s {
- if in == "" {
- continue
- }
-
- if !strings.Contains(in, "=") {
- // This is ref only format. Each field in the CSV is its own entry.
- fields, err := csvvalue.Fields(in, nil)
- if err != nil {
- return nil, err
- }
-
- for _, field := range fields {
- out := buildflags.CacheOptionsEntry{}
- if err := out.UnmarshalText([]byte(field)); err != nil {
- return nil, err
- }
- outs = append(outs, &out)
- }
- continue
- }
-
- // Normal entry.
- out := buildflags.CacheOptionsEntry{}
- if err := out.UnmarshalText([]byte(in)); err != nil {
- return nil, err
- }
- outs = append(outs, &out)
- }
- return outs, nil
-}
diff --git a/bake/bake_test.go b/bake/bake_test.go
index 15a3333d2a0d..f1f0a9f28a74 100644
--- a/bake/bake_test.go
+++ b/bake/bake_test.go
@@ -9,6 +9,7 @@ import (
"strings"
"testing"
+ "github.com/docker/buildx/util/buildflags"
"github.com/moby/buildkit/util/entitlements"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -1759,6 +1760,27 @@ func TestAnnotations(t *testing.T) {
require.Equal(t, "bar", bo["app"].Exports[0].Attrs["annotation-manifest[linux/amd64].foo"])
}
+func TestRefOnlyCacheOptions(t *testing.T) {
+ fp := File{
+ Name: "docker-bake.hcl",
+ Data: []byte(
+ `target "app" {
+ output = ["type=image,name=foo"]
+ cache-from = ["ref1,ref2"]
+ }`),
+ }
+ ctx := context.TODO()
+ m, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
+ require.NoError(t, err)
+
+ require.Len(t, m, 1)
+ require.Contains(t, m, "app")
+ require.Equal(t, buildflags.CacheOptions{
+ {Type: "registry", Attrs: map[string]string{"ref": "ref1"}},
+ {Type: "registry", Attrs: map[string]string{"ref": "ref2"}},
+ }, m["app"].CacheFrom)
+}
+
func TestHCLEntitlements(t *testing.T) {
fp := File{
Name: "docker-bake.hcl",
diff --git a/bake/compose.go b/bake/compose.go
index 58cfc80c953d..fba1f3de2836 100644
--- a/bake/compose.go
+++ b/bake/compose.go
@@ -145,12 +145,12 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf
labels[k] = &v
}
- cacheFrom, err := parseCacheArrValues(s.Build.CacheFrom)
+ cacheFrom, err := buildflags.ParseCacheEntry(s.Build.CacheFrom)
if err != nil {
return nil, err
}
- cacheTo, err := parseCacheArrValues(s.Build.CacheTo)
+ cacheTo, err := buildflags.ParseCacheEntry(s.Build.CacheTo)
if err != nil {
return nil, err
}
@@ -349,14 +349,14 @@ func (t *Target) composeExtTarget(exts map[string]interface{}) error {
t.Tags = dedupSlice(append(t.Tags, xb.Tags...))
}
if len(xb.CacheFrom) > 0 {
- cacheFrom, err := parseCacheArrValues(xb.CacheFrom)
+ cacheFrom, err := buildflags.ParseCacheEntry(xb.CacheFrom)
if err != nil {
return err
}
t.CacheFrom = t.CacheFrom.Merge(cacheFrom)
}
if len(xb.CacheTo) > 0 {
- cacheTo, err := parseCacheArrValues(xb.CacheTo)
+ cacheTo, err := buildflags.ParseCacheEntry(xb.CacheTo)
if err != nil {
return err
}
diff --git a/commands/build.go b/commands/build.go
index 264c13400b78..91a5786a4ac7 100644
--- a/commands/build.go
+++ b/commands/build.go
@@ -183,14 +183,17 @@ func (o *buildOptions) toControllerOptions() (*controllerapi.BuildOptions, error
}
}
- opts.CacheFrom, err = buildflags.ParseCacheEntry(o.cacheFrom)
+ cacheFrom, err := buildflags.ParseCacheEntry(o.cacheFrom)
if err != nil {
return nil, err
}
- opts.CacheTo, err = buildflags.ParseCacheEntry(o.cacheTo)
+ opts.CacheFrom = cacheFrom.ToPB()
+
+ cacheTo, err := buildflags.ParseCacheEntry(o.cacheTo)
if err != nil {
return nil, err
}
+ opts.CacheTo = cacheTo.ToPB()
opts.Secrets, err = buildflags.ParseSecretSpecs(o.secrets)
if err != nil {
diff --git a/docs/bake-reference.md b/docs/bake-reference.md
index 192dded4fe60..d8fd5ecc3015 100644
--- a/docs/bake-reference.md
+++ b/docs/bake-reference.md
@@ -221,8 +221,10 @@ The following table shows the complete list of attributes that you can assign to
| [`attest`](#targetattest) | List | Build attestations |
| [`cache-from`](#targetcache-from) | List | External cache sources |
| [`cache-to`](#targetcache-to) | List | External cache destinations |
+| [`call`](#targetcall) | String | Specify the frontend method to call for the target. |
| [`context`](#targetcontext) | String | Set of files located in the specified path or URL |
| [`contexts`](#targetcontexts) | Map | Additional build contexts |
+| [`description`](#targetdescription) | String | Description of a target |
| [`dockerfile-inline`](#targetdockerfile-inline) | String | Inline Dockerfile string |
| [`dockerfile`](#targetdockerfile) | String | Dockerfile location |
| [`inherits`](#targetinherits) | List | Inherit attributes from other targets |
@@ -371,6 +373,13 @@ target "app" {
}
```
+Supported values are:
+
+- `build` builds the target (default)
+- `check`: evaluates [build checks](https://docs.docker.com/build/checks/) for the target
+- `outline`: displays the target's build arguments and their default values if available
+- `targets`: lists all Bake targets in the loaded definition, along with its [description](#targetdescription).
+
For more information about frontend methods, refer to the CLI reference for
[`docker buildx build --call`](https://docs.docker.com/reference/cli/docker/buildx/build/#call).
@@ -481,6 +490,25 @@ FROM baseapp
RUN echo "Hello world"
```
+### `target.description`
+
+Defines a human-readable description for the target, clarifying its purpose or
+functionality.
+
+```hcl
+target "lint" {
+ description = "Runs golangci-lint to detect style errors"
+ args = {
+ GOLANGCI_LINT_VERSION = null
+ }
+ dockerfile = "lint.Dockerfile"
+}
+```
+
+This attribute is useful when combined with the `docker buildx bake --list=targets`
+option, providing a more informative output when listing the available build
+targets in a Bake file.
+
### `target.dockerfile-inline`
Uses the string value as an inline Dockerfile for the build target.
diff --git a/docs/reference/buildx_bake.md b/docs/reference/buildx_bake.md
index a2b6ff0c2c14..be7deee44355 100644
--- a/docs/reference/buildx_bake.md
+++ b/docs/reference/buildx_bake.md
@@ -15,7 +15,7 @@ Build from a file
| Name | Type | Default | Description |
|:------------------------------------|:--------------|:--------|:-------------------------------------------------------------------------------------------------------------|
-| `--allow` | `stringArray` | | Allow build to access specified resources |
+| [`--allow`](#allow) | `stringArray` | | Allow build to access specified resources |
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
| [`--call`](#call) | `string` | `build` | Set method for evaluating build (`check`, `outline`, `targets`) |
| [`--check`](#check) | `bool` | | Shorthand for `--call=check` |
@@ -51,6 +51,80 @@ guide for introduction to writing bake files.
## Examples
+### Allow extra privileged entitlement (--allow)
+
+```text
+--allow=ENTITLEMENT[=VALUE]
+```
+
+Entitlements are designed to provide controlled access to privileged
+operations. By default, Buildx and BuildKit operates with restricted
+permissions to protect users and their systems from unintended side effects or
+security risks. The `--allow` flag explicitly grants access to additional
+entitlements, making it clear when a build or bake operation requires elevated
+privileges.
+
+In addition to BuildKit's `network.host` and `security.insecure` entitlements
+(see [`docker buildx build --allow`](https://docs.docker.com/reference/cli/docker/buildx/build/#allow),
+Bake supports file system entitlements that grant granular control over file
+system access. These are particularly useful when working with builds that need
+access to files outside the default working directory.
+
+Bake supports the following filesystem entitlements:
+
+- `--allow fs=` - Grant read and write access to files outside of the
+ working directory.
+- `--allow fs.read=` - Grant read access to files outside of the
+ working directory.
+- `--allow fs.write=` - Grant write access to files outside of the
+ working directory.
+
+The `fs` entitlements take a path value (relative or absolute) to a directory
+on the filesystem. Alternatively, you can pass a wildcard (`*`) to allow Bake
+to access the entire filesystem.
+
+### Example: fs.read
+
+Given the following Bake configuration, Bake would need to access the parent
+directory, relative to the Bake file.
+
+```hcl
+target "app" {
+ context = "../src"
+}
+```
+
+Assuming `docker buildx bake app` is executed in the same directory as the
+`docker-bake.hcl` file, you would need to explicitly allow Bake to read from
+the `../src` directory. In this case, the following invocations all work:
+
+```console
+$ docker buildx bake --allow fs.read=* app
+$ docker buildx bake --allow fs.read=../src app
+$ docker buildx bake --allow fs=* app
+```
+
+### Example: fs.write
+
+The following `docker-bake.hcl` file requires write access to the `/tmp`
+directory.
+
+```hcl
+target "app" {
+ output = "/tmp"
+}
+```
+
+Assuming `docker buildx bake app` is executed outside of the `/tmp` directory,
+you would need to allow the `fs.write` entitlement, either by specifying the
+path or using a wildcard:
+
+```console
+$ docker buildx bake --allow fs=/tmp app
+$ docker buildx bake --allow fs.write=/tmp app
+$ docker buildx bake --allow fs.write=* app
+```
+
### Override the configured builder instance (--builder)
Same as [`buildx --builder`](buildx.md#builder).
diff --git a/util/buildflags/cache.go b/util/buildflags/cache.go
index db982e786c59..9cd5afb03d91 100644
--- a/util/buildflags/cache.go
+++ b/util/buildflags/cache.go
@@ -167,20 +167,37 @@ func (e *CacheOptionsEntry) validate(gv interface{}) error {
return nil
}
-func ParseCacheEntry(in []string) ([]*controllerapi.CacheOptionsEntry, error) {
+func ParseCacheEntry(in []string) (CacheOptions, error) {
if len(in) == 0 {
return nil, nil
}
opts := make(CacheOptions, 0, len(in))
for _, in := range in {
+ if !strings.Contains(in, "=") {
+ // This is ref only format. Each field in the CSV is its own entry.
+ fields, err := csvvalue.Fields(in, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, field := range fields {
+ opt := CacheOptionsEntry{}
+ if err := opt.UnmarshalText([]byte(field)); err != nil {
+ return nil, err
+ }
+ opts = append(opts, &opt)
+ }
+ continue
+ }
+
var out CacheOptionsEntry
if err := out.UnmarshalText([]byte(in)); err != nil {
return nil, err
}
opts = append(opts, &out)
}
- return opts.ToPB(), nil
+ return opts, nil
}
func addGithubToken(ci *controllerapi.CacheOptionsEntry) {
diff --git a/util/buildflags/cache_cty.go b/util/buildflags/cache_cty.go
index abfcd008a6a4..05e2e6bcccc3 100644
--- a/util/buildflags/cache_cty.go
+++ b/util/buildflags/cache_cty.go
@@ -30,6 +30,16 @@ func (o *CacheOptions) fromCtyValue(in cty.Value, p cty.Path) error {
continue
}
+ // Special handling for a string type to handle ref only format.
+ if value.Type() == cty.String {
+ entries, err := ParseCacheEntry([]string{value.AsString()})
+ if err != nil {
+ return err
+ }
+ *o = append(*o, entries...)
+ continue
+ }
+
entry := &CacheOptionsEntry{}
if err := entry.FromCtyValue(value, p); err != nil {
return err
@@ -52,13 +62,6 @@ func (o CacheOptions) ToCtyValue() cty.Value {
}
func (o *CacheOptionsEntry) FromCtyValue(in cty.Value, p cty.Path) error {
- if in.Type() == cty.String {
- if err := o.UnmarshalText([]byte(in.AsString())); err != nil {
- return p.NewError(err)
- }
- return nil
- }
-
conv, err := convert.Convert(in, cty.Map(cty.String))
if err != nil {
return err
diff --git a/util/buildflags/cache_test.go b/util/buildflags/cache_test.go
index c4368c0d470c..02d2f8ba431e 100644
--- a/util/buildflags/cache_test.go
+++ b/util/buildflags/cache_test.go
@@ -37,7 +37,7 @@ func TestCacheOptions_DerivedVars(t *testing.T) {
"session_token": "not_a_mitm_attack",
},
},
- }, cacheFrom)
+ }, cacheFrom.ToPB())
}
func TestCacheOptions(t *testing.T) {
@@ -109,3 +109,12 @@ func TestCacheOptions(t *testing.T) {
require.True(t, result.True())
})
}
+
+func TestCacheOptions_RefOnlyFormat(t *testing.T) {
+ opts, err := ParseCacheEntry([]string{"ref1", "ref2"})
+ require.NoError(t, err)
+ require.Equal(t, CacheOptions{
+ {Type: "registry", Attrs: map[string]string{"ref": "ref1"}},
+ {Type: "registry", Attrs: map[string]string{"ref": "ref2"}},
+ }, opts)
+}