Compare commits

..

186 Commits

Author SHA1 Message Date
Tõnis Tiigi
18ccba0720 Merge pull request #3068 from crazy-max/GHSA-m4gq-fm9h-8q75
cherry-picks for CVE-2025-0495
2025-03-17 11:37:50 -07:00
CrazyMax
f5196f1167 localstate: remove definition and inputs fields from group
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-03-17 18:14:55 +01:00
Tonis Tiigi
ef99381eab otel: avoid tracing raw os arguments
User might pass a value that they don't expect to
be kept in trace storage. For example some cache backends
allow passing authentication tokens with a flag.

Instead use known primary config values as attributes
of the root span.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-03-17 18:14:52 +01:00
CrazyMax
00fdcd38ab Merge pull request #3062 from crazy-max/builder-error-boot
builder: return error if a node fails to boot
2025-03-13 18:02:13 +01:00
Tõnis Tiigi
97f1d47464 Merge pull request #3063 from crazy-max/driver-ctn-gpu-request
driver: request gpu when creating container builder
2025-03-13 09:56:10 -07:00
CrazyMax
337578242d driver: request gpu when creating container builder
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-03-13 17:36:37 +01:00
CrazyMax
503a8925d2 builder: return error if a node fails to boot
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-03-12 16:05:16 +01:00
Tõnis Tiigi
0d708c0bc2 Merge pull request #3058 from crazy-max/buildkit-0.20.1
vendor: github.com/moby/buildkit v0.20.1
2025-03-11 09:30:42 -07:00
Tõnis Tiigi
3a7523a117 Merge pull request #3057 from crazy-max/update-compose
vendor: update compose-go to v2.4.8
2025-03-11 09:09:46 -07:00
CrazyMax
5dc1a3308d Merge pull request #3040 from crazy-max/ci-fix-no-space-left
ci: fix faulty bin-image job
2025-03-11 16:04:39 +01:00
CrazyMax
eb78253dfd Merge pull request #3055 from tonistiigi/history-queryrecord
history: generalize query loading
2025-03-11 15:10:00 +01:00
CrazyMax
5f8b78a113 vendor: github.com/moby/buildkit v0.20.1
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-03-11 15:07:47 +01:00
CrazyMax
67d3ed34e4 vendor: update compose-go to v2.4.8
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-03-11 14:56:19 +01:00
Tõnis Tiigi
b88423be50 Merge pull request #3053 from tonistiigi/modernize-fixes
lint: apply x/tools/modernize fixes and validation
2025-03-10 18:37:51 -07:00
Tonis Tiigi
c1e2ae5636 history: generalize query loading
Some commands (logs/open) were still missing offset handling.
Now all commands use the same reference parsing/sort.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-03-10 15:51:03 -07:00
Tõnis Tiigi
23afb70e40 Merge pull request #3039 from tonistiigi/history-import
history: add history import command
2025-03-10 10:09:36 -07:00
CrazyMax
812b42b329 history: desktop build backend not yet supported on WSL
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-03-10 17:12:21 +01:00
Tonis Tiigi
d5d3d3d502 lint: apply x/tools/modernize fixes
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-03-07 16:37:24 -08:00
Tõnis Tiigi
e19c729d3e Merge pull request #3049 from tonistiigi/history-inspect-index
history: allow index based inspect of builds
2025-03-06 11:09:36 -08:00
CrazyMax
aefa49c4fa Merge pull request #3044 from docker/dependabot/github_actions/peter-evans/create-pull-request-7.0.8
build(deps): bump peter-evans/create-pull-request from 7.0.7 to 7.0.8
2025-03-06 16:23:26 +01:00
dependabot[bot]
7d927ee604 build(deps): bump peter-evans/create-pull-request from 7.0.7 to 7.0.8
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.7 to 7.0.8.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](dd2324fc52...271a8d0340)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-06 14:58:27 +00:00
Tonis Tiigi
058c098c8c history: allow index based inspect of builds
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-03-05 21:33:24 -08:00
Tõnis Tiigi
7b7dbe88b1 Merge pull request #3046 from crazy-max/buildkit-0.20.1
dockerfile: update buildkit to 0.20.1
2025-03-05 17:20:14 -08:00
Tonis Tiigi
cadf4a5893 history: add multi-file/stdin import
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-03-05 11:12:52 -08:00
CrazyMax
6cd9fef556 dockerfile: update buildkit to 0.20.1
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-03-05 17:13:03 +01:00
Tonis Tiigi
963b9ca30d history: print urls after importing builds
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-03-04 16:13:49 -08:00
CrazyMax
4636c8051a ci: fix faulty bin-image job
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-03-05 00:47:17 +01:00
Tõnis Tiigi
e23695d50d Merge pull request #3042 from crazy-max/ci-bump-ubuntu
ci: bump to ubuntu-24.04
2025-03-04 15:41:06 -08:00
CrazyMax
6eff9b2d51 ci: update install-k3s step to fix issue with latest ubuntu runners
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-03-05 00:21:09 +01:00
CrazyMax
fcbfc85f42 ci: bump to ubuntu-24.04
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-03-04 23:20:01 +01:00
Tõnis Tiigi
9a204c44c3 Merge pull request #3031 from crazy-max/bake-set-append
bake: support += operator to append with overrides
2025-03-04 09:33:57 -08:00
CrazyMax
4c6eba5acd bake: support += operator to append with overrides
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-03-04 13:29:41 +01:00
Tonis Tiigi
fea7459880 history: add history import command
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-03-03 22:52:05 -08:00
Tõnis Tiigi
e2d52a8465 Merge pull request #2901 from crazy-max/netbsd
build and test netbsd
2025-03-03 16:43:02 -08:00
Tõnis Tiigi
48a591b1e1 Merge pull request #3032 from crazy-max/bake-secrets-dupes
correctly remove duplicated secrets and ssh keys
2025-03-03 16:40:14 -08:00
CrazyMax
128acdb471 Merge pull request #3027 from LaurentGoderre/fix-attest-extra-args
Fix attest extra arguments
2025-03-03 16:28:02 +01:00
CrazyMax
411d3f8cea Merge pull request #3035 from co63oc/fix1
Fix typos
2025-03-03 14:07:56 +01:00
co63oc
7925a96726 Fix
Signed-off-by: co63oc <co63oc@users.noreply.github.com>
2025-03-02 21:20:50 +08:00
Laurent Goderre
b06bddfee6 Fix handling of attest extra arguments
Signed-off-by: Laurent Goderre <laurent.goderre@docker.com>
2025-02-28 12:09:32 -05:00
CrazyMax
fe17ebda89 correctly remove duplicated secrets and ssh keys
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-28 15:24:16 +01:00
CrazyMax
4ed1e07f16 Merge pull request #3030 from thaJeztah/bump_docker_28.0.1
vendor: github.com/docker/docker, docker/cli v28.0.1
2025-02-28 10:54:35 +01:00
Sebastiaan van Stijn
f49593ce2c vendor: github.com/docker/docker, docker/cli v28.0.1
diffs:

- https://github.com/docker/docker/compare/v28.0.0...v28.0.1
- https://github.com/docker/cli/compare/v28.0.0...v28.0.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-28 00:50:48 +01:00
Laurent Goderre
4e91fe6507 Add attest extra args tests
Signed-off-by: Laurent Goderre <laurent.goderre@docker.com>
2025-02-27 17:10:30 -05:00
CrazyMax
921b576f3a Merge pull request #3023 from tonistiigi/dockerd-push-fix
avoid double pushing with docker driver with containerd
2025-02-25 16:44:00 +01:00
CrazyMax
548c80ab5a Merge pull request #3024 from tonistiigi/imagetools-push-tag-fix
imagetools: avoid multiple tag pushes on create
2025-02-25 16:36:37 +01:00
CrazyMax
f3a4740d5f Merge pull request #3026 from thaJeztah/bump_engine_28.0
vendor: docker/docker, docker/cli v28.0.0
2025-02-25 16:35:56 +01:00
Sebastiaan van Stijn
89917dc696 vendor: docker/docker, docker/cli v28.0.0
no code changes in vendored code

full diff:

- https://github.com/docker/cli/compare/v28.0.0-rc.3...v28.0.0
- https://github.com/docker/docker/compare/v28.0.0-rc.3...v28.0.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-25 12:37:44 +01:00
CrazyMax
f7276201ac Merge pull request #3021 from jsternberg/empty-cache-to-override
buildflags: skip empty cache entries when parsing
2025-02-25 10:48:39 +01:00
CrazyMax
beb9f515c0 Merge pull request #3022 from docker/dependabot/github_actions/peter-evans/create-pull-request-7.0.7
build(deps): bump peter-evans/create-pull-request from 7.0.6 to 7.0.7
2025-02-25 09:54:20 +01:00
Tonis Tiigi
4f7d145c0e avoid double pushing with docker driver with containerd
In this mode buildkit can push directly so pushing manually
with docker would result in pushing image twice.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-02-24 16:48:57 -08:00
Tonis Tiigi
ccdf63c644 imagetools: avoid multiple tag pushes on create
Ensure only the final manifest is pushed by tag and intermediate
blobs are only pushed by digest to avoid tag temorarily pointing to
wrong image.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-02-24 16:48:15 -08:00
dependabot[bot]
9a6b8754b1 build(deps): bump peter-evans/create-pull-request from 7.0.6 to 7.0.7
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.6 to 7.0.7.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](67ccf781d6...dd2324fc52)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-24 18:27:58 +00:00
Jonathan A. Sternberg
e75ac22ba6 buildflags: skip empty cache entries when parsing
Broken in 11c84973ef. The section to skip
an empty input was accidentally removed when some code was refactored to
fix a separate issue.

This skips empty cache entries which allows disabling the `cache-from` and
`cache-to` entries from the command line overrides.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-02-24 10:09:02 -06:00
Shaun Thompson
62f5cc7c80 Merge pull request #3017 from tonistiigi/remove-debug
remove accidental debug
2025-02-20 20:08:16 -05:00
Tonis Tiigi
6272ae1afa remove accidental debug
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-02-20 15:41:13 -08:00
CrazyMax
accfbf6e24 Merge pull request #2997 from jsternberg/bake-set-annotations
bake: allow annotations to be set on the command line
2025-02-20 17:53:48 +01:00
CrazyMax
af2d8fe555 build and test netbsd
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-20 13:04:48 +01:00
CrazyMax
18f4275a92 Merge pull request #2995 from crazy-max/ci-infer-goversion-bsd
ci: infer go version from workflow for bsd tests
2025-02-20 13:04:19 +01:00
CrazyMax
221a608b3c Merge pull request #3014 from crazy-max/dockerfile-docker-28
Dockerfile: update to docker v28.0.0
2025-02-20 11:36:06 +01:00
CrazyMax
cc0391eba5 ci: infer go version from workflow for bsd tests
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-20 11:29:40 +01:00
CrazyMax
aef388bf7a Dockerfile: update to docker v28.0.0
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-20 11:19:18 +01:00
CrazyMax
80c16bc28c Merge pull request #3013 from jsternberg/buildkit-bump
ci: update buildkit to 0.20.0
2025-02-20 10:57:02 +01:00
Jonathan A. Sternberg
75160643e1 ci: update buildkit to 0.20.0
Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-02-19 15:21:14 -06:00
Jonathan A. Sternberg
ad18ffc018 Merge pull request #3010 from jsternberg/vendor-update
vendor: github.com/moby/buildkit v0.20.0
2025-02-19 13:30:37 -06:00
Jonathan A. Sternberg
80c3832c94 vendor: github.com/moby/buildkit v0.20.0
Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-02-19 13:17:40 -06:00
Jonathan A. Sternberg
7762ab2c38 Merge pull request #3008 from thaJeztah/bump_engine_28.0_rc3
vendor: github.com/docker/docker, docker/cli v28.0.0-rc.3
2025-02-19 11:59:57 -06:00
Sebastiaan van Stijn
b973de2dd3 vendor: github.com/docker/cli v28.0.0-rc.3
no significant changes, only linting fixes

full diff: https://github.com/docker/cli/compare/v28.0.0-rc.2...v28.0.0-rc.3

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-19 13:39:40 +01:00
Sebastiaan van Stijn
352ce7e875 vendor: github.com/docker/docker v28.0.0-rc.3
no code changes in vendor, only updated swagger file

full diff: https://github.com/docker/docker/compare/v28.0.0-rc.2...v28.0.0-rc.3

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-19 13:37:43 +01:00
CrazyMax
cdfc1ed750 Merge pull request #2994 from tonistiigi/device-entitlements
support for device entitlement in build and bake
2025-02-18 22:28:23 +01:00
CrazyMax
d0d3433b12 vendor: update buildkit to v0.20.0-rc3
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-18 21:59:36 +01:00
CrazyMax
b04d39494f Merge pull request #3001 from crazy-max/fix-gha-cache-v2
cache: enable gha cache backend if cache service v2 detected
2025-02-18 21:24:14 +01:00
CrazyMax
52f503e806 Merge pull request #3003 from tonistiigi/debug-progress-fix
progress: fix race on pausing progress on debug shell
2025-02-18 10:58:51 +01:00
Tonis Tiigi
79a978484d progress: fix race on pausing progress on debug shell
Current progress writer has a logic of pausing/unpausing
the printer and internally recreating internal channels.

This conflicts with a change that added sync.Once to Wait
to allow it being called multiple times without erroring.

In debug shell this could mean that new progress printer
showed up in debug shell because it was not closed.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-02-17 21:02:49 -08:00
CrazyMax
f7992033bf cache: fix gha cache url handling
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-17 19:01:13 +01:00
CrazyMax
73f61aa338 cache: enable gha cache backend if cache service v2 detected
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-17 18:13:12 +01:00
CrazyMax
faa573f484 Merge pull request #2998 from thaJeztah/bump_docker
vendor:  docker/docker, docker/cli v28.0.0-rc.2
2025-02-17 17:08:43 +01:00
Sebastiaan van Stijn
0a4a1babd1 vendor: github.com/docker/cli v28.0.0-rc.2
full diff: https://github.com/docker/cli/compare/v28.0.0-rc.1...v28.0.0-rc.2

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-17 16:43:59 +01:00
Sebastiaan van Stijn
461bd9e5d1 vendor: github.com/docker/docker v28.0.0-rc.2
full diff: https://github.com/docker/docker/compare/v28.0.0-rc.1...v28.0.0-rc.2

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-17 16:43:51 +01:00
Jonathan A. Sternberg
d6fdf83f45 bake: allow annotations to be set on the command line
Annotations were not merged correctly. The overrides in `ArrValue` would
be merged, but the section of code setting them from the command line
did not include `annotations` in the list of available attributes so the
command line option was completely discarded.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-02-14 11:57:30 -06:00
CrazyMax
ef4e9fea83 Merge pull request #2992 from crazy-max/docker-28
vendor: docker, docker/cli v28.0.0-rc.1
2025-02-14 14:06:09 +01:00
Tõnis Tiigi
0c296fe857 support for device entitlement in build and bake
Allow access to CDI Devices in Buildkit v0.20.0+ for
devices that are not automatically allowed to be used by
everyone in BuildKit configuration.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-14 11:51:47 +01:00
Tõnis Tiigi
ef73c64d2c Merge pull request #2993 from tonistiigi/update-buildkit-v0.20.0-rc2
vendor: update buildkit to v0.20.0-rc2
2025-02-13 17:15:50 -08:00
Tonis Tiigi
1784f84561 vendor: update buildkit to v0.20.0-rc2
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-02-13 16:54:50 -08:00
Tõnis Tiigi
6a6fa4f422 Merge pull request #2986 from tonistiigi/remove-x-slices
remove import of x/exp
2025-02-13 10:16:48 -08:00
Sebastiaan van Stijn
2dc0350ffe vendor: github.com/docker/cli/v28.0.0-rc.1
full diff: https://github.com/docker/cli/compare/v27.5.1..v28.0.0-rc.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-13 13:53:45 +01:00
Sebastiaan van Stijn
b85fc5c484 vendor: github.com/docker/docker/v28.0.0-rc.1
full diff: https://github.com/docker/docker/compare/v27.5.1..v28.0.0-rc.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-13 13:53:44 +01:00
Tõnis Tiigi
2389d457a4 Merge pull request #2988 from crazy-max/ctn-driver-display-pull-error
docker-container: check error from response body when pulling image
2025-02-12 08:47:05 -08:00
CrazyMax
3f82aadc6e docker-container: check error from response body when pulling image
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-12 12:35:27 +01:00
Tonis Tiigi
79e3f12305 remove import of x/exp
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-02-11 19:23:36 -08:00
Tõnis Tiigi
1dc5f0751b Merge pull request #2983 from tonistiigi/update-buildkit-v0.20.0-rc1
vendor: update buildkit to v0.20.0-rc1
2025-02-11 16:20:02 -08:00
Tonis Tiigi
7ba4da0800 gha: send v2 url as url_v2
Some repositories already have v2 enabled and that
causes errors avainst older BuildKit. To avoid that we
need to send both URLs as separate keys.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-02-11 15:49:29 -08:00
Tonis Tiigi
a64e628774 .github: test github runtime envs
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-02-11 15:41:15 -08:00
Tonis Tiigi
1c4b1a376c show CDI devices in builder inspection
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-02-11 14:52:33 -08:00
Tonis Tiigi
e1f690abfc allow passing github cache v2 urls from env
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-02-11 14:52:33 -08:00
Tonis Tiigi
03569c2188 vendor: update buildkit to v0.20.0-rc1
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-02-11 14:52:19 -08:00
Tõnis Tiigi
350d3f0f4b Merge pull request #2904 from tonistiigi/history-command-trace
Add history trace command
2025-02-11 12:40:10 -08:00
CrazyMax
dc27815236 ci: fix git config for unit tests
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-11 11:40:04 -08:00
Tonis Tiigi
1089ff7341 history: add comparison support to trace
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-02-11 11:40:04 -08:00
Tonis Tiigi
7433d37183 history: add loadTrace function and support for loading Nth trace
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-02-11 11:40:04 -08:00
Tonis Tiigi
f9a76355b5 history: add UI view to traces
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-02-11 11:40:01 -08:00
Tonis Tiigi
cfeea34b2d add history trace command
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-02-11 11:38:23 -08:00
Tõnis Tiigi
ba2d3692a6 Merge pull request #2982 from crazy-max/revert-docker-28-vendor
Revert "vendor: docker, docker/cli v28.0.0-rc.1"
2025-02-11 11:37:32 -08:00
Tõnis Tiigi
853b593a4d Merge pull request #2981 from crazy-max/hack-mount-docker-cfg
hack: mount docker config on gha
2025-02-11 10:36:45 -08:00
CrazyMax
efb300e613 chore: fix vendoring
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-11 19:17:35 +01:00
CrazyMax
cee7b344da Revert "vendor: github.com/docker/docker/v28.0.0-rc.1"
This reverts commit b195b80ddf.

Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-11 18:14:49 +01:00
CrazyMax
67dbde6970 Revert "vendor: github.com/docker/cli/v28.0.0-rc.1"
This reverts commit 7216086b8c.

Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-11 18:14:49 +01:00
CrazyMax
295653dabb hack: mount docker config on gha
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-11 17:32:50 +01:00
CrazyMax
f5802119c5 Merge pull request #2978 from jsternberg/rangefunc-go1.22-revert
buildflags: make work on go 1.22 by reverting rangefunc usage
2025-02-11 10:47:01 +01:00
CrazyMax
40b9ac1ec5 Merge pull request #2979 from tonistiigi/update-buildkit-0e3037c0182e
vendor: update buildkit to 0e3037c0182e
2025-02-11 10:29:51 +01:00
Tonis Tiigi
f11496448a vendor: update buildkit to 0e3037c0182e
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-02-10 16:48:59 -08:00
Tõnis Tiigi
c8c9c72ca6 Merge pull request #2964 from crazy-max/history-inspect-json
history: inspect json and go template format
2025-02-10 16:30:42 -08:00
Tõnis Tiigi
9fe8139022 Merge pull request #2976 from crazy-max/ci-fix-vagrant
ci: install latest vagrant
2025-02-10 16:16:15 -08:00
CrazyMax
b3e8c62635 ci: install latest vagrant
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-10 20:54:44 +01:00
Tõnis Tiigi
b8e9c28315 Merge pull request #2970 from crazy-max/fix-ls-json
ls: fix duplicated builders for json format
2025-02-10 09:28:17 -08:00
Jonathan A. Sternberg
3ae9970da5 buildflags: make work on go 1.22 by reverting rangefunc usage
Reverts the usage of rangefunc and attempts to keep the foundation of it
in for when we move to go 1.23. We have downstream dependencies that
aren't ready to move to go 1.23. We can likely move after go 1.24 is
released.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-02-10 11:03:46 -06:00
CrazyMax
1d219100fc Merge pull request #2868 from thaJeztah/bump_engine
vendor: docker, docker/cli v28.0.0-rc.1
2025-02-10 17:22:31 +01:00
CrazyMax
464f9278d1 history: fix default format for inspect command
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-10 11:30:59 +01:00
Sebastiaan van Stijn
7216086b8c vendor: github.com/docker/cli/v28.0.0-rc.1
full diff: https://github.com/docker/cli/compare/v27.5.1..v28.0.0-rc.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-10 11:07:38 +01:00
Sebastiaan van Stijn
b195b80ddf vendor: github.com/docker/docker/v28.0.0-rc.1
full diff: https://github.com/docker/docker/compare/v27.5.1..v28.0.0-rc.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-10 11:07:35 +01:00
Sebastiaan van Stijn
70a5e266d1 vendor: github.com/moby/term v0.5.2
full diff:

- https://github.com/moby/term/compare/v0.5.0...v0.5.2
- d185dfc1b5...faa5f7b017

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-10 11:06:24 +01:00
Sebastiaan van Stijn
689bea7963 vendor: golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f
full diff: 701f63a606...2d47ceb269

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-10 11:06:22 +01:00
Sebastiaan van Stijn
5176c38115 vendor: golang.org/x/mod v0.22.0
full diff: https://github.com/golang/mod/compare/v0.21.0...v0.22.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-10 11:05:52 +01:00
Sebastiaan van Stijn
ec440c4574 vendor: golang.org/x/sys v0.29.0
full diff: https://github.com/golang/sys/compare/v0.28.0...v0.29.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-10 11:05:51 +01:00
CrazyMax
0a4eb7ec76 Merge pull request #2971 from thaJeztah/test_engine_28
Dockerfile: update to docker v28.0.0-rc.1
2025-02-10 11:03:38 +01:00
Sebastiaan van Stijn
f710c93157 vendor: github.com/docker/cli v27.5.1
no changes in vendored code

full diff: https://github.com/docker/cli/compare/v27.5.0...v27.5.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-09 13:53:05 +01:00
Sebastiaan van Stijn
d1a0a1497c vendor: github.com/docker/docker v27.5.1
no changes in vendored code

full diff: https://github.com/docker/docker/compare/v27.5.0...v27.5.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-09 13:53:05 +01:00
Sebastiaan van Stijn
c880ecd513 Dockerfile: update to docker v28.0.0-rc.1
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-09 13:50:14 +01:00
Tõnis Tiigi
d557da1935 Merge pull request #2957 from ndeloof/prompt-rawjson
don't warn user about missing --allows when running with progress=rawjson
2025-02-07 16:34:10 -08:00
CrazyMax
417af36abc history: support go template format for inspect
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-07 12:09:31 +01:00
CrazyMax
e236b86297 history: set materials and attachments to json output for inspect
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-07 12:09:31 +01:00
CrazyMax
633e8a0881 history: add error sources and stack to json output for inspect
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-07 11:37:46 +01:00
CrazyMax
5e1ea62f92 ls: fix duplicated builders for json format
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-07 10:23:55 +01:00
Tõnis Tiigi
4b90b84995 Merge pull request #2965 from jsternberg/handle-unknown-values
buildflags: handle unknown values from cty
2025-02-06 10:06:49 -08:00
Jonathan A. Sternberg
abc85c38f8 buildflags: handle unknown values from cty
Update the buildflags cty code to handle unknown values. When hcl
decodes a value with an invalid variable name, it appends a diagnostic
for the error and then returns an unknown value so it can continue
processing the file and finding more errors.

The iteration code has now been changed to use a rangefunc from go 1.23
and it skips empty or unknown values. Empty values are valid when they
are skipped and unknown values will have a diagnostic for itself.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-02-06 09:45:18 -06:00
CrazyMax
ccca7c795a history: json format support for inspect command
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-06 16:25:49 +01:00
CrazyMax
04aab6958c history: set num steps, name, default platform and error logs to inspect
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-02-06 16:12:37 +01:00
Tonis Tiigi
9d640f0e33 history: add formatting support to inspect
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-02-06 10:45:27 +01:00
CrazyMax
b76fdcaf8d Merge pull request #2963 from thaJeztah/consistent_alias
use a consistent alias for the docker client package
2025-02-03 13:39:27 +01:00
Sebastiaan van Stijn
d693e18c04 use a consistent alias for the docker client package
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-03 11:36:52 +01:00
CrazyMax
b066ee1110 Merge pull request #2961 from thaJeztah/driver_use_errdefs
driver/docker-container: remove uses of dockerclient.IsErrNotFound
2025-02-03 09:41:24 +01:00
CrazyMax
cf8bf9e104 Merge pull request #2950 from thaJeztah/fix_usage_and_completion
fix: strip path from usage output and shell-completion scripts
2025-02-02 01:11:29 +01:00
Sebastiaan van Stijn
3bd54b19aa driver/docker-container: remove uses of dockerclient.IsErrNotFound
It's a wrapper around errdefs.IsNotFound, which is already used, so we
can skip the wrapper.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-01 15:22:33 +01:00
Tõnis Tiigi
934841f329 Merge pull request #2958 from crazy-max/fix-debug-invoke
debug: fix invoke on error
2025-01-31 10:17:08 -08:00
CrazyMax
b2ababc7b6 debug: fix invoke on error
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-01-31 10:45:34 +01:00
Nicolas De Loof
0ccdb7e248 don't warn user about missing --allows when running with progress=rawjson
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-31 08:49:36 +01:00
CrazyMax
cacb4fb9b3 Merge pull request #2953 from dvdksn/docs-bake-composable-attrs
docs: update bake reference to use composable attrs
2025-01-29 10:44:05 +01:00
David Karlsson
df80bd72c6 docs: update bake reference to use composable attrs
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2025-01-29 09:55:45 +01:00
Sebastiaan van Stijn
bb4bef2f04 fix: strip path from usage output and shell-completion scripts
Before this patch, both "usage" and shell-completion scripts would preserve
the path of the invoked command, which was especially problematic for the
completion-scripts, because Cobra's completion depends on Command.Name()
for this (see [1], [2]);

    ./bin/build/buildx --help | head -n 5
    Extended build capabilities with BuildKit

    Usage:
      ./bin/build/buildx
      ./bin/build/buildx [command]

    ./bin/build/buildx completion bash | head -n 3
    # bash completion V2 for ./bin/build/buildx                   -*- shell-script -*-

    __./bin/build/buildx_debug()

This would also be problematic if the path contained a space, for example;

    ln -s $(pwd)/bin/build $(pwd)/bin/Program\ Files

    ./bin/Program\ Files/buildx completion bash | head -n 3
    # bash completion V2 for ./bin/Program                        -*- shell-script -*-

    __./bin/Program_debug()

With this patch, the path is stripped to prevent this issue;

    ./bin/build/buildx --help | head -n 5
    Extended build capabilities with BuildKit

    Usage:
      buildx
      buildx [command]

    ./bin/build/buildx completion bash | head -n 3
    # bash completion V2 for buildx                               -*- shell-script -*-

    __buildx_debug()

    ./bin/Program\ Files/buildx completion bash | head -n 3
    # bash completion V2 for buildx                               -*- shell-script -*-

    __buildx_debug()

It's worth noting that this patch only fixes these basic issues. Other cases
are not yet addressed, and may need fixes in Cobra because (especially for
the completion scripts) it should likely not conflate "Name" with "executable".

For example, command.Name() does not handle situations where the executable
itself has a space in its name:

    ln -s $(pwd)/bin/build/buildx $(pwd)/bin/build/hello\ world

    ./bin/build/hello\ world completion bash | head -n 3
    # bash completion V2 for hello                                -*- shell-script -*-

    __hello_debug()

Other, less problematic, issues to address are case-insensitive filesystems,
where the binary can be invoked with any case;

    ./bin/build/bUiLdX --help | head -n 5
    Extended build capabilities with BuildKit

    Usage:
      bUiLdX
      bUiLdX [command]

    ./bin/build/bUiLdX completion bash | head -n 3
    # bash completion V2 for bUiLdX                               -*- shell-script -*-

    __bUiLdX_debug()

[1]: https://github.com/spf13/cobra/blob/v1.8.1/bash_completionsV2.go#L24-L39
[2]: https://github.com/spf13/cobra/blob/v1.8.1/command.go#L1502-L1510

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-01-25 14:25:43 +01:00
Tõnis Tiigi
a11507344a Merge pull request #2932 from crazy-max/buildkit-0.19.0
vendor: update buildkit to v0.19.0
2025-01-22 12:57:37 -08:00
Tõnis Tiigi
17af006857 Merge pull request #2944 from jsternberg/cache-ref-only-format-fix
buildflags: fix ref only format for command line and bake
2025-01-22 12:57:02 -08:00
Jonathan A. Sternberg
11c84973ef buildflags: fix ref only format for command line and bake
Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-01-22 13:18:38 -06:00
Tõnis Tiigi
cc4a291f6a Merge pull request #2941 from crazy-max/ci-fix-docs-upstream
ci: use main branch for docs upstream validation workflow
2025-01-22 10:36:56 -08:00
CrazyMax
aa1fbc0421 ci: use main branch for docs upstream validation workflow
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-01-22 19:11:26 +01:00
Tõnis Tiigi
b2bbb337e4 Merge pull request #2835 from dvdksn/bake-v019-entitlements
docs: bake v0.19 entitlements
2025-01-22 09:48:38 -08:00
David Karlsson
012df71b63 docs: add docs for bake --allow
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2025-01-22 18:25:32 +01:00
David Karlsson
a26bb271ab docs(bake): improve docs on "call" and "description" in bake file
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2025-01-22 18:23:18 +01:00
CrazyMax
3e0682f039 Merge pull request #2937 from jsternberg/attests-json-marshal
buildflags: marshal attestations into json with extra attributes correctly
2025-01-22 09:16:54 +01:00
Jonathan A. Sternberg
3aed658dc4 buildflags: marshal attestations into json with extra attributes correctly
`MarshalJSON` would not include the extra attributes because it iterated
over the target map rather than the source map.

Also fixes JSON unmarshaling for SSH and secrets. The intention was to
unmarshal into the struct, but `UnmarshalText` takes priority over the
default struct unmarshaling so it didn't work as intended.

Tests have been added for all marshaling and unmarshaling methods.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-01-21 15:05:23 -06:00
CrazyMax
b4a0dee723 Merge pull request #2935 from crazy-max/ci-update-buildkit
ci: update buildkit to 0.19.0
2025-01-21 13:50:26 +01:00
CrazyMax
6904512813 ci: update buildkit to 0.19.0
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-01-21 10:31:14 +01:00
CrazyMax
d41e335466 Merge pull request #2934 from crazy-max/update-buildkit-dockerfile
dockerfile: update buildkit to 0.19.0
2025-01-21 10:17:21 +01:00
CrazyMax
0954dcb5fd dockerfile: update buildkit to 0.19.0
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-01-20 20:41:12 +01:00
CrazyMax
38f64bf709 vendor: update buildkit to v0.19.0
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-01-20 18:55:10 +01:00
Tõnis Tiigi
c1d3955fbe Merge pull request #2928 from tonistiigi/update-buildkit-v0.19.0-rc3
vendor: update buildkit to v0.19.0-rc3
2025-01-17 12:53:50 -08:00
Tonis Tiigi
d0b63e60e2 vendor: update buildkit to v0.19.0-rc3
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-01-17 12:09:08 -08:00
Tõnis Tiigi
e141c8fa71 Merge pull request #2923 from crazy-max/docs-bake-overrides
chore: comments to not forget to update docs
2025-01-17 10:45:44 -08:00
Tõnis Tiigi
2ee156236b Merge pull request #2925 from tonistiigi/history-inspect-error
history: add error details to history inspect command
2025-01-17 10:23:59 -08:00
Tonis Tiigi
1335264c9d history: update formatting of error logs
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-01-17 08:54:38 -08:00
CrazyMax
e74185aa6d Merge pull request #2927 from crazy-max/update-labels
chore: handle area/history label
2025-01-17 15:37:28 +01:00
CrazyMax
0224773102 chore: handle area/history label
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-01-17 15:21:35 +01:00
Tonis Tiigi
8c27b5c545 history: make sure started time is shown in current timezone
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-01-16 21:12:37 -08:00
Tonis Tiigi
f7594d484b history: fix printing desktop URL
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-01-16 21:12:37 -08:00
Tonis Tiigi
f118749cdc history: add error details to history inspect command
For failed builds, show the source with error location and last
logs for vertex that caused the error. When debug mode is on,
stacktrace is printed.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-01-16 21:12:17 -08:00
CrazyMax
0d92ad713c chore: comments to not forget to update docs
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-01-16 10:11:43 +01:00
Tõnis Tiigi
a18ff4d5ef Merge pull request #2891 from tonistiigi/history-command-initial
Add buildx history command
2025-01-15 08:51:23 -08:00
CrazyMax
b035a04aaa history: update containerd imports to v2
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-01-15 17:22:05 +01:00
Tonis Tiigi
6220e0aae8 add history inspect attachment command
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-01-15 16:17:21 +01:00
Tonis Tiigi
d9abc78e8f update history inspect formatting
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-01-15 16:17:21 +01:00
Tonis Tiigi
3313026961 add buildx history inspect formatting
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-01-15 16:17:20 +01:00
Tonis Tiigi
06912aa24c Add buildx history command
These commands allow working with build records
of completed and running builds.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-01-15 16:17:20 +01:00
CrazyMax
cde0e9814d Merge pull request #2921 from thaJeztah/downgrade_tagged_releases
downgrade go-difflib and go-spew to tagged releases
2025-01-15 15:03:23 +01:00
CrazyMax
2e6e146087 Merge pull request #2920 from crazy-max/dockerfile-update-buildkit
dockerfile: update buildkit to 0.19.0-rc2
2025-01-15 14:50:15 +01:00
CrazyMax
af3cbe6cec Merge pull request #2919 from crazy-max/dockerfile-update-docker
dockerfile: update docker to 27.5.0
2025-01-15 14:48:30 +01:00
Sebastiaan van Stijn
1ef9e67cbb downgrade go-difflib and go-spew to tagged releases
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-01-15 14:41:48 +01:00
CrazyMax
75204426bd dockerfile: update buildkit to 0.19.0-rc2
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-01-15 13:33:17 +01:00
CrazyMax
6f5486e718 dockerfile: update docker to 27.5.0
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-01-15 13:24:39 +01:00
559 changed files with 49317 additions and 7870 deletions

5
.github/labeler.yml vendored
View File

@@ -96,6 +96,11 @@ area/hack:
- changed-files: - changed-files:
- any-glob-to-any-file: 'hack/**' - any-glob-to-any-file: 'hack/**'
# Add 'area/history' label to changes in history command
area/history:
- changed-files:
- any-glob-to-any-file: 'commands/history/**'
# Add 'area/tests' label to changes in test files # Add 'area/tests' label to changes in test files
area/tests: area/tests:
- changed-files: - changed-files:

View File

@@ -54,9 +54,9 @@ jobs:
- master - master
- latest - latest
- buildx-stable-1 - buildx-stable-1
- v0.20.1
- v0.19.0
- v0.18.2 - v0.18.2
- v0.17.2
- v0.16.0
worker: worker:
- docker-container - docker-container
- remote - remote
@@ -76,6 +76,16 @@ jobs:
- worker: docker+containerd # same as docker, but with containerd snapshotter - worker: docker+containerd # same as docker, but with containerd snapshotter
pkg: ./tests pkg: ./tests
mode: experimental mode: experimental
- worker: "docker@27.5"
pkg: ./tests
- worker: "docker+containerd@27.5" # same as docker, but with containerd snapshotter
pkg: ./tests
- worker: "docker@27.5"
pkg: ./tests
mode: experimental
- worker: "docker+containerd@27.5" # same as docker, but with containerd snapshotter
pkg: ./tests
mode: experimental
- worker: "docker@26.1" - worker: "docker@26.1"
pkg: ./tests pkg: ./tests
- worker: "docker+containerd@26.1" # same as docker, but with containerd snapshotter - worker: "docker+containerd@26.1" # same as docker, but with containerd snapshotter
@@ -174,6 +184,11 @@ jobs:
env: env:
SKIP_INTEGRATION_TESTS: 1 SKIP_INTEGRATION_TESTS: 1
steps: steps:
-
name: Setup Git config
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- -
name: Checkout name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -243,12 +258,17 @@ jobs:
matrix: matrix:
os: os:
- freebsd - freebsd
- netbsd
- openbsd - openbsd
steps: steps:
- -
name: Prepare name: Prepare
run: | run: |
echo "VAGRANT_FILE=hack/Vagrantfile.${{ matrix.os }}" >> $GITHUB_ENV echo "VAGRANT_FILE=hack/Vagrantfile.${{ matrix.os }}" >> $GITHUB_ENV
# Sets semver Go version to be able to download tarball during vagrant setup
goVersion=$(curl --silent "https://go.dev/dl/?mode=json&include=all" | jq -r '.[].files[].version' | uniq | sed -e 's/go//' | sort -V | grep $GO_VERSION | tail -1)
echo "GO_VERSION=$goVersion" >> $GITHUB_ENV
- -
name: Checkout name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -264,8 +284,10 @@ jobs:
name: Install vagrant name: Install vagrant
run: | run: |
set -x set -x
wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt-get update sudo apt-get update
sudo apt-get install -y libvirt-daemon libvirt-daemon-system vagrant vagrant-libvirt ruby-libvirt sudo apt-get install -y libvirt-dev libvirt-daemon libvirt-daemon-system vagrant vagrant-libvirt ruby-libvirt
sudo systemctl enable --now libvirtd sudo systemctl enable --now libvirtd
sudo chmod a+rw /var/run/libvirt/libvirt-sock sudo chmod a+rw /var/run/libvirt/libvirt-sock
vagrant plugin install vagrant-libvirt vagrant plugin install vagrant-libvirt
@@ -389,6 +411,15 @@ jobs:
- test-unit - test-unit
if: ${{ github.event_name != 'pull_request' && github.repository == 'docker/buildx' }} if: ${{ github.event_name != 'pull_request' && github.repository == 'docker/buildx' }}
steps: steps:
-
name: Free disk space
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1
with:
android: true
dotnet: true
haskell: true
large-packages: true
swap-storage: true
- -
name: Set up QEMU name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3

View File

@@ -77,7 +77,7 @@ jobs:
VENDOR_MODULE: github.com/docker/buildx@${{ env.RELEASE_NAME }} VENDOR_MODULE: github.com/docker/buildx@${{ env.RELEASE_NAME }}
- -
name: Create PR on docs repo name: Create PR on docs repo
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6 uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
with: with:
token: ${{ secrets.GHPAT_DOCS_DISPATCH }} token: ${{ secrets.GHPAT_DOCS_DISPATCH }}
push-to-fork: docker-tools-robot/docker.github.io push-to-fork: docker-tools-robot/docker.github.io

View File

@@ -65,7 +65,7 @@ jobs:
retention-days: 1 retention-days: 1
validate: 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: needs:
- docs-yaml - docs-yaml
with: with:

View File

@@ -29,7 +29,7 @@ env:
SETUP_BUILDX_VERSION: "edge" SETUP_BUILDX_VERSION: "edge"
SETUP_BUILDKIT_IMAGE: "moby/buildkit:latest" SETUP_BUILDKIT_IMAGE: "moby/buildkit:latest"
DESTDIR: "./bin" DESTDIR: "./bin"
K3S_VERSION: "v1.21.2-k3s1" K3S_VERSION: "v1.32.2+k3s1"
jobs: jobs:
build: build:
@@ -65,7 +65,7 @@ jobs:
retention-days: 7 retention-days: 7
driver: driver:
runs-on: ubuntu-20.04 runs-on: ubuntu-24.04
needs: needs:
- build - build
strategy: strategy:
@@ -153,7 +153,7 @@ jobs:
- -
name: Install k3s name: Install k3s
if: matrix.driver == 'kubernetes' if: matrix.driver == 'kubernetes'
uses: crazy-max/.github/.github/actions/install-k3s@fa6141aedf23596fb8bdcceab9cce8dadaa31bd9 uses: crazy-max/.github/.github/actions/install-k3s@7730d1434364d4b9aded32735b078a7ace5ea79a
with: with:
version: ${{ env.K3S_VERSION }} version: ${{ env.K3S_VERSION }}
- -
@@ -215,6 +215,9 @@ jobs:
- -
name: Checkout name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
-
name: Expose GitHub Runtime
uses: crazy-max/ghaction-github-runtime@v3
- -
name: Environment variables name: Environment variables
if: matrix.envs != '' if: matrix.envs != ''

View File

@@ -43,6 +43,9 @@ linters-settings:
# buildkit errdefs package (or vice-versa). # buildkit errdefs package (or vice-versa).
- pkg: "github.com/containerd/errdefs" - pkg: "github.com/containerd/errdefs"
alias: "cerrdefs" alias: "cerrdefs"
# Use a consistent alias to prevent confusion with "github.com/moby/buildkit/client"
- pkg: "github.com/docker/docker/client"
alias: "dockerclient"
- pkg: "github.com/opencontainers/image-spec/specs-go/v1" - pkg: "github.com/opencontainers/image-spec/specs-go/v1"
alias: "ocispecs" alias: "ocispecs"
- pkg: "github.com/opencontainers/go-digest" - pkg: "github.com/opencontainers/go-digest"

View File

@@ -5,20 +5,23 @@ ARG ALPINE_VERSION=3.21
ARG XX_VERSION=1.6.1 ARG XX_VERSION=1.6.1
# for testing # for testing
ARG DOCKER_VERSION=27.4.1 ARG DOCKER_VERSION=28.0.0
ARG DOCKER_VERSION_ALT_27=27.5.1
ARG DOCKER_VERSION_ALT_26=26.1.3 ARG DOCKER_VERSION_ALT_26=26.1.3
ARG DOCKER_CLI_VERSION=${DOCKER_VERSION} ARG DOCKER_CLI_VERSION=${DOCKER_VERSION}
ARG GOTESTSUM_VERSION=v1.12.0 ARG GOTESTSUM_VERSION=v1.12.0
ARG REGISTRY_VERSION=2.8.3 ARG REGISTRY_VERSION=2.8.3
ARG BUILDKIT_VERSION=v0.18.2 ARG BUILDKIT_VERSION=v0.20.1
ARG UNDOCK_VERSION=0.9.0 ARG UNDOCK_VERSION=0.9.0
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS golatest FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS golatest
FROM moby/moby-bin:$DOCKER_VERSION AS docker-engine FROM moby/moby-bin:$DOCKER_VERSION AS docker-engine
FROM dockereng/cli-bin:$DOCKER_CLI_VERSION AS docker-cli FROM dockereng/cli-bin:$DOCKER_CLI_VERSION AS docker-cli
FROM moby/moby-bin:$DOCKER_VERSION_ALT_26 AS docker-engine-alt FROM moby/moby-bin:$DOCKER_VERSION_ALT_27 AS docker-engine-alt27
FROM dockereng/cli-bin:$DOCKER_VERSION_ALT_26 AS docker-cli-alt FROM moby/moby-bin:$DOCKER_VERSION_ALT_26 AS docker-engine-alt26
FROM dockereng/cli-bin:$DOCKER_VERSION_ALT_27 AS docker-cli-alt27
FROM dockereng/cli-bin:$DOCKER_VERSION_ALT_26 AS docker-cli-alt26
FROM registry:$REGISTRY_VERSION AS registry FROM registry:$REGISTRY_VERSION AS registry
FROM moby/buildkit:$BUILDKIT_VERSION AS buildkit FROM moby/buildkit:$BUILDKIT_VERSION AS buildkit
FROM crazymax/undock:$UNDOCK_VERSION AS undock FROM crazymax/undock:$UNDOCK_VERSION AS undock
@@ -102,6 +105,7 @@ COPY --link --from=buildx-build /usr/bin/docker-buildx /buildx
FROM binaries-unix AS binaries-darwin FROM binaries-unix AS binaries-darwin
FROM binaries-unix AS binaries-freebsd FROM binaries-unix AS binaries-freebsd
FROM binaries-unix AS binaries-linux FROM binaries-unix AS binaries-linux
FROM binaries-unix AS binaries-netbsd
FROM binaries-unix AS binaries-openbsd FROM binaries-unix AS binaries-openbsd
FROM scratch AS binaries-windows FROM scratch AS binaries-windows
@@ -127,13 +131,15 @@ COPY --link --from=gotestsum /out /usr/bin/
COPY --link --from=registry /bin/registry /usr/bin/ COPY --link --from=registry /bin/registry /usr/bin/
COPY --link --from=docker-engine / /usr/bin/ COPY --link --from=docker-engine / /usr/bin/
COPY --link --from=docker-cli / /usr/bin/ COPY --link --from=docker-cli / /usr/bin/
COPY --link --from=docker-engine-alt / /opt/docker-alt-26/ COPY --link --from=docker-engine-alt27 / /opt/docker-alt-27/
COPY --link --from=docker-cli-alt / /opt/docker-alt-26/ COPY --link --from=docker-engine-alt26 / /opt/docker-alt-26/
COPY --link --from=docker-cli-alt27 / /opt/docker-alt-27/
COPY --link --from=docker-cli-alt26 / /opt/docker-alt-26/
COPY --link --from=buildkit /usr/bin/buildkitd /usr/bin/ COPY --link --from=buildkit /usr/bin/buildkitd /usr/bin/
COPY --link --from=buildkit /usr/bin/buildctl /usr/bin/ COPY --link --from=buildkit /usr/bin/buildctl /usr/bin/
COPY --link --from=undock /usr/local/bin/undock /usr/bin/ COPY --link --from=undock /usr/local/bin/undock /usr/bin/
COPY --link --from=binaries /buildx /usr/bin/ COPY --link --from=binaries /buildx /usr/bin/
ENV TEST_DOCKER_EXTRA="docker@26.1=/opt/docker-alt-26" ENV TEST_DOCKER_EXTRA="docker@27.5=/opt/docker-alt-27,docker@26.1=/opt/docker-alt-26"
FROM integration-test-base AS integration-test FROM integration-test-base AS integration-test
COPY . . COPY . .

View File

@@ -27,9 +27,7 @@ import (
"github.com/moby/buildkit/client" "github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/session/auth/authprovider" "github.com/moby/buildkit/session/auth/authprovider"
"github.com/moby/buildkit/util/entitlements"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/tonistiigi/go-csvvalue"
"github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/convert" "github.com/zclconf/go-cty/cty/convert"
) )
@@ -47,6 +45,7 @@ type File struct {
type Override struct { type Override struct {
Value string Value string
ArrValue []string ArrValue []string
Append bool
} }
func defaultFilenames() []string { func defaultFilenames() []string {
@@ -487,11 +486,9 @@ func (c Config) loadLinks(name string, t *Target, m map[string]*Target, o map[st
if target == name { if target == name {
return errors.Errorf("target %s cannot link to itself", target) return errors.Errorf("target %s cannot link to itself", target)
} }
for _, v := range visited { if slices.Contains(visited, target) {
if v == target {
return errors.Errorf("infinite loop from %s to %s", name, target) return errors.Errorf("infinite loop from %s to %s", name, target)
} }
}
t2, ok := m[target] t2, ok := m[target]
if !ok { if !ok {
var err error var err error
@@ -530,9 +527,12 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
m := map[string]map[string]Override{} m := map[string]map[string]Override{}
for _, v := range v { for _, v := range v {
parts := strings.SplitN(v, "=", 2) parts := strings.SplitN(v, "=", 2)
keys := strings.SplitN(parts[0], ".", 3)
skey := strings.TrimSuffix(parts[0], "+")
appendTo := strings.HasSuffix(parts[0], "+")
keys := strings.SplitN(skey, ".", 3)
if len(keys) < 2 { if len(keys) < 2 {
return nil, errors.Errorf("invalid override key %s, expected target.name", parts[0]) return nil, errors.Errorf("invalid override key %s, expected target.name", skey)
} }
pattern := keys[0] pattern := keys[0]
@@ -545,8 +545,7 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
return nil, err return nil, err
} }
kk := strings.SplitN(parts[0], ".", 2) okey := strings.Join(keys[1:], ".")
for _, name := range names { for _, name := range names {
t, ok := m[name] t, ok := m[name]
if !ok { if !ok {
@@ -554,12 +553,15 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
m[name] = t m[name] = t
} }
o := t[kk[1]] override := t[okey]
// 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] { switch keys[1] {
case "output", "cache-to", "cache-from", "tags", "platform", "secrets", "ssh", "attest", "entitlements", "network": case "output", "cache-to", "cache-from", "tags", "platform", "secrets", "ssh", "attest", "entitlements", "network", "annotations":
if len(parts) == 2 { if len(parts) == 2 {
o.ArrValue = append(o.ArrValue, parts[1]) override.Append = appendTo
override.ArrValue = append(override.ArrValue, parts[1])
} }
case "args": case "args":
if len(keys) != 3 { if len(keys) != 3 {
@@ -570,7 +572,7 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
if !ok { if !ok {
continue continue
} }
o.Value = v override.Value = v
} }
fallthrough fallthrough
case "contexts": case "contexts":
@@ -580,11 +582,11 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
fallthrough fallthrough
default: default:
if len(parts) == 2 { if len(parts) == 2 {
o.Value = parts[1] override.Value = parts[1]
} }
} }
t[kk[1]] = o t[okey] = override
} }
} }
return m, nil return m, nil
@@ -861,6 +863,8 @@ func (t *Target) Merge(t2 *Target) {
} }
func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementConf) error { func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementConf) error {
// IMPORTANT: if you add more fields here, do not forget to update
// docs/bake-reference.md and https://docs.docker.com/build/bake/overrides/
for key, o := range overrides { for key, o := range overrides {
value := o.Value value := o.Value
keys := strings.SplitN(key, ".", 2) keys := strings.SplitN(key, ".", 2)
@@ -894,13 +898,21 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
} }
t.Labels[keys[1]] = &value t.Labels[keys[1]] = &value
case "tags": case "tags":
if o.Append {
t.Tags = append(t.Tags, o.ArrValue...)
} else {
t.Tags = o.ArrValue t.Tags = o.ArrValue
}
case "cache-from": case "cache-from":
cacheFrom, err := parseCacheArrValues(o.ArrValue) cacheFrom, err := buildflags.ParseCacheEntry(o.ArrValue)
if err != nil { if err != nil {
return err return err
} }
if o.Append {
t.CacheFrom = t.CacheFrom.Merge(cacheFrom)
} else {
t.CacheFrom = cacheFrom t.CacheFrom = cacheFrom
}
for _, c := range t.CacheFrom { for _, c := range t.CacheFrom {
if c.Type == "local" { if c.Type == "local" {
if v, ok := c.Attrs["src"]; ok { if v, ok := c.Attrs["src"]; ok {
@@ -909,11 +921,15 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
} }
} }
case "cache-to": case "cache-to":
cacheTo, err := parseCacheArrValues(o.ArrValue) cacheTo, err := buildflags.ParseCacheEntry(o.ArrValue)
if err != nil { if err != nil {
return err return err
} }
if o.Append {
t.CacheTo = t.CacheTo.Merge(cacheTo)
} else {
t.CacheTo = cacheTo t.CacheTo = cacheTo
}
for _, c := range t.CacheTo { for _, c := range t.CacheTo {
if c.Type == "local" { if c.Type == "local" {
if v, ok := c.Attrs["dest"]; ok { if v, ok := c.Attrs["dest"]; ok {
@@ -930,7 +946,11 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
if err != nil { if err != nil {
return errors.Wrap(err, "invalid value for outputs") return errors.Wrap(err, "invalid value for outputs")
} }
if o.Append {
t.Secrets = t.Secrets.Merge(secrets)
} else {
t.Secrets = secrets t.Secrets = secrets
}
for _, s := range t.Secrets { for _, s := range t.Secrets {
if s.FilePath != "" { if s.FilePath != "" {
ent.FSRead = append(ent.FSRead, s.FilePath) ent.FSRead = append(ent.FSRead, s.FilePath)
@@ -941,18 +961,30 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
if err != nil { if err != nil {
return errors.Wrap(err, "invalid value for outputs") return errors.Wrap(err, "invalid value for outputs")
} }
if o.Append {
t.SSH = t.SSH.Merge(ssh)
} else {
t.SSH = ssh t.SSH = ssh
}
for _, s := range t.SSH { for _, s := range t.SSH {
ent.FSRead = append(ent.FSRead, s.Paths...) ent.FSRead = append(ent.FSRead, s.Paths...)
} }
case "platform": case "platform":
if o.Append {
t.Platforms = append(t.Platforms, o.ArrValue...)
} else {
t.Platforms = o.ArrValue t.Platforms = o.ArrValue
}
case "output": case "output":
outputs, err := parseArrValue[buildflags.ExportEntry](o.ArrValue) outputs, err := parseArrValue[buildflags.ExportEntry](o.ArrValue)
if err != nil { if err != nil {
return errors.Wrap(err, "invalid value for outputs") return errors.Wrap(err, "invalid value for outputs")
} }
if o.Append {
t.Outputs = t.Outputs.Merge(outputs)
} else {
t.Outputs = outputs t.Outputs = outputs
}
for _, o := range t.Outputs { for _, o := range t.Outputs {
if o.Destination != "" { if o.Destination != "" {
ent.FSWrite = append(ent.FSWrite, o.Destination) ent.FSWrite = append(ent.FSWrite, o.Destination)
@@ -982,11 +1014,19 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
} }
t.NoCache = &noCache t.NoCache = &noCache
case "no-cache-filter": case "no-cache-filter":
if o.Append {
t.NoCacheFilter = append(t.NoCacheFilter, o.ArrValue...)
} else {
t.NoCacheFilter = o.ArrValue t.NoCacheFilter = o.ArrValue
}
case "shm-size": case "shm-size":
t.ShmSize = &value t.ShmSize = &value
case "ulimits": case "ulimits":
if o.Append {
t.Ulimits = append(t.Ulimits, o.ArrValue...)
} else {
t.Ulimits = o.ArrValue t.Ulimits = o.ArrValue
}
case "network": case "network":
t.NetworkMode = &value t.NetworkMode = &value
case "pull": case "pull":
@@ -1127,7 +1167,9 @@ func (t *Target) GetName(ectx *hcl.EvalContext, block *hcl.Block, loadDeps func(
func TargetsToBuildOpt(m map[string]*Target, inp *Input) (map[string]build.Options, error) { func TargetsToBuildOpt(m map[string]*Target, inp *Input) (map[string]build.Options, error) {
// make sure local credentials are loaded multiple times for different targets // make sure local credentials are loaded multiple times for different targets
dockerConfig := config.LoadDefaultConfigFile(os.Stderr) dockerConfig := config.LoadDefaultConfigFile(os.Stderr)
authProvider := authprovider.NewDockerAuthProvider(dockerConfig, nil) authProvider := authprovider.NewDockerAuthProvider(authprovider.DockerAuthProviderConfig{
ConfigFile: dockerConfig,
})
m2 := make(map[string]build.Options, len(m)) m2 := make(map[string]build.Options, len(m))
for k, v := range m { for k, v := range m {
@@ -1429,9 +1471,7 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
} }
bo.Ulimits = ulimits bo.Ulimits = ulimits
for _, ent := range t.Entitlements { bo.Allow = append(bo.Allow, t.Entitlements...)
bo.Allow = append(bo.Allow, entitlements.Entitlement(ent))
}
return bo, nil return bo, nil
} }
@@ -1581,37 +1621,3 @@ func parseArrValue[T any, PT arrValue[T]](s []string) ([]*T, error) {
} }
return outputs, nil 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
}

View File

@@ -9,6 +9,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/docker/buildx/util/buildflags"
"github.com/moby/buildkit/util/entitlements" "github.com/moby/buildkit/util/entitlements"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -33,6 +34,18 @@ target "webapp" {
args = { args = {
VAR_BOTH = "webapp" VAR_BOTH = "webapp"
} }
annotations = [
"index,manifest:org.opencontainers.image.authors=dvdksn"
]
attest = [
"type=provenance,mode=max"
]
platforms = [
"linux/amd64"
]
secret = [
"id=FOO,env=FOO"
]
inherits = ["webDEP"] inherits = ["webDEP"]
}`), }`),
} }
@@ -114,6 +127,31 @@ target "webapp" {
}) })
}) })
t.Run("AnnotationsOverrides", func(t *testing.T) {
t.Parallel()
m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.annotations=index,manifest:org.opencontainers.image.vendor=docker"}, nil, &EntitlementConf{})
require.NoError(t, err)
require.Equal(t, []string{"index,manifest:org.opencontainers.image.authors=dvdksn", "index,manifest:org.opencontainers.image.vendor=docker"}, m["webapp"].Annotations)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"webapp"}, g["default"].Targets)
})
t.Run("AttestOverride", func(t *testing.T) {
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.attest=type=sbom"}, nil, &EntitlementConf{})
require.NoError(t, err)
require.Len(t, m["webapp"].Attest, 2)
require.Equal(t, "provenance", m["webapp"].Attest[0].Type)
require.Equal(t, "sbom", m["webapp"].Attest[1].Type)
})
t.Run("AttestAppend", func(t *testing.T) {
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.attest+=type=sbom"}, nil, &EntitlementConf{})
require.NoError(t, err)
require.Len(t, m["webapp"].Attest, 2)
require.Equal(t, "provenance", m["webapp"].Attest[0].Type)
require.Equal(t, "sbom", m["webapp"].Attest[1].Type)
})
t.Run("ContextOverride", func(t *testing.T) { t.Run("ContextOverride", func(t *testing.T) {
t.Parallel() t.Parallel()
_, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.context"}, nil, &EntitlementConf{}) _, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.context"}, nil, &EntitlementConf{})
@@ -135,6 +173,49 @@ target "webapp" {
require.Equal(t, []string{"webapp"}, g["default"].Targets) require.Equal(t, []string{"webapp"}, g["default"].Targets)
}) })
t.Run("PlatformOverride", func(t *testing.T) {
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.platform=linux/arm64"}, nil, &EntitlementConf{})
require.NoError(t, err)
require.Equal(t, []string{"linux/arm64"}, m["webapp"].Platforms)
})
t.Run("PlatformAppend", func(t *testing.T) {
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.platform+=linux/arm64"}, nil, &EntitlementConf{})
require.NoError(t, err)
require.Equal(t, []string{"linux/amd64", "linux/arm64"}, m["webapp"].Platforms)
})
t.Run("PlatformAppendMulti", func(t *testing.T) {
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.platform+=linux/arm64", "webapp.platform+=linux/riscv64"}, nil, &EntitlementConf{})
require.NoError(t, err)
require.Equal(t, []string{"linux/amd64", "linux/arm64", "linux/riscv64"}, m["webapp"].Platforms)
})
t.Run("PlatformAppendMultiLastOverride", func(t *testing.T) {
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.platform+=linux/arm64", "webapp.platform=linux/riscv64"}, nil, &EntitlementConf{})
require.NoError(t, err)
require.Equal(t, []string{"linux/arm64", "linux/riscv64"}, m["webapp"].Platforms)
})
t.Run("SecretsOverride", func(t *testing.T) {
t.Setenv("FOO", "foo")
t.Setenv("BAR", "bar")
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.secrets=id=BAR,env=BAR"}, nil, &EntitlementConf{})
require.NoError(t, err)
require.Len(t, m["webapp"].Secrets, 1)
require.Equal(t, "BAR", m["webapp"].Secrets[0].ID)
})
t.Run("SecretsAppend", func(t *testing.T) {
t.Setenv("FOO", "foo")
t.Setenv("BAR", "bar")
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.secrets+=id=BAR,env=BAR"}, nil, &EntitlementConf{})
require.NoError(t, err)
require.Len(t, m["webapp"].Secrets, 2)
require.Equal(t, "FOO", m["webapp"].Secrets[0].ID)
require.Equal(t, "BAR", m["webapp"].Secrets[1].ID)
})
t.Run("ShmSizeOverride", func(t *testing.T) { t.Run("ShmSizeOverride", func(t *testing.T) {
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.shm-size=256m"}, nil, &EntitlementConf{}) m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.shm-size=256m"}, nil, &EntitlementConf{})
require.NoError(t, err) require.NoError(t, err)
@@ -1759,6 +1840,27 @@ func TestAnnotations(t *testing.T) {
require.Equal(t, "bar", bo["app"].Exports[0].Attrs["annotation-manifest[linux/amd64].foo"]) 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) { func TestHCLEntitlements(t *testing.T) {
fp := File{ fp := File{
Name: "docker-bake.hcl", Name: "docker-bake.hcl",
@@ -1784,8 +1886,8 @@ func TestHCLEntitlements(t *testing.T) {
require.Equal(t, "network.host", m["app"].Entitlements[1]) require.Equal(t, "network.host", m["app"].Entitlements[1])
require.Len(t, bo["app"].Allow, 2) require.Len(t, bo["app"].Allow, 2)
require.Equal(t, entitlements.EntitlementSecurityInsecure, bo["app"].Allow[0]) require.Equal(t, entitlements.EntitlementSecurityInsecure.String(), bo["app"].Allow[0])
require.Equal(t, entitlements.EntitlementNetworkHost, bo["app"].Allow[1]) require.Equal(t, entitlements.EntitlementNetworkHost.String(), bo["app"].Allow[1])
} }
func TestEntitlementsForNetHostCompose(t *testing.T) { func TestEntitlementsForNetHostCompose(t *testing.T) {
@@ -1824,7 +1926,7 @@ func TestEntitlementsForNetHostCompose(t *testing.T) {
require.Equal(t, "host", *m["app"].NetworkMode) require.Equal(t, "host", *m["app"].NetworkMode)
require.Len(t, bo["app"].Allow, 1) require.Len(t, bo["app"].Allow, 1)
require.Equal(t, entitlements.EntitlementNetworkHost, bo["app"].Allow[0]) require.Equal(t, entitlements.EntitlementNetworkHost.String(), bo["app"].Allow[0])
require.Equal(t, "host", bo["app"].NetworkMode) require.Equal(t, "host", bo["app"].NetworkMode)
} }
@@ -1855,7 +1957,7 @@ func TestEntitlementsForNetHost(t *testing.T) {
require.Equal(t, "host", *m["app"].NetworkMode) require.Equal(t, "host", *m["app"].NetworkMode)
require.Len(t, bo["app"].Allow, 1) require.Len(t, bo["app"].Allow, 1)
require.Equal(t, entitlements.EntitlementNetworkHost, bo["app"].Allow[0]) require.Equal(t, entitlements.EntitlementNetworkHost.String(), bo["app"].Allow[0])
require.Equal(t, "host", bo["app"].NetworkMode) require.Equal(t, "host", bo["app"].NetworkMode)
} }

View File

@@ -145,12 +145,12 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf
labels[k] = &v labels[k] = &v
} }
cacheFrom, err := parseCacheArrValues(s.Build.CacheFrom) cacheFrom, err := buildflags.ParseCacheEntry(s.Build.CacheFrom)
if err != nil { if err != nil {
return nil, err return nil, err
} }
cacheTo, err := parseCacheArrValues(s.Build.CacheTo) cacheTo, err := buildflags.ParseCacheEntry(s.Build.CacheTo)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -315,7 +315,7 @@ type (
stringArray []string stringArray []string
) )
func (sa *stringArray) UnmarshalYAML(unmarshal func(interface{}) error) error { func (sa *stringArray) UnmarshalYAML(unmarshal func(any) error) error {
var multi []string var multi []string
err := unmarshal(&multi) err := unmarshal(&multi)
if err != nil { if err != nil {
@@ -332,7 +332,7 @@ func (sa *stringArray) UnmarshalYAML(unmarshal func(interface{}) error) error {
// composeExtTarget converts Compose build extension x-bake to bake Target // composeExtTarget converts Compose build extension x-bake to bake Target
// https://github.com/compose-spec/compose-spec/blob/master/spec.md#extension // https://github.com/compose-spec/compose-spec/blob/master/spec.md#extension
func (t *Target) composeExtTarget(exts map[string]interface{}) error { func (t *Target) composeExtTarget(exts map[string]any) error {
var xb xbake var xb xbake
ext, ok := exts["x-bake"] ext, ok := exts["x-bake"]
@@ -349,14 +349,14 @@ func (t *Target) composeExtTarget(exts map[string]interface{}) error {
t.Tags = dedupSlice(append(t.Tags, xb.Tags...)) t.Tags = dedupSlice(append(t.Tags, xb.Tags...))
} }
if len(xb.CacheFrom) > 0 { if len(xb.CacheFrom) > 0 {
cacheFrom, err := parseCacheArrValues(xb.CacheFrom) cacheFrom, err := buildflags.ParseCacheEntry(xb.CacheFrom)
if err != nil { if err != nil {
return err return err
} }
t.CacheFrom = t.CacheFrom.Merge(cacheFrom) t.CacheFrom = t.CacheFrom.Merge(cacheFrom)
} }
if len(xb.CacheTo) > 0 { if len(xb.CacheTo) > 0 {
cacheTo, err := parseCacheArrValues(xb.CacheTo) cacheTo, err := buildflags.ParseCacheEntry(xb.CacheTo)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -20,6 +20,7 @@ import (
"github.com/moby/buildkit/util/entitlements" "github.com/moby/buildkit/util/entitlements"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/tonistiigi/go-csvvalue"
) )
type EntitlementKey string type EntitlementKey string
@@ -27,6 +28,7 @@ type EntitlementKey string
const ( const (
EntitlementKeyNetworkHost EntitlementKey = "network.host" EntitlementKeyNetworkHost EntitlementKey = "network.host"
EntitlementKeySecurityInsecure EntitlementKey = "security.insecure" EntitlementKeySecurityInsecure EntitlementKey = "security.insecure"
EntitlementKeyDevice EntitlementKey = "device"
EntitlementKeyFSRead EntitlementKey = "fs.read" EntitlementKeyFSRead EntitlementKey = "fs.read"
EntitlementKeyFSWrite EntitlementKey = "fs.write" EntitlementKeyFSWrite EntitlementKey = "fs.write"
EntitlementKeyFS EntitlementKey = "fs" EntitlementKeyFS EntitlementKey = "fs"
@@ -39,6 +41,7 @@ const (
type EntitlementConf struct { type EntitlementConf struct {
NetworkHost bool NetworkHost bool
SecurityInsecure bool SecurityInsecure bool
Devices *EntitlementsDevicesConf
FSRead []string FSRead []string
FSWrite []string FSWrite []string
ImagePush []string ImagePush []string
@@ -46,6 +49,11 @@ type EntitlementConf struct {
SSH bool SSH bool
} }
type EntitlementsDevicesConf struct {
All bool
Devices map[string]struct{}
}
func ParseEntitlements(in []string) (EntitlementConf, error) { func ParseEntitlements(in []string) (EntitlementConf, error) {
var conf EntitlementConf var conf EntitlementConf
for _, e := range in { for _, e := range in {
@@ -59,6 +67,22 @@ func ParseEntitlements(in []string) (EntitlementConf, error) {
default: default:
k, v, _ := strings.Cut(e, "=") k, v, _ := strings.Cut(e, "=")
switch k { switch k {
case string(EntitlementKeyDevice):
if v == "" {
conf.Devices = &EntitlementsDevicesConf{All: true}
continue
}
fields, err := csvvalue.Fields(v, nil)
if err != nil {
return EntitlementConf{}, errors.Wrapf(err, "failed to parse device entitlement %q", v)
}
if conf.Devices == nil {
conf.Devices = &EntitlementsDevicesConf{}
}
if conf.Devices.Devices == nil {
conf.Devices.Devices = make(map[string]struct{}, 0)
}
conf.Devices.Devices[fields[0]] = struct{}{}
case string(EntitlementKeyFSRead): case string(EntitlementKeyFSRead):
conf.FSRead = append(conf.FSRead, v) conf.FSRead = append(conf.FSRead, v)
case string(EntitlementKeyFSWrite): case string(EntitlementKeyFSWrite):
@@ -95,12 +119,34 @@ func (c EntitlementConf) Validate(m map[string]build.Options) (EntitlementConf,
func (c EntitlementConf) check(bo build.Options, expected *EntitlementConf) error { func (c EntitlementConf) check(bo build.Options, expected *EntitlementConf) error {
for _, e := range bo.Allow { for _, e := range bo.Allow {
k, rest, _ := strings.Cut(e, "=")
switch k {
case entitlements.EntitlementDevice.String():
if rest == "" {
if c.Devices == nil || !c.Devices.All {
expected.Devices = &EntitlementsDevicesConf{All: true}
}
continue
}
fields, err := csvvalue.Fields(rest, nil)
if err != nil {
return errors.Wrapf(err, "failed to parse device entitlement %q", rest)
}
if expected.Devices == nil {
expected.Devices = &EntitlementsDevicesConf{}
}
if expected.Devices.Devices == nil {
expected.Devices.Devices = make(map[string]struct{}, 0)
}
expected.Devices.Devices[fields[0]] = struct{}{}
}
switch e { switch e {
case entitlements.EntitlementNetworkHost: case entitlements.EntitlementNetworkHost.String():
if !c.NetworkHost { if !c.NetworkHost {
expected.NetworkHost = true expected.NetworkHost = true
} }
case entitlements.EntitlementSecurityInsecure: case entitlements.EntitlementSecurityInsecure.String():
if !c.SecurityInsecure { if !c.SecurityInsecure {
expected.SecurityInsecure = true expected.SecurityInsecure = true
} }
@@ -187,6 +233,18 @@ func (c EntitlementConf) Prompt(ctx context.Context, isRemote bool, out io.Write
flags = append(flags, string(EntitlementKeySecurityInsecure)) flags = append(flags, string(EntitlementKeySecurityInsecure))
} }
if c.Devices != nil {
if c.Devices.All {
msgs = append(msgs, " - Access to CDI devices")
flags = append(flags, string(EntitlementKeyDevice))
} else {
for d := range c.Devices.Devices {
msgs = append(msgs, fmt.Sprintf(" - Access to device %s", d))
flags = append(flags, string(EntitlementKeyDevice)+"="+d)
}
}
}
if c.SSH { if c.SSH {
msgsFS = append(msgsFS, " - Forwarding default SSH agent socket") msgsFS = append(msgsFS, " - Forwarding default SSH agent socket")
flagsFS = append(flagsFS, string(EntitlementKeySSH)) flagsFS = append(flagsFS, string(EntitlementKeySSH))
@@ -248,7 +306,7 @@ func (c EntitlementConf) Prompt(ctx context.Context, isRemote bool, out io.Write
fmt.Fprintf(out, "\nPass %q to grant requested privileges.\n", strings.Join(slices.Concat(flags, flagsFS), " ")) fmt.Fprintf(out, "\nPass %q to grant requested privileges.\n", strings.Join(slices.Concat(flags, flagsFS), " "))
} }
args := append([]string(nil), os.Args...) args := slices.Clone(os.Args)
if v, ok := os.LookupEnv("DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND"); ok && v != "" { if v, ok := os.LookupEnv("DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND"); ok && v != "" {
args[0] = v args[0] = v
} }

View File

@@ -208,8 +208,8 @@ func TestValidateEntitlements(t *testing.T) {
{ {
name: "NetworkHostMissing", name: "NetworkHostMissing",
opt: build.Options{ opt: build.Options{
Allow: []entitlements.Entitlement{ Allow: []string{
entitlements.EntitlementNetworkHost, entitlements.EntitlementNetworkHost.String(),
}, },
}, },
expected: EntitlementConf{ expected: EntitlementConf{
@@ -223,8 +223,8 @@ func TestValidateEntitlements(t *testing.T) {
NetworkHost: true, NetworkHost: true,
}, },
opt: build.Options{ opt: build.Options{
Allow: []entitlements.Entitlement{ Allow: []string{
entitlements.EntitlementNetworkHost, entitlements.EntitlementNetworkHost.String(),
}, },
}, },
expected: EntitlementConf{ expected: EntitlementConf{
@@ -234,9 +234,9 @@ func TestValidateEntitlements(t *testing.T) {
{ {
name: "SecurityAndNetworkHostMissing", name: "SecurityAndNetworkHostMissing",
opt: build.Options{ opt: build.Options{
Allow: []entitlements.Entitlement{ Allow: []string{
entitlements.EntitlementNetworkHost, entitlements.EntitlementNetworkHost.String(),
entitlements.EntitlementSecurityInsecure, entitlements.EntitlementSecurityInsecure.String(),
}, },
}, },
expected: EntitlementConf{ expected: EntitlementConf{
@@ -251,9 +251,9 @@ func TestValidateEntitlements(t *testing.T) {
NetworkHost: true, NetworkHost: true,
}, },
opt: build.Options{ opt: build.Options{
Allow: []entitlements.Entitlement{ Allow: []string{
entitlements.EntitlementNetworkHost, entitlements.EntitlementNetworkHost.String(),
entitlements.EntitlementSecurityInsecure, entitlements.EntitlementSecurityInsecure.String(),
}, },
}, },
expected: EntitlementConf{ expected: EntitlementConf{

View File

@@ -2,8 +2,10 @@ package bake
import ( import (
"reflect" "reflect"
"regexp"
"testing" "testing"
hcl "github.com/hashicorp/hcl/v2"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -606,7 +608,7 @@ func TestHCLAttrsCapsuleType(t *testing.T) {
target "app" { target "app" {
attest = [ attest = [
{ type = "provenance", mode = "max" }, { type = "provenance", mode = "max" },
"type=sbom,disabled=true", "type=sbom,disabled=true,generator=foo,\"ENV1=bar,baz\",ENV2=hello",
] ]
cache-from = [ cache-from = [
@@ -639,7 +641,7 @@ func TestHCLAttrsCapsuleType(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(c.Targets)) require.Equal(t, 1, len(c.Targets))
require.Equal(t, []string{"type=provenance,mode=max", "type=sbom,disabled=true"}, stringify(c.Targets[0].Attest)) require.Equal(t, []string{"type=provenance,mode=max", "type=sbom,disabled=true,\"ENV1=bar,baz\",ENV2=hello,generator=foo"}, stringify(c.Targets[0].Attest))
require.Equal(t, []string{"type=local,dest=../out", "type=oci,dest=../out.tar"}, stringify(c.Targets[0].Outputs)) require.Equal(t, []string{"type=local,dest=../out", "type=oci,dest=../out.tar"}, stringify(c.Targets[0].Outputs))
require.Equal(t, []string{"type=local,src=path/to/cache", "user/app:cache"}, stringify(c.Targets[0].CacheFrom)) require.Equal(t, []string{"type=local,src=path/to/cache", "user/app:cache"}, stringify(c.Targets[0].CacheFrom))
require.Equal(t, []string{"type=local,dest=path/to/cache"}, stringify(c.Targets[0].CacheTo)) require.Equal(t, []string{"type=local,dest=path/to/cache"}, stringify(c.Targets[0].CacheTo))
@@ -647,7 +649,7 @@ func TestHCLAttrsCapsuleType(t *testing.T) {
require.Equal(t, []string{"default", "key=path/to/key"}, stringify(c.Targets[0].SSH)) require.Equal(t, []string{"default", "key=path/to/key"}, stringify(c.Targets[0].SSH))
} }
func TestHCLAttrsCapsuleTypeVars(t *testing.T) { func TestHCLAttrsCapsuleType_ObjectVars(t *testing.T) {
dt := []byte(` dt := []byte(`
variable "foo" { variable "foo" {
default = "bar" default = "bar"
@@ -716,6 +718,52 @@ func TestHCLAttrsCapsuleTypeVars(t *testing.T) {
require.Equal(t, []string{"id=oci,src=/local/secret"}, stringify(web.Secrets)) require.Equal(t, []string{"id=oci,src=/local/secret"}, stringify(web.Secrets))
} }
func TestHCLAttrsCapsuleType_MissingVars(t *testing.T) {
dt := []byte(`
target "app" {
attest = [
"type=sbom,disabled=${SBOM}",
]
cache-from = [
{ type = "registry", ref = "user/app:${FOO1}" },
"type=local,src=path/to/cache:${FOO2}",
]
cache-to = [
{ type = "local", dest = "path/to/${BAR}" },
]
output = [
{ type = "oci", dest = "../${OUTPUT}.tar" },
]
secret = [
{ id = "mysecret", src = "/local/${SECRET}" },
]
ssh = [
{ id = "key", paths = ["path/to/${SSH_KEY}"] },
]
}
`)
var diags hcl.Diagnostics
_, err := ParseFile(dt, "docker-bake.hcl")
require.ErrorAs(t, err, &diags)
re := regexp.MustCompile(`There is no variable named "([\w\d_]+)"`)
var actual []string
for _, diag := range diags {
if m := re.FindStringSubmatch(diag.Error()); m != nil {
actual = append(actual, m[1])
}
}
require.ElementsMatch(t,
[]string{"SBOM", "FOO1", "FOO2", "BAR", "OUTPUT", "SECRET", "SSH_KEY"},
actual)
}
func TestHCLMultiFileAttrs(t *testing.T) { func TestHCLMultiFileAttrs(t *testing.T) {
dt := []byte(` dt := []byte(`
variable "FOO" { variable "FOO" {
@@ -1597,7 +1645,7 @@ func TestHCLIndexOfFunc(t *testing.T) {
require.Empty(t, c.Targets[1].Tags[1]) require.Empty(t, c.Targets[1].Tags[1])
} }
func ptrstr(s interface{}) *string { func ptrstr(s any) *string {
var n *string var n *string
if reflect.ValueOf(s).Kind() == reflect.String { if reflect.ValueOf(s).Kind() == reflect.String {
ss := s.(string) ss := s.(string)

View File

@@ -15,11 +15,11 @@ import (
// DecodeOptions allows customizing sections of the decoding process. // DecodeOptions allows customizing sections of the decoding process.
type DecodeOptions struct { type DecodeOptions struct {
ImpliedType func(gv interface{}) (cty.Type, error) ImpliedType func(gv any) (cty.Type, error)
Convert func(in cty.Value, want cty.Type) (cty.Value, error) Convert func(in cty.Value, want cty.Type) (cty.Value, error)
} }
func (o DecodeOptions) DecodeBody(body hcl.Body, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics { func (o DecodeOptions) DecodeBody(body hcl.Body, ctx *hcl.EvalContext, val any) hcl.Diagnostics {
o = o.withDefaults() o = o.withDefaults()
rv := reflect.ValueOf(val) rv := reflect.ValueOf(val)
@@ -46,7 +46,7 @@ func (o DecodeOptions) DecodeBody(body hcl.Body, ctx *hcl.EvalContext, val inter
// are returned then the given value may have been partially-populated but // are returned then the given value may have been partially-populated but
// may still be accessed by a careful caller for static analysis and editor // may still be accessed by a careful caller for static analysis and editor
// integration use-cases. // integration use-cases.
func DecodeBody(body hcl.Body, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics { func DecodeBody(body hcl.Body, ctx *hcl.EvalContext, val any) hcl.Diagnostics {
return DecodeOptions{}.DecodeBody(body, ctx, val) return DecodeOptions{}.DecodeBody(body, ctx, val)
} }
@@ -282,7 +282,7 @@ func (o DecodeOptions) decodeBlockToValue(block *hcl.Block, ctx *hcl.EvalContext
return diags return diags
} }
func (o DecodeOptions) DecodeExpression(expr hcl.Expression, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics { func (o DecodeOptions) DecodeExpression(expr hcl.Expression, ctx *hcl.EvalContext, val any) hcl.Diagnostics {
o = o.withDefaults() o = o.withDefaults()
srcVal, diags := expr.Value(ctx) srcVal, diags := expr.Value(ctx)
@@ -332,7 +332,7 @@ func (o DecodeOptions) DecodeExpression(expr hcl.Expression, ctx *hcl.EvalContex
// are returned then the given value may have been partially-populated but // are returned then the given value may have been partially-populated but
// may still be accessed by a careful caller for static analysis and editor // may still be accessed by a careful caller for static analysis and editor
// integration use-cases. // integration use-cases.
func DecodeExpression(expr hcl.Expression, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics { func DecodeExpression(expr hcl.Expression, ctx *hcl.EvalContext, val any) hcl.Diagnostics {
return DecodeOptions{}.DecodeExpression(expr, ctx, val) return DecodeOptions{}.DecodeExpression(expr, ctx, val)
} }

View File

@@ -16,8 +16,8 @@ import (
) )
func TestDecodeBody(t *testing.T) { func TestDecodeBody(t *testing.T) {
deepEquals := func(other interface{}) func(v interface{}) bool { deepEquals := func(other any) func(v any) bool {
return func(v interface{}) bool { return func(v any) bool {
return reflect.DeepEqual(v, other) return reflect.DeepEqual(v, other)
} }
} }
@@ -45,19 +45,19 @@ func TestDecodeBody(t *testing.T) {
} }
tests := []struct { tests := []struct {
Body map[string]interface{} Body map[string]any
Target func() interface{} Target func() any
Check func(v interface{}) bool Check func(v any) bool
DiagCount int DiagCount int
}{ }{
{ {
map[string]interface{}{}, map[string]any{},
makeInstantiateType(struct{}{}), makeInstantiateType(struct{}{}),
deepEquals(struct{}{}), deepEquals(struct{}{}),
0, 0,
}, },
{ {
map[string]interface{}{}, map[string]any{},
makeInstantiateType(struct { makeInstantiateType(struct {
Name string `hcl:"name"` Name string `hcl:"name"`
}{}), }{}),
@@ -67,7 +67,7 @@ func TestDecodeBody(t *testing.T) {
1, // name is required 1, // name is required
}, },
{ {
map[string]interface{}{}, map[string]any{},
makeInstantiateType(struct { makeInstantiateType(struct {
Name *string `hcl:"name"` Name *string `hcl:"name"`
}{}), }{}),
@@ -77,7 +77,7 @@ func TestDecodeBody(t *testing.T) {
0, 0,
}, // name nil }, // name nil
{ {
map[string]interface{}{}, map[string]any{},
makeInstantiateType(struct { makeInstantiateType(struct {
Name string `hcl:"name,optional"` Name string `hcl:"name,optional"`
}{}), }{}),
@@ -87,9 +87,9 @@ func TestDecodeBody(t *testing.T) {
0, 0,
}, // name optional }, // name optional
{ {
map[string]interface{}{}, map[string]any{},
makeInstantiateType(withNameExpression{}), makeInstantiateType(withNameExpression{}),
func(v interface{}) bool { func(v any) bool {
if v == nil { if v == nil {
return false return false
} }
@@ -109,11 +109,11 @@ func TestDecodeBody(t *testing.T) {
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"name": "Ermintrude", "name": "Ermintrude",
}, },
makeInstantiateType(withNameExpression{}), makeInstantiateType(withNameExpression{}),
func(v interface{}) bool { func(v any) bool {
if v == nil { if v == nil {
return false return false
} }
@@ -133,7 +133,7 @@ func TestDecodeBody(t *testing.T) {
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"name": "Ermintrude", "name": "Ermintrude",
}, },
makeInstantiateType(struct { makeInstantiateType(struct {
@@ -145,7 +145,7 @@ func TestDecodeBody(t *testing.T) {
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"name": "Ermintrude", "name": "Ermintrude",
"age": 23, "age": 23,
}, },
@@ -158,7 +158,7 @@ func TestDecodeBody(t *testing.T) {
1, // Extraneous "age" property 1, // Extraneous "age" property
}, },
{ {
map[string]interface{}{ map[string]any{
"name": "Ermintrude", "name": "Ermintrude",
"age": 50, "age": 50,
}, },
@@ -166,7 +166,7 @@ func TestDecodeBody(t *testing.T) {
Name string `hcl:"name"` Name string `hcl:"name"`
Attrs hcl.Attributes `hcl:",remain"` Attrs hcl.Attributes `hcl:",remain"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
got := gotI.(struct { got := gotI.(struct {
Name string `hcl:"name"` Name string `hcl:"name"`
Attrs hcl.Attributes `hcl:",remain"` Attrs hcl.Attributes `hcl:",remain"`
@@ -176,7 +176,7 @@ func TestDecodeBody(t *testing.T) {
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"name": "Ermintrude", "name": "Ermintrude",
"age": 50, "age": 50,
}, },
@@ -184,7 +184,7 @@ func TestDecodeBody(t *testing.T) {
Name string `hcl:"name"` Name string `hcl:"name"`
Remain hcl.Body `hcl:",remain"` Remain hcl.Body `hcl:",remain"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
got := gotI.(struct { got := gotI.(struct {
Name string `hcl:"name"` Name string `hcl:"name"`
Remain hcl.Body `hcl:",remain"` Remain hcl.Body `hcl:",remain"`
@@ -197,7 +197,7 @@ func TestDecodeBody(t *testing.T) {
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"name": "Ermintrude", "name": "Ermintrude",
"living": true, "living": true,
}, },
@@ -217,7 +217,7 @@ func TestDecodeBody(t *testing.T) {
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"name": "Ermintrude", "name": "Ermintrude",
"age": 50, "age": 50,
}, },
@@ -226,7 +226,7 @@ func TestDecodeBody(t *testing.T) {
Body hcl.Body `hcl:",body"` Body hcl.Body `hcl:",body"`
Remain hcl.Body `hcl:",remain"` Remain hcl.Body `hcl:",remain"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
got := gotI.(struct { got := gotI.(struct {
Name string `hcl:"name"` Name string `hcl:"name"`
Body hcl.Body `hcl:",body"` Body hcl.Body `hcl:",body"`
@@ -241,76 +241,76 @@ func TestDecodeBody(t *testing.T) {
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"noodle": map[string]interface{}{}, "noodle": map[string]any{},
}, },
makeInstantiateType(struct { makeInstantiateType(struct {
Noodle struct{} `hcl:"noodle,block"` Noodle struct{} `hcl:"noodle,block"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
// Generating no diagnostics is good enough for this one. // Generating no diagnostics is good enough for this one.
return true return true
}, },
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"noodle": []map[string]interface{}{{}}, "noodle": []map[string]any{{}},
}, },
makeInstantiateType(struct { makeInstantiateType(struct {
Noodle struct{} `hcl:"noodle,block"` Noodle struct{} `hcl:"noodle,block"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
// Generating no diagnostics is good enough for this one. // Generating no diagnostics is good enough for this one.
return true return true
}, },
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"noodle": []map[string]interface{}{{}, {}}, "noodle": []map[string]any{{}, {}},
}, },
makeInstantiateType(struct { makeInstantiateType(struct {
Noodle struct{} `hcl:"noodle,block"` Noodle struct{} `hcl:"noodle,block"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
// Generating one diagnostic is good enough for this one. // Generating one diagnostic is good enough for this one.
return true return true
}, },
1, 1,
}, },
{ {
map[string]interface{}{}, map[string]any{},
makeInstantiateType(struct { makeInstantiateType(struct {
Noodle struct{} `hcl:"noodle,block"` Noodle struct{} `hcl:"noodle,block"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
// Generating one diagnostic is good enough for this one. // Generating one diagnostic is good enough for this one.
return true return true
}, },
1, 1,
}, },
{ {
map[string]interface{}{ map[string]any{
"noodle": []map[string]interface{}{}, "noodle": []map[string]any{},
}, },
makeInstantiateType(struct { makeInstantiateType(struct {
Noodle struct{} `hcl:"noodle,block"` Noodle struct{} `hcl:"noodle,block"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
// Generating one diagnostic is good enough for this one. // Generating one diagnostic is good enough for this one.
return true return true
}, },
1, 1,
}, },
{ {
map[string]interface{}{ map[string]any{
"noodle": map[string]interface{}{}, "noodle": map[string]any{},
}, },
makeInstantiateType(struct { makeInstantiateType(struct {
Noodle *struct{} `hcl:"noodle,block"` Noodle *struct{} `hcl:"noodle,block"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
return gotI.(struct { return gotI.(struct {
Noodle *struct{} `hcl:"noodle,block"` Noodle *struct{} `hcl:"noodle,block"`
}).Noodle != nil }).Noodle != nil
@@ -318,13 +318,13 @@ func TestDecodeBody(t *testing.T) {
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"noodle": []map[string]interface{}{{}}, "noodle": []map[string]any{{}},
}, },
makeInstantiateType(struct { makeInstantiateType(struct {
Noodle *struct{} `hcl:"noodle,block"` Noodle *struct{} `hcl:"noodle,block"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
return gotI.(struct { return gotI.(struct {
Noodle *struct{} `hcl:"noodle,block"` Noodle *struct{} `hcl:"noodle,block"`
}).Noodle != nil }).Noodle != nil
@@ -332,13 +332,13 @@ func TestDecodeBody(t *testing.T) {
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"noodle": []map[string]interface{}{}, "noodle": []map[string]any{},
}, },
makeInstantiateType(struct { makeInstantiateType(struct {
Noodle *struct{} `hcl:"noodle,block"` Noodle *struct{} `hcl:"noodle,block"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
return gotI.(struct { return gotI.(struct {
Noodle *struct{} `hcl:"noodle,block"` Noodle *struct{} `hcl:"noodle,block"`
}).Noodle == nil }).Noodle == nil
@@ -346,26 +346,26 @@ func TestDecodeBody(t *testing.T) {
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"noodle": []map[string]interface{}{{}, {}}, "noodle": []map[string]any{{}, {}},
}, },
makeInstantiateType(struct { makeInstantiateType(struct {
Noodle *struct{} `hcl:"noodle,block"` Noodle *struct{} `hcl:"noodle,block"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
// Generating one diagnostic is good enough for this one. // Generating one diagnostic is good enough for this one.
return true return true
}, },
1, 1,
}, },
{ {
map[string]interface{}{ map[string]any{
"noodle": []map[string]interface{}{}, "noodle": []map[string]any{},
}, },
makeInstantiateType(struct { makeInstantiateType(struct {
Noodle []struct{} `hcl:"noodle,block"` Noodle []struct{} `hcl:"noodle,block"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
noodle := gotI.(struct { noodle := gotI.(struct {
Noodle []struct{} `hcl:"noodle,block"` Noodle []struct{} `hcl:"noodle,block"`
}).Noodle }).Noodle
@@ -374,13 +374,13 @@ func TestDecodeBody(t *testing.T) {
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"noodle": []map[string]interface{}{{}}, "noodle": []map[string]any{{}},
}, },
makeInstantiateType(struct { makeInstantiateType(struct {
Noodle []struct{} `hcl:"noodle,block"` Noodle []struct{} `hcl:"noodle,block"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
noodle := gotI.(struct { noodle := gotI.(struct {
Noodle []struct{} `hcl:"noodle,block"` Noodle []struct{} `hcl:"noodle,block"`
}).Noodle }).Noodle
@@ -389,13 +389,13 @@ func TestDecodeBody(t *testing.T) {
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"noodle": []map[string]interface{}{{}, {}}, "noodle": []map[string]any{{}, {}},
}, },
makeInstantiateType(struct { makeInstantiateType(struct {
Noodle []struct{} `hcl:"noodle,block"` Noodle []struct{} `hcl:"noodle,block"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
noodle := gotI.(struct { noodle := gotI.(struct {
Noodle []struct{} `hcl:"noodle,block"` Noodle []struct{} `hcl:"noodle,block"`
}).Noodle }).Noodle
@@ -404,15 +404,15 @@ func TestDecodeBody(t *testing.T) {
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"noodle": map[string]interface{}{}, "noodle": map[string]any{},
}, },
makeInstantiateType(struct { makeInstantiateType(struct {
Noodle struct { Noodle struct {
Name string `hcl:"name,label"` Name string `hcl:"name,label"`
} `hcl:"noodle,block"` } `hcl:"noodle,block"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
//nolint:misspell //nolint:misspell
// Generating two diagnostics is good enough for this one. // Generating two diagnostics is good enough for this one.
// (one for the missing noodle block and the other for // (one for the missing noodle block and the other for
@@ -423,9 +423,9 @@ func TestDecodeBody(t *testing.T) {
2, 2,
}, },
{ {
map[string]interface{}{ map[string]any{
"noodle": map[string]interface{}{ "noodle": map[string]any{
"foo_foo": map[string]interface{}{}, "foo_foo": map[string]any{},
}, },
}, },
makeInstantiateType(struct { makeInstantiateType(struct {
@@ -433,7 +433,7 @@ func TestDecodeBody(t *testing.T) {
Name string `hcl:"name,label"` Name string `hcl:"name,label"`
} `hcl:"noodle,block"` } `hcl:"noodle,block"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
noodle := gotI.(struct { noodle := gotI.(struct {
Noodle struct { Noodle struct {
Name string `hcl:"name,label"` Name string `hcl:"name,label"`
@@ -444,10 +444,10 @@ func TestDecodeBody(t *testing.T) {
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"noodle": map[string]interface{}{ "noodle": map[string]any{
"foo_foo": map[string]interface{}{}, "foo_foo": map[string]any{},
"bar_baz": map[string]interface{}{}, "bar_baz": map[string]any{},
}, },
}, },
makeInstantiateType(struct { makeInstantiateType(struct {
@@ -455,17 +455,17 @@ func TestDecodeBody(t *testing.T) {
Name string `hcl:"name,label"` Name string `hcl:"name,label"`
} `hcl:"noodle,block"` } `hcl:"noodle,block"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
// One diagnostic is enough for this one. // One diagnostic is enough for this one.
return true return true
}, },
1, 1,
}, },
{ {
map[string]interface{}{ map[string]any{
"noodle": map[string]interface{}{ "noodle": map[string]any{
"foo_foo": map[string]interface{}{}, "foo_foo": map[string]any{},
"bar_baz": map[string]interface{}{}, "bar_baz": map[string]any{},
}, },
}, },
makeInstantiateType(struct { makeInstantiateType(struct {
@@ -473,7 +473,7 @@ func TestDecodeBody(t *testing.T) {
Name string `hcl:"name,label"` Name string `hcl:"name,label"`
} `hcl:"noodle,block"` } `hcl:"noodle,block"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
noodles := gotI.(struct { noodles := gotI.(struct {
Noodles []struct { Noodles []struct {
Name string `hcl:"name,label"` Name string `hcl:"name,label"`
@@ -484,9 +484,9 @@ func TestDecodeBody(t *testing.T) {
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"noodle": map[string]interface{}{ "noodle": map[string]any{
"foo_foo": map[string]interface{}{ "foo_foo": map[string]any{
"type": "rice", "type": "rice",
}, },
}, },
@@ -497,7 +497,7 @@ func TestDecodeBody(t *testing.T) {
Type string `hcl:"type"` Type string `hcl:"type"`
} `hcl:"noodle,block"` } `hcl:"noodle,block"`
}{}), }{}),
func(gotI interface{}) bool { func(gotI any) bool {
noodle := gotI.(struct { noodle := gotI.(struct {
Noodle struct { Noodle struct {
Name string `hcl:"name,label"` Name string `hcl:"name,label"`
@@ -510,7 +510,7 @@ func TestDecodeBody(t *testing.T) {
}, },
{ {
map[string]interface{}{ map[string]any{
"name": "Ermintrude", "name": "Ermintrude",
"age": 34, "age": 34,
}, },
@@ -522,31 +522,31 @@ func TestDecodeBody(t *testing.T) {
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"name": "Ermintrude", "name": "Ermintrude",
"age": 89, "age": 89,
}, },
makeInstantiateType(map[string]*hcl.Attribute(nil)), makeInstantiateType(map[string]*hcl.Attribute(nil)),
func(gotI interface{}) bool { func(gotI any) bool {
got := gotI.(map[string]*hcl.Attribute) got := gotI.(map[string]*hcl.Attribute)
return len(got) == 2 && got["name"] != nil && got["age"] != nil return len(got) == 2 && got["name"] != nil && got["age"] != nil
}, },
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"name": "Ermintrude", "name": "Ermintrude",
"age": 13, "age": 13,
}, },
makeInstantiateType(map[string]hcl.Expression(nil)), makeInstantiateType(map[string]hcl.Expression(nil)),
func(gotI interface{}) bool { func(gotI any) bool {
got := gotI.(map[string]hcl.Expression) got := gotI.(map[string]hcl.Expression)
return len(got) == 2 && got["name"] != nil && got["age"] != nil return len(got) == 2 && got["name"] != nil && got["age"] != nil
}, },
0, 0,
}, },
{ {
map[string]interface{}{ map[string]any{
"name": "Ermintrude", "name": "Ermintrude",
"living": true, "living": true,
}, },
@@ -559,10 +559,10 @@ func TestDecodeBody(t *testing.T) {
}, },
{ {
// Retain "nested" block while decoding // Retain "nested" block while decoding
map[string]interface{}{ map[string]any{
"plain": "foo", "plain": "foo",
}, },
func() interface{} { func() any {
return &withNestedBlock{ return &withNestedBlock{
Plain: "bar", Plain: "bar",
Nested: &withTwoAttributes{ Nested: &withTwoAttributes{
@@ -570,7 +570,7 @@ func TestDecodeBody(t *testing.T) {
}, },
} }
}, },
func(gotI interface{}) bool { func(gotI any) bool {
foo := gotI.(withNestedBlock) foo := gotI.(withNestedBlock)
return foo.Plain == "foo" && foo.Nested != nil && foo.Nested.A == "bar" return foo.Plain == "foo" && foo.Nested != nil && foo.Nested.A == "bar"
}, },
@@ -578,19 +578,19 @@ func TestDecodeBody(t *testing.T) {
}, },
{ {
// Retain values in "nested" block while decoding // Retain values in "nested" block while decoding
map[string]interface{}{ map[string]any{
"nested": map[string]interface{}{ "nested": map[string]any{
"a": "foo", "a": "foo",
}, },
}, },
func() interface{} { func() any {
return &withNestedBlock{ return &withNestedBlock{
Nested: &withTwoAttributes{ Nested: &withTwoAttributes{
B: "bar", B: "bar",
}, },
} }
}, },
func(gotI interface{}) bool { func(gotI any) bool {
foo := gotI.(withNestedBlock) foo := gotI.(withNestedBlock)
return foo.Nested.A == "foo" && foo.Nested.B == "bar" return foo.Nested.A == "foo" && foo.Nested.B == "bar"
}, },
@@ -598,14 +598,14 @@ func TestDecodeBody(t *testing.T) {
}, },
{ {
// Retain values in "nested" block list while decoding // Retain values in "nested" block list while decoding
map[string]interface{}{ map[string]any{
"nested": []map[string]interface{}{ "nested": []map[string]any{
{ {
"a": "foo", "a": "foo",
}, },
}, },
}, },
func() interface{} { func() any {
return &withListofNestedBlocks{ return &withListofNestedBlocks{
Nested: []*withTwoAttributes{ Nested: []*withTwoAttributes{
{ {
@@ -614,7 +614,7 @@ func TestDecodeBody(t *testing.T) {
}, },
} }
}, },
func(gotI interface{}) bool { func(gotI any) bool {
n := gotI.(withListofNestedBlocks) n := gotI.(withListofNestedBlocks)
return n.Nested[0].A == "foo" && n.Nested[0].B == "bar" return n.Nested[0].A == "foo" && n.Nested[0].B == "bar"
}, },
@@ -622,14 +622,14 @@ func TestDecodeBody(t *testing.T) {
}, },
{ {
// Remove additional elements from the list while decoding nested blocks // Remove additional elements from the list while decoding nested blocks
map[string]interface{}{ map[string]any{
"nested": []map[string]interface{}{ "nested": []map[string]any{
{ {
"a": "foo", "a": "foo",
}, },
}, },
}, },
func() interface{} { func() any {
return &withListofNestedBlocks{ return &withListofNestedBlocks{
Nested: []*withTwoAttributes{ Nested: []*withTwoAttributes{
{ {
@@ -641,7 +641,7 @@ func TestDecodeBody(t *testing.T) {
}, },
} }
}, },
func(gotI interface{}) bool { func(gotI any) bool {
n := gotI.(withListofNestedBlocks) n := gotI.(withListofNestedBlocks)
return len(n.Nested) == 1 return len(n.Nested) == 1
}, },
@@ -649,8 +649,8 @@ func TestDecodeBody(t *testing.T) {
}, },
{ {
// Make sure decoding value slices works the same as pointer slices. // Make sure decoding value slices works the same as pointer slices.
map[string]interface{}{ map[string]any{
"nested": []map[string]interface{}{ "nested": []map[string]any{
{ {
"b": "bar", "b": "bar",
}, },
@@ -659,7 +659,7 @@ func TestDecodeBody(t *testing.T) {
}, },
}, },
}, },
func() interface{} { func() any {
return &withListofNestedBlocksNoPointers{ return &withListofNestedBlocksNoPointers{
Nested: []withTwoAttributes{ Nested: []withTwoAttributes{
{ {
@@ -668,7 +668,7 @@ func TestDecodeBody(t *testing.T) {
}, },
} }
}, },
func(gotI interface{}) bool { func(gotI any) bool {
n := gotI.(withListofNestedBlocksNoPointers) n := gotI.(withListofNestedBlocksNoPointers)
return n.Nested[0].B == "bar" && len(n.Nested) == 2 return n.Nested[0].B == "bar" && len(n.Nested) == 2
}, },
@@ -710,8 +710,8 @@ func TestDecodeBody(t *testing.T) {
func TestDecodeExpression(t *testing.T) { func TestDecodeExpression(t *testing.T) {
tests := []struct { tests := []struct {
Value cty.Value Value cty.Value
Target interface{} Target any
Want interface{} Want any
DiagCount int DiagCount int
}{ }{
{ {
@@ -799,8 +799,8 @@ func (e *fixedExpression) Variables() []hcl.Traversal {
return nil return nil
} }
func makeInstantiateType(target interface{}) func() interface{} { func makeInstantiateType(target any) func() any {
return func() interface{} { return func() any {
return reflect.New(reflect.TypeOf(target)).Interface() return reflect.New(reflect.TypeOf(target)).Interface()
} }
} }

View File

@@ -34,9 +34,9 @@ import (
// The layout of the resulting HCL source is derived from the ordering of // The layout of the resulting HCL source is derived from the ordering of
// the struct fields, with blank lines around nested blocks of different types. // the struct fields, with blank lines around nested blocks of different types.
// Fields representing attributes should usually precede those representing // Fields representing attributes should usually precede those representing
// blocks so that the attributes can group togather in the result. For more // blocks so that the attributes can group together in the result. For more
// control, use the hclwrite API directly. // control, use the hclwrite API directly.
func EncodeIntoBody(val interface{}, dst *hclwrite.Body) { func EncodeIntoBody(val any, dst *hclwrite.Body) {
rv := reflect.ValueOf(val) rv := reflect.ValueOf(val)
ty := rv.Type() ty := rv.Type()
if ty.Kind() == reflect.Ptr { if ty.Kind() == reflect.Ptr {
@@ -60,7 +60,7 @@ func EncodeIntoBody(val interface{}, dst *hclwrite.Body) {
// //
// This function has the same constraints as EncodeIntoBody and will panic // This function has the same constraints as EncodeIntoBody and will panic
// if they are violated. // if they are violated.
func EncodeAsBlock(val interface{}, blockType string) *hclwrite.Block { func EncodeAsBlock(val any, blockType string) *hclwrite.Block {
rv := reflect.ValueOf(val) rv := reflect.ValueOf(val)
ty := rv.Type() ty := rv.Type()
if ty.Kind() == reflect.Ptr { if ty.Kind() == reflect.Ptr {
@@ -158,7 +158,7 @@ func populateBody(rv reflect.Value, ty reflect.Type, tags *fieldTags, dst *hclwr
if isSeq { if isSeq {
l := fieldVal.Len() l := fieldVal.Len()
for i := 0; i < l; i++ { for i := range l {
elemVal := fieldVal.Index(i) elemVal := fieldVal.Index(i)
if !elemVal.IsValid() { if !elemVal.IsValid() {
continue // ignore (elem value is nil pointer) continue // ignore (elem value is nil pointer)

View File

@@ -22,7 +22,7 @@ import (
// This uses the tags on the fields of the struct to discover how each // This uses the tags on the fields of the struct to discover how each
// field's value should be expressed within configuration. If an invalid // field's value should be expressed within configuration. If an invalid
// mapping is attempted, this function will panic. // mapping is attempted, this function will panic.
func ImpliedBodySchema(val interface{}) (schema *hcl.BodySchema, partial bool) { func ImpliedBodySchema(val any) (schema *hcl.BodySchema, partial bool) {
ty := reflect.TypeOf(val) ty := reflect.TypeOf(val)
if ty.Kind() == reflect.Ptr { if ty.Kind() == reflect.Ptr {
@@ -134,7 +134,7 @@ func getFieldTags(ty reflect.Type) *fieldTags {
} }
ct := ty.NumField() ct := ty.NumField()
for i := 0; i < ct; i++ { for i := range ct {
field := ty.Field(i) field := ty.Field(i)
tag := field.Tag.Get("hcl") tag := field.Tag.Get("hcl")
if tag == "" { if tag == "" {

View File

@@ -14,7 +14,7 @@ import (
func TestImpliedBodySchema(t *testing.T) { func TestImpliedBodySchema(t *testing.T) {
tests := []struct { tests := []struct {
val interface{} val any
wantSchema *hcl.BodySchema wantSchema *hcl.BodySchema
wantPartial bool wantPartial bool
}{ }{

View File

@@ -7,6 +7,7 @@ import (
"math" "math"
"math/big" "math/big"
"reflect" "reflect"
"slices"
"strconv" "strconv"
"strings" "strings"
@@ -589,7 +590,7 @@ type ParseMeta struct {
AllVariables []*Variable AllVariables []*Variable
} }
func Parse(b hcl.Body, opt Opt, val interface{}) (*ParseMeta, hcl.Diagnostics) { func Parse(b hcl.Body, opt Opt, val any) (*ParseMeta, hcl.Diagnostics) {
reserved := map[string]struct{}{} reserved := map[string]struct{}{}
schema, _ := gohcl.ImpliedBodySchema(val) schema, _ := gohcl.ImpliedBodySchema(val)
@@ -763,7 +764,7 @@ func Parse(b hcl.Body, opt Opt, val interface{}) (*ParseMeta, hcl.Diagnostics) {
types := map[string]field{} types := map[string]field{}
renamed := map[string]map[string][]string{} renamed := map[string]map[string][]string{}
vt := reflect.ValueOf(val).Elem().Type() vt := reflect.ValueOf(val).Elem().Type()
for i := 0; i < vt.NumField(); i++ { for i := range vt.NumField() {
tags := strings.Split(vt.Field(i).Tag.Get("hcl"), ",") tags := strings.Split(vt.Field(i).Tag.Get("hcl"), ",")
p.blockTypes[tags[0]] = vt.Field(i).Type.Elem().Elem() p.blockTypes[tags[0]] = vt.Field(i).Type.Elem().Elem()
@@ -831,7 +832,7 @@ func Parse(b hcl.Body, opt Opt, val interface{}) (*ParseMeta, hcl.Diagnostics) {
oldValue, exists := t.values[lblName] oldValue, exists := t.values[lblName]
if !exists && lblExists { if !exists && lblExists {
if v.Elem().Field(t.idx).Type().Kind() == reflect.Slice { if v.Elem().Field(t.idx).Type().Kind() == reflect.Slice {
for i := 0; i < v.Elem().Field(t.idx).Len(); i++ { for i := range v.Elem().Field(t.idx).Len() {
if lblName == v.Elem().Field(t.idx).Index(i).Elem().Field(lblIndex).String() { if lblName == v.Elem().Field(t.idx).Index(i).Elem().Field(lblIndex).String() {
exists = true exists = true
oldValue = value{Value: v.Elem().Field(t.idx).Index(i), idx: i} oldValue = value{Value: v.Elem().Field(t.idx).Index(i), idx: i}
@@ -898,7 +899,7 @@ func wrapErrorDiagnostic(message string, err error, subject *hcl.Range, context
func setName(v reflect.Value, name string) { func setName(v reflect.Value, name string) {
numFields := v.Elem().Type().NumField() numFields := v.Elem().Type().NumField()
for i := 0; i < numFields; i++ { for i := range numFields {
parts := strings.Split(v.Elem().Type().Field(i).Tag.Get("hcl"), ",") parts := strings.Split(v.Elem().Type().Field(i).Tag.Get("hcl"), ",")
for _, t := range parts[1:] { for _, t := range parts[1:] {
if t == "label" { if t == "label" {
@@ -910,27 +911,23 @@ func setName(v reflect.Value, name string) {
func getName(v reflect.Value) (string, bool) { func getName(v reflect.Value) (string, bool) {
numFields := v.Elem().Type().NumField() numFields := v.Elem().Type().NumField()
for i := 0; i < numFields; i++ { for i := range numFields {
parts := strings.Split(v.Elem().Type().Field(i).Tag.Get("hcl"), ",") parts := strings.Split(v.Elem().Type().Field(i).Tag.Get("hcl"), ",")
for _, t := range parts[1:] { if slices.Contains(parts[1:], "label") {
if t == "label" {
return v.Elem().Field(i).String(), true return v.Elem().Field(i).String(), true
} }
} }
}
return "", false return "", false
} }
func getNameIndex(v reflect.Value) (int, bool) { func getNameIndex(v reflect.Value) (int, bool) {
numFields := v.Elem().Type().NumField() numFields := v.Elem().Type().NumField()
for i := 0; i < numFields; i++ { for i := range numFields {
parts := strings.Split(v.Elem().Type().Field(i).Tag.Get("hcl"), ",") parts := strings.Split(v.Elem().Type().Field(i).Tag.Get("hcl"), ",")
for _, t := range parts[1:] { if slices.Contains(parts[1:], "label") {
if t == "label" {
return i, true return i, true
} }
} }
}
return 0, false return 0, false
} }
@@ -988,7 +985,7 @@ func key(ks ...any) uint64 {
return hash.Sum64() return hash.Sum64()
} }
func decodeBody(body hcl.Body, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics { func decodeBody(body hcl.Body, ctx *hcl.EvalContext, val any) hcl.Diagnostics {
dec := gohcl.DecodeOptions{ImpliedType: ImpliedType} dec := gohcl.DecodeOptions{ImpliedType: ImpliedType}
return dec.DecodeBody(body, ctx, val) return dec.DecodeBody(body, ctx, val)
} }

View File

@@ -43,7 +43,7 @@ import (
// In particular, ImpliedType will never use capsule types in its returned // In particular, ImpliedType will never use capsule types in its returned
// type, because it cannot know the capsule types supported by the calling // type, because it cannot know the capsule types supported by the calling
// program. // program.
func ImpliedType(gv interface{}) (cty.Type, error) { func ImpliedType(gv any) (cty.Type, error) {
rt := reflect.TypeOf(gv) rt := reflect.TypeOf(gv)
var path cty.Path var path cty.Path
return impliedType(rt, path) return impliedType(rt, path)
@@ -148,7 +148,7 @@ func structTagIndices(st reflect.Type) map[string]int {
ct := st.NumField() ct := st.NumField()
ret := make(map[string]int, ct) ret := make(map[string]int, ct)
for i := 0; i < ct; i++ { for i := range ct {
field := st.Field(i) field := st.Field(i)
attrName := field.Tag.Get("cty") attrName := field.Tag.Get("cty")
if attrName != "" { if attrName != "" {

View File

@@ -40,7 +40,6 @@ import (
"github.com/moby/buildkit/solver/errdefs" "github.com/moby/buildkit/solver/errdefs"
"github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/solver/pb"
spb "github.com/moby/buildkit/sourcepolicy/pb" spb "github.com/moby/buildkit/sourcepolicy/pb"
"github.com/moby/buildkit/util/entitlements"
"github.com/moby/buildkit/util/progress/progresswriter" "github.com/moby/buildkit/util/progress/progresswriter"
"github.com/moby/buildkit/util/tracing" "github.com/moby/buildkit/util/tracing"
"github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
@@ -63,7 +62,7 @@ type Options struct {
Inputs Inputs Inputs Inputs
Ref string Ref string
Allow []entitlements.Entitlement Allow []string
Attests map[string]*string Attests map[string]*string
BuildArgs map[string]string BuildArgs map[string]string
CacheFrom []client.CacheOptionsEntry CacheFrom []client.CacheOptionsEntry
@@ -540,7 +539,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
node := dp.Node().Driver node := dp.Node().Driver
if node.IsMobyDriver() { if node.IsMobyDriver() {
for _, e := range so.Exports { for _, e := range so.Exports {
if e.Type == "moby" && e.Attrs["push"] != "" { if e.Type == "moby" && e.Attrs["push"] != "" && !node.Features(ctx)[driver.DirectPush] {
if ok, _ := strconv.ParseBool(e.Attrs["push"]); ok { if ok, _ := strconv.ParseBool(e.Attrs["push"]); ok {
pushNames = e.Attrs["name"] pushNames = e.Attrs["name"]
if pushNames == "" { if pushNames == "" {
@@ -623,7 +622,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
// This is fallback for some very old buildkit versions. // This is fallback for some very old buildkit versions.
// Note that the mediatype isn't really correct as most of the time it is image manifest and // Note that the mediatype isn't really correct as most of the time it is image manifest and
// not manifest list but actually both are handled because for Docker mediatypes the // not manifest list but actually both are handled because for Docker mediatypes the
// mediatype value in the Accpet header does not seem to matter. // mediatype value in the Accept header does not seem to matter.
s, ok = r.ExporterResponse[exptypes.ExporterImageDigestKey] s, ok = r.ExporterResponse[exptypes.ExporterImageDigestKey]
if ok { if ok {
descs = append(descs, specs.Descriptor{ descs = append(descs, specs.Descriptor{
@@ -835,7 +834,7 @@ func remoteDigestWithMoby(ctx context.Context, d *driver.DriverHandle, name stri
if err != nil { if err != nil {
return "", err return "", err
} }
img, _, err := api.ImageInspectWithRaw(ctx, name) img, err := api.ImageInspect(ctx, name)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
stderrors "errors" stderrors "errors"
"net" "net"
"slices"
"github.com/containerd/platforms" "github.com/containerd/platforms"
"github.com/docker/buildx/builder" "github.com/docker/buildx/builder"
@@ -37,15 +38,7 @@ func Dial(ctx context.Context, nodes []builder.Node, pw progress.Writer, platfor
for _, ls := range resolved { for _, ls := range resolved {
for _, rn := range ls { for _, rn := range ls {
if platform != nil { if platform != nil {
p := *platform if !slices.ContainsFunc(rn.platforms, platforms.Only(*platform).Match) {
var found bool
for _, pp := range rn.platforms {
if platforms.Only(p).Match(pp) {
found = true
break
}
}
if !found {
continue continue
} }
} }

View File

@@ -3,6 +3,7 @@ package build
import ( import (
"context" "context"
"fmt" "fmt"
"slices"
"sync" "sync"
"github.com/containerd/platforms" "github.com/containerd/platforms"
@@ -221,7 +222,7 @@ func (r *nodeResolver) get(p specs.Platform, matcher matchMaker, additionalPlatf
for i, node := range r.nodes { for i, node := range r.nodes {
platforms := node.Platforms platforms := node.Platforms
if additionalPlatforms != nil { if additionalPlatforms != nil {
platforms = append([]specs.Platform{}, platforms...) platforms = slices.Clone(platforms)
platforms = append(platforms, additionalPlatforms(i, node)...) platforms = append(platforms, additionalPlatforms(i, node)...)
} }
for _, p2 := range platforms { for _, p2 := range platforms {

View File

@@ -318,7 +318,7 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt *O
switch opt.NetworkMode { switch opt.NetworkMode {
case "host": case "host":
so.FrontendAttrs["force-network-mode"] = opt.NetworkMode so.FrontendAttrs["force-network-mode"] = opt.NetworkMode
so.AllowedEntitlements = append(so.AllowedEntitlements, entitlements.EntitlementNetworkHost) so.AllowedEntitlements = append(so.AllowedEntitlements, entitlements.EntitlementNetworkHost.String())
case "none": case "none":
so.FrontendAttrs["force-network-mode"] = opt.NetworkMode so.FrontendAttrs["force-network-mode"] = opt.NetworkMode
case "", "default": case "", "default":

View File

@@ -28,11 +28,11 @@ func TestSyncMultiReaderParallel(t *testing.T) {
readers := make([]io.ReadCloser, numReaders) readers := make([]io.ReadCloser, numReaders)
for i := 0; i < numReaders; i++ { for i := range numReaders {
readers[i] = mr.NewReadCloser() readers[i] = mr.NewReadCloser()
} }
for i := 0; i < numReaders; i++ { for i := range numReaders {
wg.Add(1) wg.Add(1)
go func(readerId int) { go func(readerId int) {
defer wg.Done() defer wg.Done()

View File

@@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"net/url" "net/url"
"os" "os"
"slices"
"sort" "sort"
"strings" "strings"
"sync" "sync"
@@ -199,7 +200,7 @@ func (b *Builder) Boot(ctx context.Context) (bool, error) {
err = err1 err = err1
} }
if err == nil && len(errCh) == len(toBoot) { if err == nil && len(errCh) > 0 {
return false, <-errCh return false, <-errCh
} }
return true, err return true, err
@@ -656,13 +657,7 @@ func parseBuildkitdFlags(inp string, driver string, driverOpts map[string]string
flags.StringArrayVar(&allowInsecureEntitlements, "allow-insecure-entitlement", nil, "") flags.StringArrayVar(&allowInsecureEntitlements, "allow-insecure-entitlement", nil, "")
_ = flags.Parse(res) _ = flags.Parse(res)
var hasNetworkHostEntitlement bool hasNetworkHostEntitlement := slices.Contains(allowInsecureEntitlements, "network.host")
for _, e := range allowInsecureEntitlements {
if e == "network.host" {
hasNetworkHostEntitlement = true
break
}
}
var hasNetworkHostEntitlementInConf bool var hasNetworkHostEntitlementInConf bool
if buildkitdConfigFile != "" { if buildkitdConfigFile != "" {
@@ -671,11 +666,8 @@ func parseBuildkitdFlags(inp string, driver string, driverOpts map[string]string
return nil, err return nil, err
} else if btoml != nil { } else if btoml != nil {
if ies := btoml.GetArray("insecure-entitlements"); ies != nil { if ies := btoml.GetArray("insecure-entitlements"); ies != nil {
for _, e := range ies.([]string) { if slices.Contains(ies.([]string), "network.host") {
if e == "network.host" {
hasNetworkHostEntitlementInConf = true hasNetworkHostEntitlementInConf = true
break
}
} }
} }
} }

View File

@@ -36,6 +36,7 @@ type Node struct {
Platforms []ocispecs.Platform Platforms []ocispecs.Platform
GCPolicy []client.PruneInfo GCPolicy []client.PruneInfo
Labels map[string]string Labels map[string]string
CDIDevices []client.CDIDevice
} }
// Nodes returns nodes for this builder. // Nodes returns nodes for this builder.
@@ -168,7 +169,7 @@ func (b *Builder) LoadNodes(ctx context.Context, opts ...LoadNodesOption) (_ []N
// dynamic nodes are used in Kubernetes driver. // dynamic nodes are used in Kubernetes driver.
// Kubernetes' pods are dynamically mapped to BuildKit Nodes. // Kubernetes' pods are dynamically mapped to BuildKit Nodes.
if di.DriverInfo != nil && len(di.DriverInfo.DynamicNodes) > 0 { if di.DriverInfo != nil && len(di.DriverInfo.DynamicNodes) > 0 {
for i := 0; i < len(di.DriverInfo.DynamicNodes); i++ { for i := range di.DriverInfo.DynamicNodes {
diClone := di diClone := di
if pl := di.DriverInfo.DynamicNodes[i].Platforms; len(pl) > 0 { if pl := di.DriverInfo.DynamicNodes[i].Platforms; len(pl) > 0 {
diClone.Platforms = pl diClone.Platforms = pl
@@ -259,6 +260,7 @@ func (n *Node) loadData(ctx context.Context, clientOpt ...client.ClientOpt) erro
n.GCPolicy = w.GCPolicy n.GCPolicy = w.GCPolicy
n.Labels = w.Labels n.Labels = w.Labels
} }
n.CDIDevices = w.CDIDevices
} }
sort.Strings(n.IDs) sort.Strings(n.IDs)
n.Platforms = platformutil.Dedupe(n.Platforms) n.Platforms = platformutil.Dedupe(n.Platforms)

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"os" "os"
"path/filepath"
"github.com/docker/buildx/commands" "github.com/docker/buildx/commands"
controllererrors "github.com/docker/buildx/controller/errdefs" controllererrors "github.com/docker/buildx/controller/errdefs"
@@ -41,7 +42,8 @@ func runStandalone(cmd *command.DockerCli) error {
} }
defer flushMetrics(cmd) defer flushMetrics(cmd)
rootCmd := commands.NewRootCmd(os.Args[0], false, cmd) executable := os.Args[0]
rootCmd := commands.NewRootCmd(filepath.Base(executable), false, cmd)
return rootCmd.Execute() return rootCmd.Execute()
} }

View File

@@ -66,7 +66,11 @@ type bakeOptions struct {
func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in bakeOptions, cFlags commonFlags) (err error) { func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in bakeOptions, cFlags commonFlags) (err error) {
mp := dockerCli.MeterProvider() mp := dockerCli.MeterProvider()
ctx, end, err := tracing.TraceCurrentCommand(ctx, "bake") ctx, end, err := tracing.TraceCurrentCommand(ctx, append([]string{"bake"}, targets...),
attribute.String("builder", in.builder),
attribute.StringSlice("targets", targets),
attribute.StringSlice("files", in.files),
)
if err != nil { if err != nil {
return err return err
} }
@@ -271,9 +275,11 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
if err != nil { if err != nil {
return err return err
} }
if progressMode != progressui.RawJSONMode {
if err := exp.Prompt(ctx, url != "", &syncWriter{w: dockerCli.Err(), wait: printer.Wait}); err != nil { if err := exp.Prompt(ctx, url != "", &syncWriter{w: dockerCli.Err(), wait: printer.Wait}); err != nil {
return err return err
} }
}
if printer.IsDone() { if printer.IsDone() {
// init new printer as old one was stopped to show the prompt // init new printer as old one was stopped to show the prompt
if err := makePrinter(); err != nil { if err := makePrinter(); err != nil {
@@ -281,7 +287,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
} }
} }
if err := saveLocalStateGroup(dockerCli, in, targets, bo, overrides, def); err != nil { if err := saveLocalStateGroup(dockerCli, in, targets, bo); err != nil {
return err return err
} }
@@ -303,7 +309,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
desktop.PrintBuildDetails(os.Stderr, printer.BuildRefs(), term) desktop.PrintBuildDetails(os.Stderr, printer.BuildRefs(), term)
} }
if len(in.metadataFile) > 0 { if len(in.metadataFile) > 0 {
dt := make(map[string]interface{}) dt := make(map[string]any)
for t, r := range resp { for t, r := range resp {
dt[t] = decodeExporterResponse(r.ExporterResponse) dt[t] = decodeExporterResponse(r.ExporterResponse)
} }
@@ -486,7 +492,14 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
return cmd return cmd
} }
func saveLocalStateGroup(dockerCli command.Cli, in bakeOptions, targets []string, bo map[string]build.Options, overrides []string, def any) error { func saveLocalStateGroup(dockerCli command.Cli, in bakeOptions, targets []string, bo map[string]build.Options) error {
l, err := localstate.New(confutil.NewConfig(dockerCli))
if err != nil {
return err
}
defer l.MigrateIfNeeded()
prm := confutil.MetadataProvenance() prm := confutil.MetadataProvenance()
if len(in.metadataFile) == 0 { if len(in.metadataFile) == 0 {
prm = confutil.MetadataProvenanceModeDisabled prm = confutil.MetadataProvenanceModeDisabled
@@ -506,19 +519,10 @@ func saveLocalStateGroup(dockerCli command.Cli, in bakeOptions, targets []string
if len(refs) == 0 { if len(refs) == 0 {
return nil return nil
} }
l, err := localstate.New(confutil.NewConfig(dockerCli))
if err != nil {
return err
}
dtdef, err := json.MarshalIndent(def, "", " ")
if err != nil {
return err
}
return l.SaveGroup(groupRef, localstate.StateGroup{ return l.SaveGroup(groupRef, localstate.StateGroup{
Definition: dtdef,
Targets: targets,
Inputs: overrides,
Refs: refs, Refs: refs,
Targets: targets,
}) })
} }

View File

@@ -11,6 +11,7 @@ import (
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
"slices"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@@ -41,7 +42,7 @@ import (
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
dockeropts "github.com/docker/cli/opts" dockeropts "github.com/docker/cli/opts"
"github.com/docker/docker/api/types/versions" "github.com/docker/docker/api/types/versions"
"github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/atomicwriter"
"github.com/moby/buildkit/client" "github.com/moby/buildkit/client"
"github.com/moby/buildkit/exporter/containerimage/exptypes" "github.com/moby/buildkit/exporter/containerimage/exptypes"
"github.com/moby/buildkit/frontend/subrequests" "github.com/moby/buildkit/frontend/subrequests"
@@ -156,7 +157,7 @@ func (o *buildOptions) toControllerOptions() (*controllerapi.BuildOptions, error
return nil, err return nil, err
} }
inAttests := append([]string{}, o.attests...) inAttests := slices.Clone(o.attests)
if o.provenance != "" { if o.provenance != "" {
inAttests = append(inAttests, buildflags.CanonicalizeAttest("provenance", o.provenance)) inAttests = append(inAttests, buildflags.CanonicalizeAttest("provenance", o.provenance))
} }
@@ -183,14 +184,17 @@ func (o *buildOptions) toControllerOptions() (*controllerapi.BuildOptions, error
} }
} }
opts.CacheFrom, err = buildflags.ParseCacheEntry(o.cacheFrom) cacheFrom, err := buildflags.ParseCacheEntry(o.cacheFrom)
if err != nil { if err != nil {
return nil, err return nil, err
} }
opts.CacheTo, err = buildflags.ParseCacheEntry(o.cacheTo) opts.CacheFrom = cacheFrom.ToPB()
cacheTo, err := buildflags.ParseCacheEntry(o.cacheTo)
if err != nil { if err != nil {
return nil, err return nil, err
} }
opts.CacheTo = cacheTo.ToPB()
opts.Secrets, err = buildflags.ParseSecretSpecs(o.secrets) opts.Secrets, err = buildflags.ParseSecretSpecs(o.secrets)
if err != nil { if err != nil {
@@ -282,7 +286,11 @@ func (o *buildOptionsHash) String() string {
func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) (err error) { func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) (err error) {
mp := dockerCli.MeterProvider() mp := dockerCli.MeterProvider()
ctx, end, err := tracing.TraceCurrentCommand(ctx, "build") ctx, end, err := tracing.TraceCurrentCommand(ctx, []string{"build", options.contextPath},
attribute.String("builder", options.builder),
attribute.String("context", options.contextPath),
attribute.String("dockerfile", options.dockerfileName),
)
if err != nil { if err != nil {
return err return err
} }
@@ -463,7 +471,7 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro
if err != nil { if err != nil {
var be *controllererrors.BuildError var be *controllererrors.BuildError
if errors.As(err, &be) { if errors.As(err, &be) {
ref = be.Ref ref = be.SessionID
retErr = err retErr = err
// We can proceed to monitor // We can proceed to monitor
} else { } else {
@@ -590,7 +598,7 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugConfig *debug.D
flags.StringSliceVar(&options.extraHosts, "add-host", []string{}, `Add a custom host-to-IP mapping (format: "host:ip")`) flags.StringSliceVar(&options.extraHosts, "add-host", []string{}, `Add a custom host-to-IP mapping (format: "host:ip")`)
flags.StringSliceVar(&options.allow, "allow", []string{}, `Allow extra privileged entitlement (e.g., "network.host", "security.insecure")`) flags.StringArrayVar(&options.allow, "allow", []string{}, `Allow extra privileged entitlement (e.g., "network.host", "security.insecure")`)
flags.StringArrayVarP(&options.annotations, "annotation", "", []string{}, "Add annotation to the image") flags.StringArrayVarP(&options.annotations, "annotation", "", []string{}, "Add annotation to the image")
@@ -737,15 +745,15 @@ func checkWarnedFlags(f *pflag.Flag) {
} }
} }
func writeMetadataFile(filename string, dt interface{}) error { func writeMetadataFile(filename string, dt any) error {
b, err := json.MarshalIndent(dt, "", " ") b, err := json.MarshalIndent(dt, "", " ")
if err != nil { if err != nil {
return err return err
} }
return ioutils.AtomicWriteFile(filename, b, 0644) return atomicwriter.WriteFile(filename, b, 0644)
} }
func decodeExporterResponse(exporterResponse map[string]string) map[string]interface{} { func decodeExporterResponse(exporterResponse map[string]string) map[string]any {
decFunc := func(k, v string) ([]byte, error) { decFunc := func(k, v string) ([]byte, error) {
if k == "result.json" { if k == "result.json" {
// result.json is part of metadata response for subrequests which // result.json is part of metadata response for subrequests which
@@ -754,16 +762,16 @@ func decodeExporterResponse(exporterResponse map[string]string) map[string]inter
} }
return base64.StdEncoding.DecodeString(v) return base64.StdEncoding.DecodeString(v)
} }
out := make(map[string]interface{}) out := make(map[string]any)
for k, v := range exporterResponse { for k, v := range exporterResponse {
dt, err := decFunc(k, v) dt, err := decFunc(k, v)
if err != nil { if err != nil {
out[k] = v out[k] = v
continue continue
} }
var raw map[string]interface{} var raw map[string]any
if err = json.Unmarshal(dt, &raw); err != nil || len(raw) == 0 { if err = json.Unmarshal(dt, &raw); err != nil || len(raw) == 0 {
var rawList []map[string]interface{} var rawList []map[string]any
if err = json.Unmarshal(dt, &rawList); err != nil || len(rawList) == 0 { if err = json.Unmarshal(dt, &rawList); err != nil || len(rawList) == 0 {
out[k] = v out[k] = v
continue continue

View File

@@ -124,7 +124,7 @@ func duCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
return cmd return cmd
} }
func printKV(w io.Writer, k string, v interface{}) { func printKV(w io.Writer, k string, v any) {
fmt.Fprintf(w, "%s:\t%v\n", k, v) fmt.Fprintf(w, "%s:\t%v\n", k, v)
} }

135
commands/history/import.go Normal file
View File

@@ -0,0 +1,135 @@
package history
import (
"context"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"os"
"strings"
remoteutil "github.com/docker/buildx/driver/remote/util"
"github.com/docker/buildx/util/cobrautil/completion"
"github.com/docker/buildx/util/desktop"
"github.com/docker/cli/cli/command"
"github.com/pkg/browser"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
type importOptions struct {
file []string
}
func runImport(ctx context.Context, dockerCli command.Cli, opts importOptions) error {
sock, err := desktop.BuildServerAddr()
if err != nil {
return err
}
tr := http.DefaultTransport.(*http.Transport).Clone()
tr.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) {
network, addr, ok := strings.Cut(sock, "://")
if !ok {
return nil, errors.Errorf("invalid endpoint address: %s", sock)
}
return remoteutil.DialContext(ctx, network, addr)
}
client := &http.Client{
Transport: tr,
}
var urls []string
if len(opts.file) == 0 {
u, err := importFrom(ctx, client, os.Stdin)
if err != nil {
return err
}
urls = append(urls, u...)
} else {
for _, fn := range opts.file {
var f *os.File
var rdr io.Reader = os.Stdin
if fn != "-" {
f, err = os.Open(fn)
if err != nil {
return errors.Wrapf(err, "failed to open file %s", fn)
}
rdr = f
}
u, err := importFrom(ctx, client, rdr)
if err != nil {
return err
}
urls = append(urls, u...)
if f != nil {
f.Close()
}
}
}
if len(urls) == 0 {
return errors.New("no build records found in the bundle")
}
for i, url := range urls {
fmt.Fprintln(dockerCli.Err(), url)
if i == 0 {
err = browser.OpenURL(url)
}
}
return err
}
func importFrom(ctx context.Context, c *http.Client, rdr io.Reader) ([]string, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "http://docker-desktop/upload", rdr)
if err != nil {
return nil, errors.Wrap(err, "failed to create request")
}
resp, err := c.Do(req)
if err != nil {
return nil, errors.Wrap(err, "failed to send request, check if Docker Desktop is running")
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, errors.Errorf("failed to import build: %s", string(body))
}
var refs []string
dec := json.NewDecoder(resp.Body)
if err := dec.Decode(&refs); err != nil {
return nil, errors.Wrap(err, "failed to decode response")
}
var urls []string
for _, ref := range refs {
urls = append(urls, desktop.BuildURL(fmt.Sprintf(".imported/_/%s", ref)))
}
return urls, err
}
func importCmd(dockerCli command.Cli, _ RootOptions) *cobra.Command {
var options importOptions
cmd := &cobra.Command{
Use: "import [OPTIONS] < bundle.dockerbuild",
Short: "Import a build into Docker Desktop",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return runImport(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
}
flags := cmd.Flags()
flags.StringArrayVarP(&options.file, "file", "f", nil, "Import from a file path")
return cmd
}

893
commands/history/inspect.go Normal file
View File

@@ -0,0 +1,893 @@
package history
import (
"bytes"
"cmp"
"context"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"slices"
"strconv"
"strings"
"text/tabwriter"
"text/template"
"time"
"github.com/containerd/containerd/v2/core/content"
"github.com/containerd/containerd/v2/core/content/proxy"
"github.com/containerd/containerd/v2/core/images"
"github.com/containerd/platforms"
"github.com/docker/buildx/builder"
"github.com/docker/buildx/localstate"
"github.com/docker/buildx/util/cobrautil/completion"
"github.com/docker/buildx/util/confutil"
"github.com/docker/buildx/util/desktop"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/formatter"
"github.com/docker/cli/cli/debug"
slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
controlapi "github.com/moby/buildkit/api/services/control"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/solver/errdefs"
provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types"
"github.com/moby/buildkit/util/grpcerrors"
"github.com/moby/buildkit/util/stack"
"github.com/opencontainers/go-digest"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/tonistiigi/go-csvvalue"
spb "google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
proto "google.golang.org/protobuf/proto"
)
type statusT string
const (
statusComplete statusT = "completed"
statusRunning statusT = "running"
statusError statusT = "failed"
statusCanceled statusT = "canceled"
)
type inspectOptions struct {
builder string
ref string
format string
}
type inspectOutput struct {
Name string `json:",omitempty"`
Ref string
Context string `json:",omitempty"`
Dockerfile string `json:",omitempty"`
VCSRepository string `json:",omitempty"`
VCSRevision string `json:",omitempty"`
Target string `json:",omitempty"`
Platform []string `json:",omitempty"`
KeepGitDir bool `json:",omitempty"`
NamedContexts []keyValueOutput `json:",omitempty"`
StartedAt *time.Time `json:",omitempty"`
CompletedAt *time.Time `json:",omitempty"`
Duration time.Duration `json:",omitempty"`
Status statusT `json:",omitempty"`
Error *errorOutput `json:",omitempty"`
NumCompletedSteps int32
NumTotalSteps int32
NumCachedSteps int32
BuildArgs []keyValueOutput `json:",omitempty"`
Labels []keyValueOutput `json:",omitempty"`
Config configOutput `json:",omitempty"`
Materials []materialOutput `json:",omitempty"`
Attachments []attachmentOutput `json:",omitempty"`
Errors []string `json:",omitempty"`
}
type configOutput struct {
Network string `json:",omitempty"`
ExtraHosts []string `json:",omitempty"`
Hostname string `json:",omitempty"`
CgroupParent string `json:",omitempty"`
ImageResolveMode string `json:",omitempty"`
MultiPlatform bool `json:",omitempty"`
NoCache bool `json:",omitempty"`
NoCacheFilter []string `json:",omitempty"`
ShmSize string `json:",omitempty"`
Ulimit string `json:",omitempty"`
CacheMountNS string `json:",omitempty"`
DockerfileCheckConfig string `json:",omitempty"`
SourceDateEpoch string `json:",omitempty"`
SandboxHostname string `json:",omitempty"`
RestRaw []keyValueOutput `json:",omitempty"`
}
type materialOutput struct {
URI string `json:",omitempty"`
Digests []string `json:",omitempty"`
}
type attachmentOutput struct {
Digest string `json:",omitempty"`
Platform string `json:",omitempty"`
Type string `json:",omitempty"`
}
type errorOutput struct {
Code int `json:",omitempty"`
Message string `json:",omitempty"`
Name string `json:",omitempty"`
Logs []string `json:",omitempty"`
Sources []byte `json:",omitempty"`
Stack []byte `json:",omitempty"`
}
type keyValueOutput struct {
Name string `json:",omitempty"`
Value string `json:",omitempty"`
}
func readAttr[T any](attrs map[string]string, k string, dest *T, f func(v string) (T, bool)) {
if sv, ok := attrs[k]; ok {
if f != nil {
v, ok := f(sv)
if ok {
*dest = v
}
}
if d, ok := any(dest).(*string); ok {
*d = sv
}
}
delete(attrs, k)
}
func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions) error {
b, err := builder.New(dockerCli, builder.WithName(opts.builder))
if err != nil {
return err
}
nodes, err := b.LoadNodes(ctx)
if err != nil {
return err
}
for _, node := range nodes {
if node.Err != nil {
return node.Err
}
}
recs, err := queryRecords(ctx, opts.ref, nodes, nil)
if err != nil {
return err
}
if len(recs) == 0 {
if opts.ref == "" {
return errors.New("no records found")
}
return errors.Errorf("no record found for ref %q", opts.ref)
}
rec := &recs[0]
c, err := rec.node.Driver.Client(ctx)
if err != nil {
return err
}
store := proxy.NewContentStore(c.ContentClient())
var defaultPlatform string
workers, err := c.ListWorkers(ctx)
if err != nil {
return errors.Wrap(err, "failed to list workers")
}
workers0:
for _, w := range workers {
for _, p := range w.Platforms {
defaultPlatform = platforms.FormatAll(platforms.Normalize(p))
break workers0
}
}
ls, err := localstate.New(confutil.NewConfig(dockerCli))
if err != nil {
return err
}
st, _ := ls.ReadRef(rec.node.Builder, rec.node.Name, rec.Ref)
attrs := rec.FrontendAttrs
delete(attrs, "frontend.caps")
var out inspectOutput
var context string
var dockerfile string
if st != nil {
context = st.LocalPath
dockerfile = st.DockerfilePath
wd, _ := os.Getwd()
if dockerfile != "" && dockerfile != "-" {
if rel, err := filepath.Rel(context, dockerfile); err == nil {
if !strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
dockerfile = rel
}
}
}
if context != "" {
if rel, err := filepath.Rel(wd, context); err == nil {
if !strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
context = rel
}
}
}
}
if v, ok := attrs["context"]; ok && context == "" {
delete(attrs, "context")
context = v
}
if dockerfile == "" {
if v, ok := attrs["filename"]; ok {
dockerfile = v
if dfdir, ok := attrs["vcs:localdir:dockerfile"]; ok {
dockerfile = filepath.Join(dfdir, dockerfile)
}
}
}
delete(attrs, "filename")
out.Name = buildName(rec.FrontendAttrs, st)
out.Ref = rec.Ref
out.Context = context
out.Dockerfile = dockerfile
if _, ok := attrs["context"]; !ok {
if src, ok := attrs["vcs:source"]; ok {
out.VCSRepository = src
}
if rev, ok := attrs["vcs:revision"]; ok {
out.VCSRevision = rev
}
}
readAttr(attrs, "target", &out.Target, nil)
readAttr(attrs, "platform", &out.Platform, func(v string) ([]string, bool) {
return tryParseValue(v, &out.Errors, func(v string) ([]string, error) {
var pp []string
for _, v := range strings.Split(v, ",") {
p, err := platforms.Parse(v)
if err != nil {
return nil, err
}
pp = append(pp, platforms.FormatAll(platforms.Normalize(p)))
}
if len(pp) == 0 {
pp = append(pp, defaultPlatform)
}
return pp, nil
})
})
readAttr(attrs, "build-arg:BUILDKIT_CONTEXT_KEEP_GIT_DIR", &out.KeepGitDir, func(v string) (bool, bool) {
return tryParseValue(v, &out.Errors, strconv.ParseBool)
})
out.NamedContexts = readKeyValues(attrs, "context:")
if rec.CreatedAt != nil {
tm := rec.CreatedAt.AsTime().Local()
out.StartedAt = &tm
}
out.Status = statusRunning
if rec.CompletedAt != nil {
tm := rec.CompletedAt.AsTime().Local()
out.CompletedAt = &tm
out.Status = statusComplete
}
if rec.Error != nil || rec.ExternalError != nil {
out.Error = &errorOutput{}
if rec.Error != nil {
if codes.Code(rec.Error.Code) == codes.Canceled {
out.Status = statusCanceled
} else {
out.Status = statusError
}
out.Error.Code = int(codes.Code(rec.Error.Code))
out.Error.Message = rec.Error.Message
}
if rec.ExternalError != nil {
dt, err := content.ReadBlob(ctx, store, ociDesc(rec.ExternalError))
if err != nil {
return errors.Wrapf(err, "failed to read external error %s", rec.ExternalError.Digest)
}
var st spb.Status
if err := proto.Unmarshal(dt, &st); err != nil {
return errors.Wrapf(err, "failed to unmarshal external error %s", rec.ExternalError.Digest)
}
retErr := grpcerrors.FromGRPC(status.ErrorProto(&st))
var errsources bytes.Buffer
for _, s := range errdefs.Sources(retErr) {
s.Print(&errsources)
errsources.WriteString("\n")
}
out.Error.Sources = errsources.Bytes()
var ve *errdefs.VertexError
if errors.As(retErr, &ve) {
dgst, err := digest.Parse(ve.Vertex.Digest)
if err != nil {
return errors.Wrapf(err, "failed to parse vertex digest %s", ve.Vertex.Digest)
}
name, logs, err := loadVertexLogs(ctx, c, rec.Ref, dgst, 16)
if err != nil {
return errors.Wrapf(err, "failed to load vertex logs %s", dgst)
}
out.Error.Name = name
out.Error.Logs = logs
}
out.Error.Stack = fmt.Appendf(nil, "%+v", stack.Formatter(retErr))
}
}
if out.StartedAt != nil {
if out.CompletedAt != nil {
out.Duration = out.CompletedAt.Sub(*out.StartedAt)
} else {
out.Duration = rec.currentTimestamp.Sub(*out.StartedAt)
}
}
out.NumCompletedSteps = rec.NumCompletedSteps
out.NumTotalSteps = rec.NumTotalSteps
out.NumCachedSteps = rec.NumCachedSteps
out.BuildArgs = readKeyValues(attrs, "build-arg:")
out.Labels = readKeyValues(attrs, "label:")
readAttr(attrs, "force-network-mode", &out.Config.Network, nil)
readAttr(attrs, "hostname", &out.Config.Hostname, nil)
readAttr(attrs, "cgroup-parent", &out.Config.CgroupParent, nil)
readAttr(attrs, "image-resolve-mode", &out.Config.ImageResolveMode, nil)
readAttr(attrs, "build-arg:BUILDKIT_MULTI_PLATFORM", &out.Config.MultiPlatform, func(v string) (bool, bool) {
return tryParseValue(v, &out.Errors, strconv.ParseBool)
})
readAttr(attrs, "multi-platform", &out.Config.MultiPlatform, func(v string) (bool, bool) {
return tryParseValue(v, &out.Errors, strconv.ParseBool)
})
readAttr(attrs, "no-cache", &out.Config.NoCache, func(v string) (bool, bool) {
if v == "" {
return true, true
}
return false, false
})
readAttr(attrs, "no-cache", &out.Config.NoCacheFilter, func(v string) ([]string, bool) {
if v == "" {
return nil, false
}
return strings.Split(v, ","), true
})
readAttr(attrs, "add-hosts", &out.Config.ExtraHosts, func(v string) ([]string, bool) {
return tryParseValue(v, &out.Errors, func(v string) ([]string, error) {
fields, err := csvvalue.Fields(v, nil)
if err != nil {
return nil, err
}
return fields, nil
})
})
readAttr(attrs, "shm-size", &out.Config.ShmSize, nil)
readAttr(attrs, "ulimit", &out.Config.Ulimit, nil)
readAttr(attrs, "build-arg:BUILDKIT_CACHE_MOUNT_NS", &out.Config.CacheMountNS, nil)
readAttr(attrs, "build-arg:BUILDKIT_DOCKERFILE_CHECK", &out.Config.DockerfileCheckConfig, nil)
readAttr(attrs, "build-arg:SOURCE_DATE_EPOCH", &out.Config.SourceDateEpoch, nil)
readAttr(attrs, "build-arg:SANDBOX_HOSTNAME", &out.Config.SandboxHostname, nil)
var unusedAttrs []keyValueOutput
for k := range attrs {
if strings.HasPrefix(k, "vcs:") || strings.HasPrefix(k, "build-arg:") || strings.HasPrefix(k, "label:") || strings.HasPrefix(k, "context:") || strings.HasPrefix(k, "attest:") {
continue
}
unusedAttrs = append(unusedAttrs, keyValueOutput{
Name: k,
Value: attrs[k],
})
}
slices.SortFunc(unusedAttrs, func(a, b keyValueOutput) int {
return cmp.Compare(a.Name, b.Name)
})
out.Config.RestRaw = unusedAttrs
attachments, err := allAttachments(ctx, store, *rec)
if err != nil {
return err
}
provIndex := slices.IndexFunc(attachments, func(a attachment) bool {
return descrType(a.descr) == slsa02.PredicateSLSAProvenance
})
if provIndex != -1 {
prov := attachments[provIndex]
dt, err := content.ReadBlob(ctx, store, prov.descr)
if err != nil {
return errors.Errorf("failed to read provenance %s: %v", prov.descr.Digest, err)
}
var pred provenancetypes.ProvenancePredicate
if err := json.Unmarshal(dt, &pred); err != nil {
return errors.Errorf("failed to unmarshal provenance %s: %v", prov.descr.Digest, err)
}
for _, m := range pred.Materials {
out.Materials = append(out.Materials, materialOutput{
URI: m.URI,
Digests: digestSetToDigests(m.Digest),
})
}
}
if len(attachments) > 0 {
for _, a := range attachments {
p := ""
if a.platform != nil {
p = platforms.FormatAll(*a.platform)
}
out.Attachments = append(out.Attachments, attachmentOutput{
Digest: a.descr.Digest.String(),
Platform: p,
Type: descrType(a.descr),
})
}
}
if opts.format == formatter.JSONFormatKey {
enc := json.NewEncoder(dockerCli.Out())
enc.SetIndent("", " ")
return enc.Encode(out)
} else if opts.format != formatter.PrettyFormatKey {
tmpl, err := template.New("inspect").Parse(opts.format)
if err != nil {
return errors.Wrapf(err, "failed to parse format template")
}
var buf bytes.Buffer
if err := tmpl.Execute(&buf, out); err != nil {
return errors.Wrapf(err, "failed to execute format template")
}
fmt.Fprintln(dockerCli.Out(), buf.String())
return nil
}
tw := tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
if out.Name != "" {
fmt.Fprintf(tw, "Name:\t%s\n", out.Name)
}
if opts.ref == "" && out.Ref != "" {
fmt.Fprintf(tw, "Ref:\t%s\n", out.Ref)
}
if out.Context != "" {
fmt.Fprintf(tw, "Context:\t%s\n", out.Context)
}
if out.Dockerfile != "" {
fmt.Fprintf(tw, "Dockerfile:\t%s\n", out.Dockerfile)
}
if out.VCSRepository != "" {
fmt.Fprintf(tw, "VCS Repository:\t%s\n", out.VCSRepository)
}
if out.VCSRevision != "" {
fmt.Fprintf(tw, "VCS Revision:\t%s\n", out.VCSRevision)
}
if out.Target != "" {
fmt.Fprintf(tw, "Target:\t%s\n", out.Target)
}
if len(out.Platform) > 0 {
fmt.Fprintf(tw, "Platforms:\t%s\n", strings.Join(out.Platform, ", "))
}
if out.KeepGitDir {
fmt.Fprintf(tw, "Keep Git Dir:\t%s\n", strconv.FormatBool(out.KeepGitDir))
}
tw.Flush()
fmt.Fprintln(dockerCli.Out())
printTable(dockerCli.Out(), out.NamedContexts, "Named Context")
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
fmt.Fprintf(tw, "Started:\t%s\n", out.StartedAt.Format("2006-01-02 15:04:05"))
var statusStr string
if out.Status == statusRunning {
statusStr = " (running)"
}
fmt.Fprintf(tw, "Duration:\t%s%s\n", formatDuration(out.Duration), statusStr)
if out.Status == statusError {
fmt.Fprintf(tw, "Error:\t%s %s\n", codes.Code(rec.Error.Code).String(), rec.Error.Message)
} else if out.Status == statusCanceled {
fmt.Fprintf(tw, "Status:\tCanceled\n")
}
fmt.Fprintf(tw, "Build Steps:\t%d/%d (%.0f%% cached)\n", out.NumCompletedSteps, out.NumTotalSteps, float64(out.NumCachedSteps)/float64(out.NumTotalSteps)*100)
tw.Flush()
fmt.Fprintln(dockerCli.Out())
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
if out.Config.Network != "" {
fmt.Fprintf(tw, "Network:\t%s\n", out.Config.Network)
}
if out.Config.Hostname != "" {
fmt.Fprintf(tw, "Hostname:\t%s\n", out.Config.Hostname)
}
if len(out.Config.ExtraHosts) > 0 {
fmt.Fprintf(tw, "Extra Hosts:\t%s\n", strings.Join(out.Config.ExtraHosts, ", "))
}
if out.Config.CgroupParent != "" {
fmt.Fprintf(tw, "Cgroup Parent:\t%s\n", out.Config.CgroupParent)
}
if out.Config.ImageResolveMode != "" {
fmt.Fprintf(tw, "Image Resolve Mode:\t%s\n", out.Config.ImageResolveMode)
}
if out.Config.MultiPlatform {
fmt.Fprintf(tw, "Multi-Platform:\t%s\n", strconv.FormatBool(out.Config.MultiPlatform))
}
if out.Config.NoCache {
fmt.Fprintf(tw, "No Cache:\t%s\n", strconv.FormatBool(out.Config.NoCache))
}
if len(out.Config.NoCacheFilter) > 0 {
fmt.Fprintf(tw, "No Cache Filter:\t%s\n", strings.Join(out.Config.NoCacheFilter, ", "))
}
if out.Config.ShmSize != "" {
fmt.Fprintf(tw, "Shm Size:\t%s\n", out.Config.ShmSize)
}
if out.Config.Ulimit != "" {
fmt.Fprintf(tw, "Resource Limits:\t%s\n", out.Config.Ulimit)
}
if out.Config.CacheMountNS != "" {
fmt.Fprintf(tw, "Cache Mount Namespace:\t%s\n", out.Config.CacheMountNS)
}
if out.Config.DockerfileCheckConfig != "" {
fmt.Fprintf(tw, "Dockerfile Check Config:\t%s\n", out.Config.DockerfileCheckConfig)
}
if out.Config.SourceDateEpoch != "" {
fmt.Fprintf(tw, "Source Date Epoch:\t%s\n", out.Config.SourceDateEpoch)
}
if out.Config.SandboxHostname != "" {
fmt.Fprintf(tw, "Sandbox Hostname:\t%s\n", out.Config.SandboxHostname)
}
for _, kv := range out.Config.RestRaw {
fmt.Fprintf(tw, "%s:\t%s\n", kv.Name, kv.Value)
}
tw.Flush()
fmt.Fprintln(dockerCli.Out())
printTable(dockerCli.Out(), out.BuildArgs, "Build Arg")
printTable(dockerCli.Out(), out.Labels, "Label")
if len(out.Materials) > 0 {
fmt.Fprintln(dockerCli.Out(), "Materials:")
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
fmt.Fprintf(tw, "URI\tDIGEST\n")
for _, m := range out.Materials {
fmt.Fprintf(tw, "%s\t%s\n", m.URI, strings.Join(m.Digests, ", "))
}
tw.Flush()
fmt.Fprintln(dockerCli.Out())
}
if len(out.Attachments) > 0 {
fmt.Fprintf(tw, "Attachments:\n")
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
fmt.Fprintf(tw, "DIGEST\tPLATFORM\tTYPE\n")
for _, a := range out.Attachments {
fmt.Fprintf(tw, "%s\t%s\t%s\n", a.Digest, a.Platform, a.Type)
}
tw.Flush()
fmt.Fprintln(dockerCli.Out())
}
if out.Error != nil {
if out.Error.Sources != nil {
fmt.Fprint(dockerCli.Out(), string(out.Error.Sources))
}
if len(out.Error.Logs) > 0 {
fmt.Fprintln(dockerCli.Out(), "Logs:")
fmt.Fprintf(dockerCli.Out(), "> => %s:\n", out.Error.Name)
for _, l := range out.Error.Logs {
fmt.Fprintln(dockerCli.Out(), "> "+l)
}
fmt.Fprintln(dockerCli.Out())
}
if len(out.Error.Stack) > 0 {
if debug.IsEnabled() {
fmt.Fprintf(dockerCli.Out(), "\n%s\n", out.Error.Stack)
} else {
fmt.Fprintf(dockerCli.Out(), "Enable --debug to see stack traces for error\n")
}
}
}
fmt.Fprintf(dockerCli.Out(), "Print build logs: docker buildx history logs %s\n", rec.Ref)
fmt.Fprintf(dockerCli.Out(), "View build in Docker Desktop: %s\n", desktop.BuildURL(fmt.Sprintf("%s/%s/%s", rec.node.Builder, rec.node.Name, rec.Ref)))
return nil
}
func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
var options inspectOptions
cmd := &cobra.Command{
Use: "inspect [OPTIONS] [REF]",
Short: "Inspect a build",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
options.ref = args[0]
}
options.builder = *rootOpts.Builder
return runInspect(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
}
cmd.AddCommand(
attachmentCmd(dockerCli, rootOpts),
)
flags := cmd.Flags()
flags.StringVar(&options.format, "format", formatter.PrettyFormatKey, "Format the output")
return cmd
}
func loadVertexLogs(ctx context.Context, c *client.Client, ref string, dgst digest.Digest, limit int) (string, []string, error) {
st, err := c.ControlClient().Status(ctx, &controlapi.StatusRequest{
Ref: ref,
})
if err != nil {
return "", nil, err
}
var name string
var logs []string
lastState := map[int]int{}
loop0:
for {
select {
case <-ctx.Done():
st.CloseSend()
return "", nil, context.Cause(ctx)
default:
ev, err := st.Recv()
if err != nil {
if errors.Is(err, io.EOF) {
break loop0
}
return "", nil, err
}
ss := client.NewSolveStatus(ev)
for _, v := range ss.Vertexes {
if v.Digest == dgst {
name = v.Name
break
}
}
for _, l := range ss.Logs {
if l.Vertex == dgst {
parts := bytes.Split(l.Data, []byte("\n"))
for i, p := range parts {
var wrote bool
if i == 0 {
idx, ok := lastState[l.Stream]
if ok && idx != -1 {
logs[idx] = logs[idx] + string(p)
wrote = true
}
}
if !wrote {
if len(p) > 0 {
logs = append(logs, string(p))
}
lastState[l.Stream] = len(logs) - 1
}
if i == len(parts)-1 && len(p) == 0 {
lastState[l.Stream] = -1
}
}
}
}
}
}
if limit > 0 && len(logs) > limit {
logs = logs[len(logs)-limit:]
}
return name, logs, nil
}
type attachment struct {
platform *ocispecs.Platform
descr ocispecs.Descriptor
}
func allAttachments(ctx context.Context, store content.Store, rec historyRecord) ([]attachment, error) {
var attachments []attachment
if rec.Result != nil {
for _, a := range rec.Result.Attestations {
attachments = append(attachments, attachment{
descr: ociDesc(a),
})
}
for _, r := range rec.Result.Results {
attachments = append(attachments, walkAttachments(ctx, store, ociDesc(r), nil)...)
}
}
for key, ri := range rec.Results {
p, err := platforms.Parse(key)
if err != nil {
return nil, err
}
for _, a := range ri.Attestations {
attachments = append(attachments, attachment{
platform: &p,
descr: ociDesc(a),
})
}
for _, r := range ri.Results {
attachments = append(attachments, walkAttachments(ctx, store, ociDesc(r), &p)...)
}
}
slices.SortFunc(attachments, func(a, b attachment) int {
pCmp := 0
if a.platform == nil && b.platform != nil {
return -1
} else if a.platform != nil && b.platform == nil {
return 1
} else if a.platform != nil && b.platform != nil {
pCmp = cmp.Compare(platforms.FormatAll(*a.platform), platforms.FormatAll(*b.platform))
}
return cmp.Or(
pCmp,
cmp.Compare(descrType(a.descr), descrType(b.descr)),
)
})
return attachments, nil
}
func walkAttachments(ctx context.Context, store content.Store, desc ocispecs.Descriptor, platform *ocispecs.Platform) []attachment {
_, err := store.Info(ctx, desc.Digest)
if err != nil {
return nil
}
var out []attachment
if desc.Annotations["vnd.docker.reference.type"] != "attestation-manifest" {
out = append(out, attachment{platform: platform, descr: desc})
}
if desc.MediaType != ocispecs.MediaTypeImageIndex && desc.MediaType != images.MediaTypeDockerSchema2ManifestList {
return out
}
dt, err := content.ReadBlob(ctx, store, desc)
if err != nil {
return out
}
var idx ocispecs.Index
if err := json.Unmarshal(dt, &idx); err != nil {
return out
}
for _, d := range idx.Manifests {
p := platform
if d.Platform != nil {
p = d.Platform
}
out = append(out, walkAttachments(ctx, store, d, p)...)
}
return out
}
func ociDesc(in *controlapi.Descriptor) ocispecs.Descriptor {
return ocispecs.Descriptor{
MediaType: in.MediaType,
Digest: digest.Digest(in.Digest),
Size: in.Size,
Annotations: in.Annotations,
}
}
func descrType(desc ocispecs.Descriptor) string {
if typ, ok := desc.Annotations["in-toto.io/predicate-type"]; ok {
return typ
}
return desc.MediaType
}
func tryParseValue[T any](s string, errs *[]string, f func(string) (T, error)) (T, bool) {
v, err := f(s)
if err != nil {
errStr := fmt.Sprintf("failed to parse %s: (%v)", s, err)
*errs = append(*errs, errStr)
}
return v, true
}
func printTable(w io.Writer, kvs []keyValueOutput, title string) {
if len(kvs) == 0 {
return
}
tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
fmt.Fprintf(tw, "%s\tVALUE\n", strings.ToUpper(title))
for _, k := range kvs {
fmt.Fprintf(tw, "%s\t%s\n", k.Name, k.Value)
}
tw.Flush()
fmt.Fprintln(w)
}
func readKeyValues(attrs map[string]string, prefix string) []keyValueOutput {
var out []keyValueOutput
for k, v := range attrs {
if strings.HasPrefix(k, prefix) {
out = append(out, keyValueOutput{
Name: strings.TrimPrefix(k, prefix),
Value: v,
})
}
}
if len(out) == 0 {
return nil
}
slices.SortFunc(out, func(a, b keyValueOutput) int {
return cmp.Compare(a.Name, b.Name)
})
return out
}
func digestSetToDigests(ds slsa.DigestSet) []string {
var out []string
for k, v := range ds {
out = append(out, fmt.Sprintf("%s:%s", k, v))
}
return out
}

View File

@@ -0,0 +1,145 @@
package history
import (
"context"
"io"
"github.com/containerd/containerd/v2/core/content/proxy"
"github.com/containerd/platforms"
"github.com/docker/buildx/builder"
"github.com/docker/buildx/util/cobrautil/completion"
"github.com/docker/cli/cli/command"
intoto "github.com/in-toto/in-toto-golang/in_toto"
slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
"github.com/opencontainers/go-digest"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
type attachmentOptions struct {
builder string
typ string
platform string
ref string
digest digest.Digest
}
func runAttachment(ctx context.Context, dockerCli command.Cli, opts attachmentOptions) error {
b, err := builder.New(dockerCli, builder.WithName(opts.builder))
if err != nil {
return err
}
nodes, err := b.LoadNodes(ctx)
if err != nil {
return err
}
for _, node := range nodes {
if node.Err != nil {
return node.Err
}
}
recs, err := queryRecords(ctx, opts.ref, nodes, nil)
if err != nil {
return err
}
if len(recs) == 0 {
if opts.ref == "" {
return errors.New("no records found")
}
return errors.Errorf("no record found for ref %q", opts.ref)
}
rec := &recs[0]
c, err := rec.node.Driver.Client(ctx)
if err != nil {
return err
}
store := proxy.NewContentStore(c.ContentClient())
if opts.digest != "" {
ra, err := store.ReaderAt(ctx, ocispecs.Descriptor{Digest: opts.digest})
if err != nil {
return err
}
_, err = io.Copy(dockerCli.Out(), io.NewSectionReader(ra, 0, ra.Size()))
return err
}
attachments, err := allAttachments(ctx, store, *rec)
if err != nil {
return err
}
typ := opts.typ
switch typ {
case "index":
typ = ocispecs.MediaTypeImageIndex
case "manifest":
typ = ocispecs.MediaTypeImageManifest
case "image":
typ = ocispecs.MediaTypeImageConfig
case "provenance":
typ = slsa02.PredicateSLSAProvenance
case "sbom":
typ = intoto.PredicateSPDX
}
for _, a := range attachments {
if opts.platform != "" && (a.platform == nil || platforms.FormatAll(*a.platform) != opts.platform) {
continue
}
if typ != "" && descrType(a.descr) != typ {
continue
}
ra, err := store.ReaderAt(ctx, a.descr)
if err != nil {
return err
}
_, err = io.Copy(dockerCli.Out(), io.NewSectionReader(ra, 0, ra.Size()))
return err
}
return errors.Errorf("no matching attachment found for ref %q", opts.ref)
}
func attachmentCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
var options attachmentOptions
cmd := &cobra.Command{
Use: "attachment [OPTIONS] REF [DIGEST]",
Short: "Inspect a build attachment",
Args: cobra.RangeArgs(1, 2),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
options.ref = args[0]
}
if len(args) > 1 {
dgst, err := digest.Parse(args[1])
if err != nil {
return errors.Wrapf(err, "invalid digest %q", args[1])
}
options.digest = dgst
}
if options.digest == "" && options.platform == "" && options.typ == "" {
return errors.New("at least one of --type, --platform or DIGEST must be specified")
}
options.builder = *rootOpts.Builder
return runAttachment(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
}
flags := cmd.Flags()
flags.StringVar(&options.typ, "type", "", "Type of attachment")
flags.StringVar(&options.platform, "platform", "", "Platform of attachment")
return cmd
}

117
commands/history/logs.go Normal file
View File

@@ -0,0 +1,117 @@
package history
import (
"context"
"io"
"os"
"github.com/docker/buildx/builder"
"github.com/docker/buildx/util/cobrautil/completion"
"github.com/docker/buildx/util/progress"
"github.com/docker/cli/cli/command"
controlapi "github.com/moby/buildkit/api/services/control"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/util/progress/progressui"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
type logsOptions struct {
builder string
ref string
progress string
}
func runLogs(ctx context.Context, dockerCli command.Cli, opts logsOptions) error {
b, err := builder.New(dockerCli, builder.WithName(opts.builder))
if err != nil {
return err
}
nodes, err := b.LoadNodes(ctx)
if err != nil {
return err
}
for _, node := range nodes {
if node.Err != nil {
return node.Err
}
}
recs, err := queryRecords(ctx, opts.ref, nodes, nil)
if err != nil {
return err
}
if len(recs) == 0 {
if opts.ref == "" {
return errors.New("no records found")
}
return errors.Errorf("no record found for ref %q", opts.ref)
}
rec := &recs[0]
c, err := rec.node.Driver.Client(ctx)
if err != nil {
return err
}
cl, err := c.ControlClient().Status(ctx, &controlapi.StatusRequest{
Ref: rec.Ref,
})
if err != nil {
return err
}
var mode progressui.DisplayMode = progressui.DisplayMode(opts.progress)
if mode == progressui.AutoMode {
mode = progressui.PlainMode
}
printer, err := progress.NewPrinter(context.TODO(), os.Stderr, mode)
if err != nil {
return err
}
loop0:
for {
select {
case <-ctx.Done():
cl.CloseSend()
return context.Cause(ctx)
default:
ev, err := cl.Recv()
if err != nil {
if errors.Is(err, io.EOF) {
break loop0
}
return err
}
printer.Write(client.NewSolveStatus(ev))
}
}
return printer.Wait()
}
func logsCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
var options logsOptions
cmd := &cobra.Command{
Use: "logs [OPTIONS] [REF]",
Short: "Print the logs of a build",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
options.ref = args[0]
}
options.builder = *rootOpts.Builder
return runLogs(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
}
flags := cmd.Flags()
flags.StringVar(&options.progress, "progress", "plain", "Set type of progress output (plain, rawjson, tty)")
return cmd
}

234
commands/history/ls.go Normal file
View File

@@ -0,0 +1,234 @@
package history
import (
"context"
"encoding/json"
"fmt"
"os"
"slices"
"time"
"github.com/containerd/console"
"github.com/docker/buildx/builder"
"github.com/docker/buildx/localstate"
"github.com/docker/buildx/util/cobrautil/completion"
"github.com/docker/buildx/util/confutil"
"github.com/docker/buildx/util/desktop"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/formatter"
"github.com/docker/go-units"
"github.com/spf13/cobra"
)
const (
lsHeaderBuildID = "BUILD ID"
lsHeaderName = "NAME"
lsHeaderStatus = "STATUS"
lsHeaderCreated = "CREATED AT"
lsHeaderDuration = "DURATION"
lsHeaderLink = ""
lsDefaultTableFormat = "table {{.Ref}}\t{{.Name}}\t{{.Status}}\t{{.CreatedAt}}\t{{.Duration}}\t{{.Link}}"
headerKeyTimestamp = "buildkit-current-timestamp"
)
type lsOptions struct {
builder string
format string
noTrunc bool
}
func runLs(ctx context.Context, dockerCli command.Cli, opts lsOptions) error {
b, err := builder.New(dockerCli, builder.WithName(opts.builder))
if err != nil {
return err
}
nodes, err := b.LoadNodes(ctx)
if err != nil {
return err
}
for _, node := range nodes {
if node.Err != nil {
return node.Err
}
}
out, err := queryRecords(ctx, "", nodes, nil)
if err != nil {
return err
}
ls, err := localstate.New(confutil.NewConfig(dockerCli))
if err != nil {
return err
}
for i, rec := range out {
st, _ := ls.ReadRef(rec.node.Builder, rec.node.Name, rec.Ref)
rec.name = buildName(rec.FrontendAttrs, st)
out[i] = rec
}
return lsPrint(dockerCli, out, opts)
}
func lsCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
var options lsOptions
cmd := &cobra.Command{
Use: "ls",
Short: "List build records",
Args: cli.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
options.builder = *rootOpts.Builder
return runLs(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
}
flags := cmd.Flags()
flags.StringVar(&options.format, "format", formatter.TableFormatKey, "Format the output")
flags.BoolVar(&options.noTrunc, "no-trunc", false, "Don't truncate output")
return cmd
}
func lsPrint(dockerCli command.Cli, records []historyRecord, in lsOptions) error {
if in.format == formatter.TableFormatKey {
in.format = lsDefaultTableFormat
}
ctx := formatter.Context{
Output: dockerCli.Out(),
Format: formatter.Format(in.format),
Trunc: !in.noTrunc,
}
slices.SortFunc(records, func(a, b historyRecord) int {
if a.CompletedAt == nil && b.CompletedAt != nil {
return -1
}
if a.CompletedAt != nil && b.CompletedAt == nil {
return 1
}
return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime())
})
var term bool
if _, err := console.ConsoleFromFile(os.Stdout); err == nil {
term = true
}
render := func(format func(subContext formatter.SubContext) error) error {
for _, r := range records {
if err := format(&lsContext{
format: formatter.Format(in.format),
isTerm: term,
trunc: !in.noTrunc,
record: &r,
}); err != nil {
return err
}
}
return nil
}
lsCtx := lsContext{
isTerm: term,
trunc: !in.noTrunc,
}
lsCtx.Header = formatter.SubHeaderContext{
"Ref": lsHeaderBuildID,
"Name": lsHeaderName,
"Status": lsHeaderStatus,
"CreatedAt": lsHeaderCreated,
"Duration": lsHeaderDuration,
"Link": lsHeaderLink,
}
return ctx.Write(&lsCtx, render)
}
type lsContext struct {
formatter.HeaderContext
isTerm bool
trunc bool
format formatter.Format
record *historyRecord
}
func (c *lsContext) MarshalJSON() ([]byte, error) {
m := map[string]any{
"ref": c.FullRef(),
"name": c.Name(),
"status": c.Status(),
"created_at": c.record.CreatedAt.AsTime().Format(time.RFC3339Nano),
"total_steps": c.record.NumTotalSteps,
"completed_steps": c.record.NumCompletedSteps,
"cached_steps": c.record.NumCachedSteps,
}
if c.record.CompletedAt != nil {
m["completed_at"] = c.record.CompletedAt.AsTime().Format(time.RFC3339Nano)
}
return json.Marshal(m)
}
func (c *lsContext) Ref() string {
return c.record.Ref
}
func (c *lsContext) FullRef() string {
return fmt.Sprintf("%s/%s/%s", c.record.node.Builder, c.record.node.Name, c.record.Ref)
}
func (c *lsContext) Name() string {
name := c.record.name
if c.trunc && c.format.IsTable() {
return trimBeginning(name, 36)
}
return name
}
func (c *lsContext) Status() string {
if c.record.CompletedAt != nil {
if c.record.Error != nil {
return "Error"
}
return "Completed"
}
return "Running"
}
func (c *lsContext) CreatedAt() string {
return units.HumanDuration(time.Since(c.record.CreatedAt.AsTime())) + " ago"
}
func (c *lsContext) Duration() string {
lastTime := c.record.currentTimestamp
if c.record.CompletedAt != nil {
tm := c.record.CompletedAt.AsTime()
lastTime = &tm
}
if lastTime == nil {
return ""
}
v := formatDuration(lastTime.Sub(c.record.CreatedAt.AsTime()))
if c.record.CompletedAt == nil {
v += "+"
}
return v
}
func (c *lsContext) Link() string {
url := desktop.BuildURL(c.FullRef())
if c.format.IsTable() {
if c.isTerm {
return desktop.ANSIHyperlink(url, "Open")
}
return ""
}
return url
}

73
commands/history/open.go Normal file
View File

@@ -0,0 +1,73 @@
package history
import (
"context"
"fmt"
"github.com/docker/buildx/builder"
"github.com/docker/buildx/util/cobrautil/completion"
"github.com/docker/buildx/util/desktop"
"github.com/docker/cli/cli/command"
"github.com/pkg/browser"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
type openOptions struct {
builder string
ref string
}
func runOpen(ctx context.Context, dockerCli command.Cli, opts openOptions) error {
b, err := builder.New(dockerCli, builder.WithName(opts.builder))
if err != nil {
return err
}
nodes, err := b.LoadNodes(ctx)
if err != nil {
return err
}
for _, node := range nodes {
if node.Err != nil {
return node.Err
}
}
recs, err := queryRecords(ctx, opts.ref, nodes, nil)
if err != nil {
return err
}
if len(recs) == 0 {
if opts.ref == "" {
return errors.New("no records found")
}
return errors.Errorf("no record found for ref %q", opts.ref)
}
rec := &recs[0]
url := desktop.BuildURL(fmt.Sprintf("%s/%s/%s", rec.node.Builder, rec.node.Name, rec.Ref))
return browser.OpenURL(url)
}
func openCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
var options openOptions
cmd := &cobra.Command{
Use: "open [OPTIONS] [REF]",
Short: "Open a build in Docker Desktop",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
options.ref = args[0]
}
options.builder = *rootOpts.Builder
return runOpen(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
}
return cmd
}

151
commands/history/rm.go Normal file
View File

@@ -0,0 +1,151 @@
package history
import (
"context"
"io"
"github.com/docker/buildx/builder"
"github.com/docker/buildx/util/cobrautil/completion"
"github.com/docker/cli/cli/command"
"github.com/hashicorp/go-multierror"
controlapi "github.com/moby/buildkit/api/services/control"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"
)
type rmOptions struct {
builder string
refs []string
all bool
}
func runRm(ctx context.Context, dockerCli command.Cli, opts rmOptions) error {
b, err := builder.New(dockerCli, builder.WithName(opts.builder))
if err != nil {
return err
}
nodes, err := b.LoadNodes(ctx)
if err != nil {
return err
}
for _, node := range nodes {
if node.Err != nil {
return node.Err
}
}
errs := make([][]error, len(opts.refs))
for i := range errs {
errs[i] = make([]error, len(nodes))
}
eg, ctx := errgroup.WithContext(ctx)
for i, node := range nodes {
node := node
eg.Go(func() error {
if node.Driver == nil {
return nil
}
c, err := node.Driver.Client(ctx)
if err != nil {
return err
}
refs := opts.refs
if opts.all {
serv, err := c.ControlClient().ListenBuildHistory(ctx, &controlapi.BuildHistoryRequest{
EarlyExit: true,
})
if err != nil {
return err
}
defer serv.CloseSend()
for {
resp, err := serv.Recv()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return err
}
if resp.Type == controlapi.BuildHistoryEventType_COMPLETE {
refs = append(refs, resp.Record.Ref)
}
}
}
for j, ref := range refs {
_, err = c.ControlClient().UpdateBuildHistory(ctx, &controlapi.UpdateBuildHistoryRequest{
Ref: ref,
Delete: true,
})
if opts.all {
if err != nil {
return err
}
} else {
errs[j][i] = err
}
}
return nil
})
}
if err := eg.Wait(); err != nil {
return err
}
var out []error
loop0:
for _, nodeErrs := range errs {
var nodeErr error
for _, err1 := range nodeErrs {
if err1 == nil {
continue loop0
}
if nodeErr == nil {
nodeErr = err1
} else {
nodeErr = multierror.Append(nodeErr, err1)
}
}
out = append(out, nodeErr)
}
if len(out) == 0 {
return nil
}
if len(out) == 1 {
return out[0]
}
return multierror.Append(out[0], out[1:]...)
}
func rmCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
var options rmOptions
cmd := &cobra.Command{
Use: "rm [OPTIONS] [REF...]",
Short: "Remove build records",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 && !options.all {
return errors.New("rm requires at least one argument")
}
if len(args) > 0 && options.all {
return errors.New("rm requires either --all or at least one argument")
}
options.refs = args
options.builder = *rootOpts.Builder
return runRm(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
}
flags := cmd.Flags()
flags.BoolVar(&options.all, "all", false, "Remove all build records")
return cmd
}

32
commands/history/root.go Normal file
View File

@@ -0,0 +1,32 @@
package history
import (
"github.com/docker/buildx/util/cobrautil/completion"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
)
type RootOptions struct {
Builder *string
}
func RootCmd(rootcmd *cobra.Command, dockerCli command.Cli, opts RootOptions) *cobra.Command {
cmd := &cobra.Command{
Use: "history",
Short: "Commands to work on build records",
ValidArgsFunction: completion.Disable,
RunE: rootcmd.RunE,
}
cmd.AddCommand(
lsCmd(dockerCli, opts),
rmCmd(dockerCli, opts),
logsCmd(dockerCli, opts),
inspectCmd(dockerCli, opts),
openCmd(dockerCli, opts),
traceCmd(dockerCli, opts),
importCmd(dockerCli, opts),
)
return cmd
}

228
commands/history/trace.go Normal file
View File

@@ -0,0 +1,228 @@
package history
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net"
"os"
"time"
"github.com/containerd/console"
"github.com/containerd/containerd/v2/core/content/proxy"
"github.com/docker/buildx/builder"
"github.com/docker/buildx/util/cobrautil/completion"
"github.com/docker/buildx/util/otelutil"
"github.com/docker/buildx/util/otelutil/jaeger"
"github.com/docker/cli/cli/command"
controlapi "github.com/moby/buildkit/api/services/control"
"github.com/opencontainers/go-digest"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/browser"
"github.com/pkg/errors"
"github.com/spf13/cobra"
jaegerui "github.com/tonistiigi/jaeger-ui-rest"
)
type traceOptions struct {
builder string
ref string
addr string
compare string
}
func loadTrace(ctx context.Context, ref string, nodes []builder.Node) (string, []byte, error) {
recs, err := queryRecords(ctx, ref, nodes, &queryOptions{
CompletedOnly: true,
})
if err != nil {
return "", nil, err
}
if len(recs) == 0 {
if ref == "" {
return "", nil, errors.New("no records found")
}
return "", nil, errors.Errorf("no record found for ref %q", ref)
}
rec := &recs[0]
if rec.CompletedAt == nil {
return "", nil, errors.Errorf("build %q is not completed, only completed builds can be traced", rec.Ref)
}
if rec.Trace == nil {
// build is complete but no trace yet. try to finalize the trace
time.Sleep(1 * time.Second) // give some extra time for last parts of trace to be written
c, err := rec.node.Driver.Client(ctx)
if err != nil {
return "", nil, err
}
_, err = c.ControlClient().UpdateBuildHistory(ctx, &controlapi.UpdateBuildHistoryRequest{
Ref: rec.Ref,
Finalize: true,
})
if err != nil {
return "", nil, err
}
recs, err := queryRecords(ctx, rec.Ref, []builder.Node{*rec.node}, &queryOptions{
CompletedOnly: true,
})
if err != nil {
return "", nil, err
}
if len(recs) == 0 {
return "", nil, errors.Errorf("build record %q was deleted", rec.Ref)
}
rec = &recs[0]
if rec.Trace == nil {
return "", nil, errors.Errorf("build record %q is missing a trace", rec.Ref)
}
}
c, err := rec.node.Driver.Client(ctx)
if err != nil {
return "", nil, err
}
store := proxy.NewContentStore(c.ContentClient())
ra, err := store.ReaderAt(ctx, ocispecs.Descriptor{
Digest: digest.Digest(rec.Trace.Digest),
MediaType: rec.Trace.MediaType,
Size: rec.Trace.Size,
})
if err != nil {
return "", nil, err
}
spans, err := otelutil.ParseSpanStubs(io.NewSectionReader(ra, 0, ra.Size()))
if err != nil {
return "", nil, err
}
wrapper := struct {
Data []jaeger.Trace `json:"data"`
}{
Data: spans.JaegerData().Data,
}
if len(wrapper.Data) == 0 {
return "", nil, errors.New("no trace data")
}
buf := &bytes.Buffer{}
enc := json.NewEncoder(buf)
enc.SetIndent("", " ")
if err := enc.Encode(wrapper); err != nil {
return "", nil, err
}
return string(wrapper.Data[0].TraceID), buf.Bytes(), nil
}
func runTrace(ctx context.Context, dockerCli command.Cli, opts traceOptions) error {
b, err := builder.New(dockerCli, builder.WithName(opts.builder))
if err != nil {
return err
}
nodes, err := b.LoadNodes(ctx)
if err != nil {
return err
}
for _, node := range nodes {
if node.Err != nil {
return node.Err
}
}
traceID, data, err := loadTrace(ctx, opts.ref, nodes)
if err != nil {
return err
}
srv := jaegerui.NewServer(jaegerui.Config{})
if err := srv.AddTrace(traceID, bytes.NewReader(data)); err != nil {
return err
}
url := "/trace/" + traceID
if opts.compare != "" {
traceIDcomp, data, err := loadTrace(ctx, opts.compare, nodes)
if err != nil {
return errors.Wrapf(err, "failed to load trace for %s", opts.compare)
}
if err := srv.AddTrace(traceIDcomp, bytes.NewReader(data)); err != nil {
return err
}
url = "/trace/" + traceIDcomp + "..." + traceID
}
var term bool
if _, err := console.ConsoleFromFile(os.Stdout); err == nil {
term = true
}
if !term && opts.compare == "" {
fmt.Fprintln(dockerCli.Out(), string(data))
return nil
}
ln, err := net.Listen("tcp", opts.addr)
if err != nil {
return err
}
go func() {
time.Sleep(100 * time.Millisecond)
browser.OpenURL(url)
}()
url = "http://" + ln.Addr().String() + url
fmt.Fprintf(dockerCli.Err(), "Trace available at %s\n", url)
go func() {
<-ctx.Done()
ln.Close()
}()
err = srv.Serve(ln)
if err != nil {
select {
case <-ctx.Done():
return nil
default:
}
}
return err
}
func traceCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
var options traceOptions
cmd := &cobra.Command{
Use: "trace [OPTIONS] [REF]",
Short: "Show the OpenTelemetry trace of a build record",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
options.ref = args[0]
}
options.builder = *rootOpts.Builder
return runTrace(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
}
flags := cmd.Flags()
flags.StringVar(&options.addr, "addr", "127.0.0.1:0", "Address to bind the UI server")
flags.StringVar(&options.compare, "compare", "", "Compare with another build reference")
return cmd
}

221
commands/history/utils.go Normal file
View File

@@ -0,0 +1,221 @@
package history
import (
"context"
"fmt"
"io"
"path/filepath"
"slices"
"strconv"
"strings"
"sync"
"time"
"github.com/docker/buildx/build"
"github.com/docker/buildx/builder"
"github.com/docker/buildx/localstate"
controlapi "github.com/moby/buildkit/api/services/control"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
)
func buildName(fattrs map[string]string, ls *localstate.State) string {
var res string
var target, contextPath, dockerfilePath, vcsSource string
if v, ok := fattrs["target"]; ok {
target = v
}
if v, ok := fattrs["context"]; ok {
contextPath = filepath.ToSlash(v)
} else if v, ok := fattrs["vcs:localdir:context"]; ok && v != "." {
contextPath = filepath.ToSlash(v)
}
if v, ok := fattrs["vcs:source"]; ok {
vcsSource = v
}
if v, ok := fattrs["filename"]; ok && v != "Dockerfile" {
dockerfilePath = filepath.ToSlash(v)
}
if v, ok := fattrs["vcs:localdir:dockerfile"]; ok && v != "." {
dockerfilePath = filepath.ToSlash(filepath.Join(v, dockerfilePath))
}
var localPath string
if ls != nil && !build.IsRemoteURL(ls.LocalPath) {
if ls.LocalPath != "" && ls.LocalPath != "-" {
localPath = filepath.ToSlash(ls.LocalPath)
}
if ls.DockerfilePath != "" && ls.DockerfilePath != "-" && ls.DockerfilePath != "Dockerfile" {
dockerfilePath = filepath.ToSlash(ls.DockerfilePath)
}
}
// remove default dockerfile name
const defaultFilename = "/Dockerfile"
hasDefaultFileName := strings.HasSuffix(dockerfilePath, defaultFilename) || dockerfilePath == ""
dockerfilePath = strings.TrimSuffix(dockerfilePath, defaultFilename)
// dockerfile is a subpath of context
if strings.HasPrefix(dockerfilePath, localPath) && len(dockerfilePath) > len(localPath) {
res = dockerfilePath[strings.LastIndex(localPath, "/")+1:]
} else {
// Otherwise, use basename
bpath := localPath
if len(dockerfilePath) > 0 {
bpath = dockerfilePath
}
if len(bpath) > 0 {
lidx := strings.LastIndex(bpath, "/")
res = bpath[lidx+1:]
if !hasDefaultFileName {
if lidx != -1 {
res = filepath.ToSlash(filepath.Join(filepath.Base(bpath[:lidx]), res))
} else {
res = filepath.ToSlash(filepath.Join(filepath.Base(bpath), res))
}
}
}
}
if len(contextPath) > 0 {
res = contextPath
}
if len(target) > 0 {
if len(res) > 0 {
res = res + " (" + target + ")"
} else {
res = target
}
}
if res == "" && vcsSource != "" {
return vcsSource
}
return res
}
func trimBeginning(s string, n int) string {
if len(s) <= n {
return s
}
return ".." + s[len(s)-n+2:]
}
type historyRecord struct {
*controlapi.BuildHistoryRecord
currentTimestamp *time.Time
node *builder.Node
name string
}
type queryOptions struct {
CompletedOnly bool
}
func queryRecords(ctx context.Context, ref string, nodes []builder.Node, opts *queryOptions) ([]historyRecord, error) {
var mu sync.Mutex
var out []historyRecord
var offset *int
if strings.HasPrefix(ref, "^") {
off, err := strconv.Atoi(ref[1:])
if err != nil {
return nil, errors.Wrapf(err, "invalid offset %q", ref)
}
offset = &off
ref = ""
}
eg, ctx := errgroup.WithContext(ctx)
for _, node := range nodes {
node := node
eg.Go(func() error {
if node.Driver == nil {
return nil
}
var records []historyRecord
c, err := node.Driver.Client(ctx)
if err != nil {
return err
}
serv, err := c.ControlClient().ListenBuildHistory(ctx, &controlapi.BuildHistoryRequest{
EarlyExit: true,
Ref: ref,
})
if err != nil {
return err
}
md, err := serv.Header()
if err != nil {
return err
}
var ts *time.Time
if v, ok := md[headerKeyTimestamp]; ok {
t, err := time.Parse(time.RFC3339Nano, v[0])
if err != nil {
return err
}
ts = &t
}
defer serv.CloseSend()
for {
he, err := serv.Recv()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return err
}
if he.Type == controlapi.BuildHistoryEventType_DELETED || he.Record == nil {
continue
}
if opts != nil && opts.CompletedOnly && he.Type != controlapi.BuildHistoryEventType_COMPLETE {
continue
}
records = append(records, historyRecord{
BuildHistoryRecord: he.Record,
currentTimestamp: ts,
node: &node,
})
}
mu.Lock()
out = append(out, records...)
mu.Unlock()
return nil
})
}
if err := eg.Wait(); err != nil {
return nil, err
}
slices.SortFunc(out, func(a, b historyRecord) int {
return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime())
})
if offset != nil {
var filtered []historyRecord
for _, r := range out {
if *offset > 0 {
*offset--
continue
}
filtered = append(filtered, r)
break
}
if *offset > 0 {
return nil, errors.Errorf("no completed build found with offset %d", *offset)
}
out = filtered
}
return out, nil
}
func formatDuration(d time.Duration) string {
if d < time.Minute {
return fmt.Sprintf("%.1fs", d.Seconds())
}
return fmt.Sprintf("%dm %2ds", int(d.Minutes()), int(d.Seconds())%60)
}

View File

@@ -194,7 +194,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, in createOptions, arg
} }
s := s s := s
eg2.Go(func() error { eg2.Go(func() error {
sub.Log(1, []byte(fmt.Sprintf("copying %s from %s to %s\n", s.Desc.Digest.String(), s.Ref.String(), t.String()))) sub.Log(1, fmt.Appendf(nil, "copying %s from %s to %s\n", s.Desc.Digest.String(), s.Ref.String(), t.String()))
return r.Copy(ctx, s, t) return r.Copy(ctx, s, t)
}) })
} }
@@ -202,7 +202,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, in createOptions, arg
if err := eg2.Wait(); err != nil { if err := eg2.Wait(); err != nil {
return err return err
} }
sub.Log(1, []byte(fmt.Sprintf("pushing %s to %s\n", desc.Digest.String(), t.String()))) sub.Log(1, fmt.Appendf(nil, "pushing %s to %s\n", desc.Digest.String(), t.String()))
return r.Push(ctx, t, desc, dt) return r.Push(ctx, t, desc, dt)
}) })
}) })

View File

@@ -115,6 +115,25 @@ func runInspect(ctx context.Context, dockerCli command.Cli, in inspectOptions) e
fmt.Fprintf(w, "\t%s:\t%s\n", k, v) fmt.Fprintf(w, "\t%s:\t%s\n", k, v)
} }
} }
if len(nodes[i].CDIDevices) > 0 {
fmt.Fprintf(w, "Devices:\n")
for _, dev := range nodes[i].CDIDevices {
fmt.Fprintf(w, "\tName:\t%s\n", dev.Name)
if dev.OnDemand {
fmt.Fprintf(w, "\tOn-Demand:\t%v\n", dev.OnDemand)
} else {
fmt.Fprintf(w, "\tAutomatically allowed:\t%v\n", dev.AutoAllow)
}
if len(dev.Annotations) > 0 {
fmt.Fprintf(w, "\tAnnotations:\n")
for k, v := range dev.Annotations {
fmt.Fprintf(w, "\t\t%s:\t%s\n", k, v)
}
}
}
}
for ri, rule := range nodes[i].GCPolicy { for ri, rule := range nodes[i].GCPolicy {
fmt.Fprintf(w, "GC Policy rule#%d:\n", ri) fmt.Fprintf(w, "GC Policy rule#%d:\n", ri)
fmt.Fprintf(w, "\tAll:\t%v\n", rule.All) fmt.Fprintf(w, "\tAll:\t%v\n", rule.All)

View File

@@ -159,6 +159,9 @@ func lsPrint(dockerCli command.Cli, current *store.NodeGroup, builders []*builde
} }
continue continue
} }
if ctx.Format.IsJSON() {
continue
}
for _, n := range b.Nodes() { for _, n := range b.Nodes() {
if n.Err != nil { if n.Err != nil {
if ctx.Format.IsTable() { if ctx.Format.IsTable() {

View File

@@ -5,6 +5,7 @@ import (
"os" "os"
debugcmd "github.com/docker/buildx/commands/debug" debugcmd "github.com/docker/buildx/commands/debug"
historycmd "github.com/docker/buildx/commands/history"
imagetoolscmd "github.com/docker/buildx/commands/imagetools" imagetoolscmd "github.com/docker/buildx/commands/imagetools"
"github.com/docker/buildx/controller/remote" "github.com/docker/buildx/controller/remote"
"github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/buildx/util/cobrautil/completion"
@@ -106,6 +107,7 @@ func addCommands(cmd *cobra.Command, opts *rootOptions, dockerCli command.Cli) {
pruneCmd(dockerCli, opts), pruneCmd(dockerCli, opts),
duCmd(dockerCli, opts), duCmd(dockerCli, opts),
imagetoolscmd.RootCmd(cmd, dockerCli, imagetoolscmd.RootOptions{Builder: &opts.builder}), imagetoolscmd.RootCmd(cmd, dockerCli, imagetoolscmd.RootOptions{Builder: &opts.builder}),
historycmd.RootCmd(cmd, dockerCli, historycmd.RootOptions{Builder: &opts.builder}),
) )
if confutil.IsExperimental() { if confutil.IsExperimental() {
cmd.AddCommand(debugcmd.RootCmd(dockerCli, cmd.AddCommand(debugcmd.RootCmd(dockerCli,

View File

@@ -75,7 +75,9 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in *controllerapi.Buil
opts.Platforms = platforms opts.Platforms = platforms
dockerConfig := dockerCli.ConfigFile() dockerConfig := dockerCli.ConfigFile()
opts.Session = append(opts.Session, authprovider.NewDockerAuthProvider(dockerConfig, nil)) opts.Session = append(opts.Session, authprovider.NewDockerAuthProvider(authprovider.DockerAuthProviderConfig{
ConfigFile: dockerConfig,
}))
secrets, err := controllerapi.CreateSecrets(in.Secrets) secrets, err := controllerapi.CreateSecrets(in.Secrets)
if err != nil { if err != nil {

View File

@@ -13,8 +13,8 @@ import (
type BuildxController interface { type BuildxController interface {
Build(ctx context.Context, options *controllerapi.BuildOptions, in io.ReadCloser, progress progress.Writer) (ref string, resp *client.SolveResponse, inputs *build.Inputs, err error) Build(ctx context.Context, options *controllerapi.BuildOptions, in io.ReadCloser, progress progress.Writer) (ref string, resp *client.SolveResponse, inputs *build.Inputs, err error)
// Invoke starts an IO session into the specified process. // Invoke starts an IO session into the specified process.
// If pid doesn't matche to any running processes, it starts a new process with the specified config. // If pid doesn't match to any running processes, it starts a new process with the specified config.
// If there is no container running or InvokeConfig.Rollback is speicfied, the process will start in a newly created container. // If there is no container running or InvokeConfig.Rollback is specified, the process will start in a newly created container.
// NOTE: If needed, in the future, we can split this API into three APIs (NewContainer, NewProcess and Attach). // NOTE: If needed, in the future, we can split this API into three APIs (NewContainer, NewProcess and Attach).
Invoke(ctx context.Context, ref, pid string, options *controllerapi.InvokeConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) error Invoke(ctx context.Context, ref, pid string, options *controllerapi.InvokeConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) error
Kill(ctx context.Context) error Kill(ctx context.Context) error

View File

@@ -24,11 +24,11 @@ func (w *writer) Write(status *client.SolveStatus) {
func (w *writer) WriteBuildRef(target string, ref string) {} func (w *writer) WriteBuildRef(target string, ref string) {}
func (w *writer) ValidateLogSource(digest.Digest, interface{}) bool { func (w *writer) ValidateLogSource(digest.Digest, any) bool {
return true return true
} }
func (w *writer) ClearLogSource(interface{}) {} func (w *writer) ClearLogSource(any) {}
func ToControlStatus(s *client.SolveStatus) *StatusResponse { func ToControlStatus(s *client.SolveStatus) *StatusResponse {
resp := StatusResponse{} resp := StatusResponse{}

View File

@@ -1,6 +1,8 @@
package pb package pb
import ( import (
"slices"
"github.com/moby/buildkit/session" "github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/sshforward/sshprovider" "github.com/moby/buildkit/session/sshforward/sshprovider"
) )
@@ -10,7 +12,7 @@ func CreateSSH(ssh []*SSH) (session.Attachable, error) {
for _, ssh := range ssh { for _, ssh := range ssh {
cfg := sshprovider.AgentConfig{ cfg := sshprovider.AgentConfig{
ID: ssh.ID, ID: ssh.ID,
Paths: append([]string{}, ssh.Paths...), Paths: slices.Clone(ssh.Paths),
} }
configs = append(configs, cfg) configs = append(configs, cfg)
} }

View File

@@ -39,7 +39,7 @@ func (p *Process) Done() <-chan error {
return p.errCh return p.errCh
} }
// Manager manages a set of proceses. // Manager manages a set of processes.
type Manager struct { type Manager struct {
container atomic.Value container atomic.Value
processes sync.Map processes sync.Map

View File

@@ -140,7 +140,7 @@ func serveCmd(dockerCli command.Cli) *cobra.Command {
return err return err
} }
pidF := filepath.Join(root, defaultPIDFilename) pidF := filepath.Join(root, defaultPIDFilename)
if err := os.WriteFile(pidF, []byte(fmt.Sprintf("%d", os.Getpid())), 0600); err != nil { if err := os.WriteFile(pidF, fmt.Appendf(nil, "%d", os.Getpid()), 0600); err != nil {
return err return err
} }
defer func() { defer func() {

View File

@@ -48,6 +48,8 @@ target "lint" {
"linux/s390x", "linux/s390x",
"linux/ppc64le", "linux/ppc64le",
"linux/riscv64", "linux/riscv64",
"netbsd/amd64",
"netbsd/arm64",
"openbsd/amd64", "openbsd/amd64",
"openbsd/arm64", "openbsd/arm64",
"windows/amd64", "windows/amd64",
@@ -167,6 +169,8 @@ target "binaries-cross" {
"linux/ppc64le", "linux/ppc64le",
"linux/riscv64", "linux/riscv64",
"linux/s390x", "linux/s390x",
"netbsd/amd64",
"netbsd/arm64",
"openbsd/amd64", "openbsd/amd64",
"openbsd/arm64", "openbsd/arm64",
"windows/amd64", "windows/amd64",

View File

@@ -221,8 +221,10 @@ The following table shows the complete list of attributes that you can assign to
| [`attest`](#targetattest) | List | Build attestations | | [`attest`](#targetattest) | List | Build attestations |
| [`cache-from`](#targetcache-from) | List | External cache sources | | [`cache-from`](#targetcache-from) | List | External cache sources |
| [`cache-to`](#targetcache-to) | List | External cache destinations | | [`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 | | [`context`](#targetcontext) | String | Set of files located in the specified path or URL |
| [`contexts`](#targetcontexts) | Map | Additional build contexts | | [`contexts`](#targetcontexts) | Map | Additional build contexts |
| [`description`](#targetdescription) | String | Description of a target |
| [`dockerfile-inline`](#targetdockerfile-inline) | String | Inline Dockerfile string | | [`dockerfile-inline`](#targetdockerfile-inline) | String | Inline Dockerfile string |
| [`dockerfile`](#targetdockerfile) | String | Dockerfile location | | [`dockerfile`](#targetdockerfile) | String | Dockerfile location |
| [`inherits`](#targetinherits) | List | Inherit attributes from other targets | | [`inherits`](#targetinherits) | List | Inherit attributes from other targets |
@@ -283,19 +285,11 @@ The key takes a list of annotations, in the format of `KEY=VALUE`.
```hcl ```hcl
target "default" { target "default" {
output = ["type=image,name=foo"] output = [{ type = "image", name = "foo" }]
annotations = ["org.opencontainers.image.authors=dvdksn"] annotations = ["org.opencontainers.image.authors=dvdksn"]
} }
``` ```
is the same as
```hcl
target "default" {
output = ["type=image,name=foo,annotation.org.opencontainers.image.authors=dvdksn"]
}
```
By default, the annotation is added to image manifests. You can configure the By default, the annotation is added to image manifests. You can configure the
level of the annotations by adding a prefix to the annotation, containing a level of the annotations by adding a prefix to the annotation, containing a
comma-separated list of all the levels that you want to annotate. The following comma-separated list of all the levels that you want to annotate. The following
@@ -303,7 +297,7 @@ example adds annotations to both the image index and manifests.
```hcl ```hcl
target "default" { target "default" {
output = ["type=image,name=foo"] output = [{ type = "image", name = "foo" }]
annotations = ["index,manifest:org.opencontainers.image.authors=dvdksn"] annotations = ["index,manifest:org.opencontainers.image.authors=dvdksn"]
} }
``` ```
@@ -319,8 +313,13 @@ This attribute accepts the long-form CSV version of attestation parameters.
```hcl ```hcl
target "default" { target "default" {
attest = [ attest = [
"type=provenance,mode=min", {
"type=sbom" type = "provenance",
mode = "max",
},
{
type = "sbom",
}
] ]
} }
``` ```
@@ -336,8 +335,15 @@ This takes a list value, so you can specify multiple cache sources.
```hcl ```hcl
target "app" { target "app" {
cache-from = [ cache-from = [
"type=s3,region=eu-west-1,bucket=mybucket", {
"user/repo:cache", type = "s3",
region = "eu-west-1",
bucket = "mybucket"
},
{
type = "registry",
ref = "user/repo:cache"
}
] ]
} }
``` ```
@@ -353,8 +359,14 @@ This takes a list value, so you can specify multiple cache export targets.
```hcl ```hcl
target "app" { target "app" {
cache-to = [ cache-to = [
"type=s3,region=eu-west-1,bucket=mybucket", {
"type=inline" type = "s3",
region = "eu-west-1",
bucket = "mybucket"
},
{
type = "inline",
}
] ]
} }
``` ```
@@ -371,6 +383,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 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). [`docker buildx build --call`](https://docs.docker.com/reference/cli/docker/buildx/build/#call).
@@ -481,6 +500,25 @@ FROM baseapp
RUN echo "Hello world" 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` ### `target.dockerfile-inline`
Uses the string value as an inline Dockerfile for the build target. Uses the string value as an inline Dockerfile for the build target.
@@ -835,7 +873,7 @@ The following example configures the target to use a cache-only output,
```hcl ```hcl
target "default" { target "default" {
output = ["type=cacheonly"] output = [{ type = "cacheonly" }]
} }
``` ```
@@ -875,8 +913,8 @@ variable "HOME" {
target "default" { target "default" {
secret = [ secret = [
"type=env,id=KUBECONFIG", { type = "env", id = "KUBECONFIG" },
"type=file,id=aws,src=${HOME}/.aws/credentials" { type = "file", id = "aws", src = "${HOME}/.aws/credentials" },
] ]
} }
``` ```
@@ -920,7 +958,7 @@ This can be useful if you need to access private repositories during a build.
```hcl ```hcl
target "default" { target "default" {
ssh = ["default"] ssh = [{ id = "default" }]
} }
``` ```

View File

@@ -17,6 +17,7 @@ Extended build capabilities with BuildKit
| [`debug`](buildx_debug.md) | Start debugger (EXPERIMENTAL) | | [`debug`](buildx_debug.md) | Start debugger (EXPERIMENTAL) |
| [`dial-stdio`](buildx_dial-stdio.md) | Proxy current stdio streams to builder instance | | [`dial-stdio`](buildx_dial-stdio.md) | Proxy current stdio streams to builder instance |
| [`du`](buildx_du.md) | Disk usage | | [`du`](buildx_du.md) | Disk usage |
| [`history`](buildx_history.md) | Commands to work on build records |
| [`imagetools`](buildx_imagetools.md) | Commands to work on images in registry | | [`imagetools`](buildx_imagetools.md) | Commands to work on images in registry |
| [`inspect`](buildx_inspect.md) | Inspect current builder instance | | [`inspect`](buildx_inspect.md) | Inspect current builder instance |
| [`ls`](buildx_ls.md) | List builder instances | | [`ls`](buildx_ls.md) | List builder instances |

View File

@@ -15,7 +15,7 @@ Build from a file
| Name | Type | Default | Description | | 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 | | [`--builder`](#builder) | `string` | | Override the configured builder instance |
| [`--call`](#call) | `string` | `build` | Set method for evaluating build (`check`, `outline`, `targets`) | | [`--call`](#call) | `string` | `build` | Set method for evaluating build (`check`, `outline`, `targets`) |
| [`--check`](#check) | `bool` | | Shorthand for `--call=check` | | [`--check`](#check) | `bool` | | Shorthand for `--call=check` |
@@ -51,6 +51,80 @@ guide for introduction to writing bake files.
## Examples ## Examples
### <a name="allow"></a> 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=<path|*>` - Grant read and write access to files outside of the
working directory.
- `--allow fs.read=<path|*>` - Grant read access to files outside of the
working directory.
- `--allow fs.write=<path|*>` - 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
```
### <a name="builder"></a> Override the configured builder instance (--builder) ### <a name="builder"></a> Override the configured builder instance (--builder)
Same as [`buildx --builder`](buildx.md#builder). Same as [`buildx --builder`](buildx.md#builder).
@@ -276,15 +350,19 @@ $ docker buildx bake --set target.platform=linux/arm64
$ docker buildx bake --set foo*.args.mybuildarg=value # overrides build arg for all targets starting with 'foo' $ docker buildx bake --set foo*.args.mybuildarg=value # overrides build arg for all targets starting with 'foo'
$ docker buildx bake --set *.platform=linux/arm64 # overrides platform for all targets $ docker buildx bake --set *.platform=linux/arm64 # overrides platform for all targets
$ docker buildx bake --set foo*.no-cache # bypass caching only for targets starting with 'foo' $ docker buildx bake --set foo*.no-cache # bypass caching only for targets starting with 'foo'
$ docker buildx bake --set target.platform+=linux/arm64 # appends 'linux/arm64' to the platform list
``` ```
You can override the following fields: You can override the following fields:
* `annotations`
* `attest`
* `args` * `args`
* `cache-from` * `cache-from`
* `cache-to` * `cache-to`
* `context` * `context`
* `dockerfile` * `dockerfile`
* `entitlements`
* `labels` * `labels`
* `load` * `load`
* `no-cache` * `no-cache`
@@ -297,3 +375,20 @@ You can override the following fields:
* `ssh` * `ssh`
* `tags` * `tags`
* `target` * `target`
You can append using `+=` operator for the following fields:
* `annotations
* `attest
* `cache-from`
* `cache-to`
* `entitlements
* `no-cache-filter`
* `output`
* `platform`
* `secrets`
* `ssh`
* `tags`
> [!NOTE]
> ¹ These fields already append by default.

View File

@@ -16,7 +16,7 @@ Start a build
| Name | Type | Default | Description | | Name | Type | Default | Description |
|:----------------------------------------|:--------------|:----------|:-------------------------------------------------------------------------------------------------------------| |:----------------------------------------|:--------------|:----------|:-------------------------------------------------------------------------------------------------------------|
| [`--add-host`](#add-host) | `stringSlice` | | Add a custom host-to-IP mapping (format: `host:ip`) | | [`--add-host`](#add-host) | `stringSlice` | | Add a custom host-to-IP mapping (format: `host:ip`) |
| [`--allow`](#allow) | `stringSlice` | | Allow extra privileged entitlement (e.g., `network.host`, `security.insecure`) | | [`--allow`](#allow) | `stringArray` | | Allow extra privileged entitlement (e.g., `network.host`, `security.insecure`) |
| [`--annotation`](#annotation) | `stringArray` | | Add annotation to the image | | [`--annotation`](#annotation) | `stringArray` | | Add annotation to the image |
| [`--attest`](#attest) | `stringArray` | | Attestation parameters (format: `type=sbom,generator=image`) | | [`--attest`](#attest) | `stringArray` | | Attestation parameters (format: `type=sbom,generator=image`) |
| [`--build-arg`](#build-arg) | `stringArray` | | Set build-time variables | | [`--build-arg`](#build-arg) | `stringArray` | | Set build-time variables |

View File

@@ -12,7 +12,7 @@ Start a build
| Name | Type | Default | Description | | Name | Type | Default | Description |
|:--------------------|:--------------|:----------|:-------------------------------------------------------------------------------------------------------------| |:--------------------|:--------------|:----------|:-------------------------------------------------------------------------------------------------------------|
| `--add-host` | `stringSlice` | | Add a custom host-to-IP mapping (format: `host:ip`) | | `--add-host` | `stringSlice` | | Add a custom host-to-IP mapping (format: `host:ip`) |
| `--allow` | `stringSlice` | | Allow extra privileged entitlement (e.g., `network.host`, `security.insecure`) | | `--allow` | `stringArray` | | Allow extra privileged entitlement (e.g., `network.host`, `security.insecure`) |
| `--annotation` | `stringArray` | | Add annotation to the image | | `--annotation` | `stringArray` | | Add annotation to the image |
| `--attest` | `stringArray` | | Attestation parameters (format: `type=sbom,generator=image`) | | `--attest` | `stringArray` | | Attestation parameters (format: `type=sbom,generator=image`) |
| `--build-arg` | `stringArray` | | Set build-time variables | | `--build-arg` | `stringArray` | | Set build-time variables |

View File

@@ -0,0 +1,28 @@
# docker buildx history
<!---MARKER_GEN_START-->
Commands to work on build records
### Subcommands
| Name | Description |
|:---------------------------------------|:-----------------------------------------------|
| [`import`](buildx_history_import.md) | Import a build into Docker Desktop |
| [`inspect`](buildx_history_inspect.md) | Inspect a build |
| [`logs`](buildx_history_logs.md) | Print the logs of a build |
| [`ls`](buildx_history_ls.md) | List build records |
| [`open`](buildx_history_open.md) | Open a build in Docker Desktop |
| [`rm`](buildx_history_rm.md) | Remove build records |
| [`trace`](buildx_history_trace.md) | Show the OpenTelemetry trace of a build record |
### Options
| Name | Type | Default | Description |
|:----------------|:---------|:--------|:-----------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
<!---MARKER_GEN_END-->

View File

@@ -0,0 +1,16 @@
# docker buildx history import
<!---MARKER_GEN_START-->
Import a build into Docker Desktop
### Options
| Name | Type | Default | Description |
|:----------------|:--------------|:--------|:-----------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| `-f`, `--file` | `stringArray` | | Import from a file path |
<!---MARKER_GEN_END-->

View File

@@ -0,0 +1,117 @@
# docker buildx history inspect
<!---MARKER_GEN_START-->
Inspect a build
### Subcommands
| Name | Description |
|:-----------------------------------------------------|:---------------------------|
| [`attachment`](buildx_history_inspect_attachment.md) | Inspect a build attachment |
### Options
| Name | Type | Default | Description |
|:----------------------|:---------|:---------|:-----------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| [`--format`](#format) | `string` | `pretty` | Format the output |
<!---MARKER_GEN_END-->
## Examples
### <a name="format"></a> Format the output (--format)
The formatting options (`--format`) pretty-prints the output to `pretty` (default),
`json` or using a Go template.
```console
$ docker buildx history inspect
Name: buildx (binaries)
Context: .
Dockerfile: Dockerfile
VCS Repository: https://github.com/crazy-max/buildx.git
VCS Revision: f15eaa1ee324ffbbab29605600d27a84cab86361
Target: binaries
Platforms: linux/amd64
Keep Git Dir: true
Started: 2025-02-07 11:56:24
Duration: 1m 1s
Build Steps: 16/16 (25% cached)
Image Resolve Mode: local
Materials:
URI DIGEST
pkg:docker/docker/dockerfile@1 sha256:93bfd3b68c109427185cd78b4779fc82b484b0b7618e36d0f104d4d801e66d25
pkg:docker/golang@1.23-alpine3.21?platform=linux%2Famd64 sha256:2c49857f2295e89b23b28386e57e018a86620a8fede5003900f2d138ba9c4037
pkg:docker/tonistiigi/xx@1.6.1?platform=linux%2Famd64 sha256:923441d7c25f1e2eb5789f82d987693c47b8ed987c4ab3b075d6ed2b5d6779a3
Attachments:
DIGEST PLATFORM TYPE
sha256:217329d2af959d4f02e3a96dcbe62bf100cab1feb8006a047ddfe51a5397f7e3 https://slsa.dev/provenance/v0.2
Print build logs: docker buildx history logs g9808bwrjrlkbhdamxklx660b
```
```console
$ docker buildx history inspect --format json
{
"Name": "buildx (binaries)",
"Ref": "5w7vkqfi0rf59hw4hnmn627r9",
"Context": ".",
"Dockerfile": "Dockerfile",
"VCSRepository": "https://github.com/crazy-max/buildx.git",
"VCSRevision": "f15eaa1ee324ffbbab29605600d27a84cab86361",
"Target": "binaries",
"Platform": [
"linux/amd64"
],
"KeepGitDir": true,
"StartedAt": "2025-02-07T12:01:05.75807272+01:00",
"CompletedAt": "2025-02-07T12:02:07.991778875+01:00",
"Duration": 62233706155,
"Status": "completed",
"NumCompletedSteps": 16,
"NumTotalSteps": 16,
"NumCachedSteps": 4,
"Config": {
"ImageResolveMode": "local"
},
"Materials": [
{
"URI": "pkg:docker/docker/dockerfile@1",
"Digests": [
"sha256:93bfd3b68c109427185cd78b4779fc82b484b0b7618e36d0f104d4d801e66d25"
]
},
{
"URI": "pkg:docker/golang@1.23-alpine3.21?platform=linux%2Famd64",
"Digests": [
"sha256:2c49857f2295e89b23b28386e57e018a86620a8fede5003900f2d138ba9c4037"
]
},
{
"URI": "pkg:docker/tonistiigi/xx@1.6.1?platform=linux%2Famd64",
"Digests": [
"sha256:923441d7c25f1e2eb5789f82d987693c47b8ed987c4ab3b075d6ed2b5d6779a3"
]
}
],
"Attachments": [
{
"Digest": "sha256:450fdd2e6b868fecd69e9891c2c404ba461aa38a47663b4805edeb8d2baf80b1",
"Type": "https://slsa.dev/provenance/v0.2"
}
]
}
```
```console
$ docker buildx history inspect --format "{{.Name}}: {{.VCSRepository}} ({{.VCSRevision}})"
buildx (binaries): https://github.com/crazy-max/buildx.git (f15eaa1ee324ffbbab29605600d27a84cab86361)
```

View File

@@ -0,0 +1,17 @@
# docker buildx history inspect attachment
<!---MARKER_GEN_START-->
Inspect a build attachment
### Options
| Name | Type | Default | Description |
|:----------------|:---------|:--------|:-----------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| `--platform` | `string` | | Platform of attachment |
| `--type` | `string` | | Type of attachment |
<!---MARKER_GEN_END-->

View File

@@ -0,0 +1,16 @@
# docker buildx history logs
<!---MARKER_GEN_START-->
Print the logs of a build
### Options
| Name | Type | Default | Description |
|:----------------|:---------|:--------|:--------------------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| `--progress` | `string` | `plain` | Set type of progress output (plain, rawjson, tty) |
<!---MARKER_GEN_END-->

View File

@@ -0,0 +1,17 @@
# docker buildx history ls
<!---MARKER_GEN_START-->
List build records
### Options
| Name | Type | Default | Description |
|:----------------|:---------|:--------|:-----------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| `--format` | `string` | `table` | Format the output |
| `--no-trunc` | `bool` | | Don't truncate output |
<!---MARKER_GEN_END-->

View File

@@ -0,0 +1,15 @@
# docker buildx history open
<!---MARKER_GEN_START-->
Open a build in Docker Desktop
### Options
| Name | Type | Default | Description |
|:----------------|:---------|:--------|:-----------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
<!---MARKER_GEN_END-->

View File

@@ -0,0 +1,16 @@
# docker buildx history rm
<!---MARKER_GEN_START-->
Remove build records
### Options
| Name | Type | Default | Description |
|:----------------|:---------|:--------|:-----------------------------------------|
| `--all` | `bool` | | Remove all build records |
| `--builder` | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
<!---MARKER_GEN_END-->

View File

@@ -0,0 +1,17 @@
# docker buildx history trace
<!---MARKER_GEN_START-->
Show the OpenTelemetry trace of a build record
### Options
| Name | Type | Default | Description |
|:----------------|:---------|:--------------|:-----------------------------------------|
| `--addr` | `string` | `127.0.0.1:0` | Address to bind the UI server |
| `--builder` | `string` | | Override the configured builder instance |
| `--compare` | `string` | | Compare with another build reference |
| `-D`, `--debug` | `bool` | | Enable debug logging |
<!---MARKER_GEN_END-->

View File

@@ -23,10 +23,10 @@ import (
"github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/system" "github.com/docker/docker/api/types/system"
dockerclient "github.com/docker/docker/client"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
dockerarchive "github.com/docker/docker/pkg/archive" dockerarchive "github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/pkg/stdcopy" "github.com/docker/docker/pkg/stdcopy"
"github.com/moby/buildkit/client" "github.com/moby/buildkit/client"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -56,6 +56,7 @@ type Driver struct {
restartPolicy container.RestartPolicy restartPolicy container.RestartPolicy
env []string env []string
defaultLoad bool defaultLoad bool
gpus []container.DeviceRequest
} }
func (d *Driver) IsMobyDriver() bool { func (d *Driver) IsMobyDriver() bool {
@@ -70,7 +71,7 @@ func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
return progress.Wrap("[internal] booting buildkit", l, func(sub progress.SubLogger) error { return progress.Wrap("[internal] booting buildkit", l, func(sub progress.SubLogger) error {
_, err := d.DockerAPI.ContainerInspect(ctx, d.Name) _, err := d.DockerAPI.ContainerInspect(ctx, d.Name)
if err != nil { if err != nil {
if dockerclient.IsErrNotFound(err) { if errdefs.IsNotFound(err) {
return d.create(ctx, sub) return d.create(ctx, sub)
} }
return err return err
@@ -95,19 +96,20 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
if err != nil { if err != nil {
return err return err
} }
rc, err := d.DockerAPI.ImageCreate(ctx, imageName, image.CreateOptions{ resp, err := d.DockerAPI.ImageCreate(ctx, imageName, image.CreateOptions{
RegistryAuth: ra, RegistryAuth: ra,
}) })
if err != nil { if err != nil {
return err return err
} }
_, err = io.Copy(io.Discard, rc) defer resp.Close()
return err return jsonmessage.DisplayJSONMessagesStream(resp, io.Discard, 0, false, nil)
}); err != nil { }); err != nil {
// image pulling failed, check if it exists in local image store. // image pulling failed, check if it exists in local image store.
// if not, return pulling error. otherwise log it. // if not, return pulling error. otherwise log it.
_, _, errInspect := d.DockerAPI.ImageInspectWithRaw(ctx, imageName) _, errInspect := d.DockerAPI.ImageInspect(ctx, imageName)
if errInspect != nil { found := errInspect == nil
if !found {
return err return err
} }
l.Wrap("pulling failed, using local image "+imageName, func() error { return nil }) l.Wrap("pulling failed, using local image "+imageName, func() error { return nil })
@@ -157,6 +159,9 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
if d.cpusetMems != "" { if d.cpusetMems != "" {
hc.Resources.CpusetMems = d.cpusetMems hc.Resources.CpusetMems = d.cpusetMems
} }
if len(d.gpus) > 0 && d.hasGPUCapability(ctx, cfg.Image, d.gpus) {
hc.Resources.DeviceRequests = d.gpus
}
if info, err := d.DockerAPI.Info(ctx); err == nil { if info, err := d.DockerAPI.Info(ctx); err == nil {
if info.CgroupDriver == "cgroupfs" { if info.CgroupDriver == "cgroupfs" {
// Place all buildkit containers inside this cgroup by default so limits can be attached // Place all buildkit containers inside this cgroup by default so limits can be attached
@@ -306,7 +311,7 @@ func (d *Driver) start(ctx context.Context) error {
func (d *Driver) Info(ctx context.Context) (*driver.Info, error) { func (d *Driver) Info(ctx context.Context) (*driver.Info, error) {
ctn, err := d.DockerAPI.ContainerInspect(ctx, d.Name) ctn, err := d.DockerAPI.ContainerInspect(ctx, d.Name)
if err != nil { if err != nil {
if dockerclient.IsErrNotFound(err) { if errdefs.IsNotFound(err) {
return &driver.Info{ return &driver.Info{
Status: driver.Inactive, Status: driver.Inactive,
}, nil }, nil
@@ -419,6 +424,7 @@ func (d *Driver) Features(ctx context.Context) map[driver.Feature]bool {
driver.DockerExporter: true, driver.DockerExporter: true,
driver.CacheExport: true, driver.CacheExport: true,
driver.MultiPlatform: true, driver.MultiPlatform: true,
driver.DirectPush: true,
driver.DefaultLoad: d.defaultLoad, driver.DefaultLoad: d.defaultLoad,
} }
} }
@@ -427,6 +433,31 @@ func (d *Driver) HostGatewayIP(ctx context.Context) (net.IP, error) {
return nil, errors.New("host-gateway is not supported by the docker-container driver") return nil, errors.New("host-gateway is not supported by the docker-container driver")
} }
// hasGPUCapability checks if docker daemon has GPU capability. We need to run
// a dummy container with GPU device to check if the daemon has this capability
// because there is no API to check it yet.
func (d *Driver) hasGPUCapability(ctx context.Context, image string, gpus []container.DeviceRequest) bool {
cfg := &container.Config{
Image: image,
Entrypoint: []string{"/bin/true"},
}
hc := &container.HostConfig{
NetworkMode: container.NetworkMode(container.IPCModeNone),
AutoRemove: true,
Resources: container.Resources{
DeviceRequests: gpus,
},
}
resp, err := d.DockerAPI.ContainerCreate(ctx, cfg, hc, &network.NetworkingConfig{}, nil, "")
if err != nil {
return false
}
if err := d.DockerAPI.ContainerStart(ctx, resp.ID, container.StartOptions{}); err != nil {
return false
}
return true
}
func demuxConn(c net.Conn) net.Conn { func demuxConn(c net.Conn) net.Conn {
pr, pw := io.Pipe() pr, pw := io.Pipe()
// TODO: rewrite parser with Reader() to avoid goroutine switch // TODO: rewrite parser with Reader() to avoid goroutine switch

View File

@@ -51,6 +51,12 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver
InitConfig: cfg, InitConfig: cfg,
restartPolicy: rp, restartPolicy: rp,
} }
var gpus dockeropts.GpuOpts
if err := gpus.Set("all"); err == nil {
if v := gpus.Value(); len(v) > 0 {
d.gpus = v
}
}
for k, v := range cfg.DriverOpts { for k, v := range cfg.DriverOpts {
switch { switch {
case k == "network": case k == "network":

View File

@@ -93,6 +93,7 @@ func (d *Driver) Features(ctx context.Context) map[driver.Feature]bool {
driver.DockerExporter: useContainerdSnapshotter, driver.DockerExporter: useContainerdSnapshotter,
driver.CacheExport: useContainerdSnapshotter, driver.CacheExport: useContainerdSnapshotter,
driver.MultiPlatform: useContainerdSnapshotter, driver.MultiPlatform: useContainerdSnapshotter,
driver.DirectPush: useContainerdSnapshotter,
driver.DefaultLoad: true, driver.DefaultLoad: true,
} }
}) })

View File

@@ -7,5 +7,6 @@ const DockerExporter Feature = "Docker exporter"
const CacheExport Feature = "Cache export" const CacheExport Feature = "Cache export"
const MultiPlatform Feature = "Multi-platform build" const MultiPlatform Feature = "Multi-platform build"
const DirectPush Feature = "Direct push"
const DefaultLoad Feature = "Automatically load images to the Docker Engine image store" const DefaultLoad Feature = "Automatically load images to the Docker Engine image store"

View File

@@ -35,10 +35,10 @@ func testEndpoint(server, defaultNamespace string, ca, cert, key []byte, skipTLS
} }
var testStoreCfg = store.NewConfig( var testStoreCfg = store.NewConfig(
func() interface{} { func() any {
return &map[string]interface{}{} return &map[string]any{}
}, },
store.EndpointTypeGetter(KubernetesEndpoint, func() interface{} { return &EndpointMeta{} }), store.EndpointTypeGetter(KubernetesEndpoint, func() any { return &EndpointMeta{} }),
) )
func TestSaveLoadContexts(t *testing.T) { func TestSaveLoadContexts(t *testing.T) {
@@ -197,7 +197,7 @@ func checkClientConfig(t *testing.T, ep Endpoint, server, namespace string, ca,
func save(s store.Writer, ep Endpoint, name string) error { func save(s store.Writer, ep Endpoint, name string) error {
meta := store.Metadata{ meta := store.Metadata{
Endpoints: map[string]interface{}{ Endpoints: map[string]any{
KubernetesEndpoint: ep.EndpointMeta, KubernetesEndpoint: ep.EndpointMeta,
}, },
Name: name, Name: name,

View File

@@ -43,7 +43,7 @@ type Endpoint struct {
func init() { func init() {
command.RegisterDefaultStoreEndpoints( command.RegisterDefaultStoreEndpoints(
store.EndpointTypeGetter(KubernetesEndpoint, func() interface{} { return &EndpointMeta{} }), store.EndpointTypeGetter(KubernetesEndpoint, func() any { return &EndpointMeta{} }),
) )
} }
@@ -96,7 +96,7 @@ func (c *Endpoint) KubernetesConfig() clientcmd.ClientConfig {
// ResolveDefault returns endpoint metadata for the default Kubernetes // ResolveDefault returns endpoint metadata for the default Kubernetes
// endpoint, which is derived from the env-based kubeconfig. // endpoint, which is derived from the env-based kubeconfig.
func (c *EndpointMeta) ResolveDefault() (interface{}, *store.EndpointTLSData, error) { func (c *EndpointMeta) ResolveDefault() (any, *store.EndpointTLSData, error) {
kubeconfig := os.Getenv("KUBECONFIG") kubeconfig := os.Getenv("KUBECONFIG")
if kubeconfig == "" { if kubeconfig == "" {
kubeconfig = filepath.Join(homedir.Get(), ".kube/config") kubeconfig = filepath.Join(homedir.Get(), ".kube/config")

View File

@@ -238,6 +238,7 @@ func (d *Driver) Features(_ context.Context) map[driver.Feature]bool {
driver.DockerExporter: d.DockerAPI != nil, driver.DockerExporter: d.DockerAPI != nil,
driver.CacheExport: true, driver.CacheExport: true,
driver.MultiPlatform: true, // Untested (needs multiple Driver instances) driver.MultiPlatform: true, // Untested (needs multiple Driver instances)
driver.DirectPush: true,
driver.DefaultLoad: d.defaultLoad, driver.DefaultLoad: d.defaultLoad,
} }
} }

View File

@@ -90,7 +90,7 @@ func ListRunningPods(ctx context.Context, client clientcorev1.PodInterface, depl
for i := range podList.Items { for i := range podList.Items {
pod := &podList.Items[i] pod := &podList.Items[i]
if pod.Status.Phase == corev1.PodRunning { if pod.Status.Phase == corev1.PodRunning {
logrus.Debugf("pod runnning: %q", pod.Name) logrus.Debugf("pod running: %q", pod.Name)
runningPods = append(runningPods, pod) runningPods = append(runningPods, pod)
} }
} }

View File

@@ -25,7 +25,7 @@ func GenerateNodeName(builderName string, txn *store.Txn) (string, error) {
} }
var name string var name string
for i := 0; i < 6; i++ { for range 6 {
name, err = randomName() name, err = randomName()
if err != nil { if err != nil {
return "", err return "", err

View File

@@ -164,6 +164,7 @@ func (d *Driver) Features(ctx context.Context) map[driver.Feature]bool {
driver.DockerExporter: true, driver.DockerExporter: true,
driver.CacheExport: true, driver.CacheExport: true,
driver.MultiPlatform: true, driver.MultiPlatform: true,
driver.DirectPush: true,
driver.DefaultLoad: d.defaultLoad, driver.DefaultLoad: d.defaultLoad,
} }
} }

46
go.mod
View File

@@ -6,35 +6,37 @@ require (
github.com/Masterminds/semver/v3 v3.2.1 github.com/Masterminds/semver/v3 v3.2.1
github.com/Microsoft/go-winio v0.6.2 github.com/Microsoft/go-winio v0.6.2
github.com/aws/aws-sdk-go-v2/config v1.27.27 github.com/aws/aws-sdk-go-v2/config v1.27.27
github.com/compose-spec/compose-go/v2 v2.4.7 github.com/compose-spec/compose-go/v2 v2.4.8
github.com/containerd/console v1.0.4 github.com/containerd/console v1.0.4
github.com/containerd/containerd/v2 v2.0.2 github.com/containerd/containerd/v2 v2.0.3
github.com/containerd/continuity v0.4.5 github.com/containerd/continuity v0.4.5
github.com/containerd/errdefs v1.0.0 github.com/containerd/errdefs v1.0.0
github.com/containerd/log v0.1.0 github.com/containerd/log v0.1.0
github.com/containerd/platforms v1.0.0-rc.1 github.com/containerd/platforms v1.0.0-rc.1
github.com/containerd/typeurl/v2 v2.2.3 github.com/containerd/typeurl/v2 v2.2.3
github.com/creack/pty v1.1.24 github.com/creack/pty v1.1.24
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/davecgh/go-spew v1.1.1
github.com/distribution/reference v0.6.0 github.com/distribution/reference v0.6.0
github.com/docker/cli v27.5.0+incompatible github.com/docker/cli v28.0.1+incompatible
github.com/docker/cli-docs-tool v0.9.0 github.com/docker/cli-docs-tool v0.9.0
github.com/docker/docker v27.5.0+incompatible github.com/docker/docker v28.0.1+incompatible
github.com/docker/go-units v0.5.0 github.com/docker/go-units v0.5.0
github.com/gofrs/flock v0.12.1 github.com/gofrs/flock v0.12.1
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/hashicorp/go-cty-funcs v0.0.0-20241120183456-c51673e0b3dd github.com/hashicorp/go-cty-funcs v0.0.0-20241120183456-c51673e0b3dd
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/hcl/v2 v2.23.0 github.com/hashicorp/hcl/v2 v2.23.0
github.com/in-toto/in-toto-golang v0.5.0 github.com/in-toto/in-toto-golang v0.5.0
github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/moby/buildkit v0.19.0-rc2 github.com/moby/buildkit v0.20.1
github.com/moby/sys/mountinfo v0.7.2 github.com/moby/sys/mountinfo v0.7.2
github.com/moby/sys/signal v0.7.1 github.com/moby/sys/signal v0.7.1
github.com/morikuni/aec v1.0.0 github.com/morikuni/aec v1.0.0
github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.0 github.com/opencontainers/image-spec v1.1.0
github.com/pelletier/go-toml v1.9.5 github.com/pelletier/go-toml v1.9.5
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b
@@ -44,17 +46,20 @@ require (
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250211190051-7d4944a45bb6
github.com/zclconf/go-cty v1.16.0 github.com/zclconf/go-cty v1.16.0
go.opentelemetry.io/otel v1.31.0 go.opentelemetry.io/otel v1.31.0
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0
go.opentelemetry.io/otel/metric v1.31.0 go.opentelemetry.io/otel/metric v1.31.0
go.opentelemetry.io/otel/sdk v1.31.0 go.opentelemetry.io/otel/sdk v1.31.0
go.opentelemetry.io/otel/trace v1.31.0 go.opentelemetry.io/otel/trace v1.31.0
golang.org/x/mod v0.21.0 golang.org/x/mod v0.22.0
golang.org/x/sync v0.10.0 golang.org/x/sync v0.10.0
golang.org/x/sys v0.28.0 golang.org/x/sys v0.29.0
golang.org/x/term v0.27.0 golang.org/x/term v0.27.0
golang.org/x/text v0.21.0 golang.org/x/text v0.21.0
google.golang.org/grpc v1.68.1 google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38
google.golang.org/grpc v1.69.4
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
google.golang.org/protobuf v1.35.2 google.golang.org/protobuf v1.35.2
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
@@ -65,7 +70,7 @@ require (
require ( require (
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect
github.com/agext/levenshtein v1.2.3 // indirect github.com/agext/levenshtein v1.2.3 // indirect
github.com/apparentlymart/go-cidr v1.0.1 // indirect github.com/apparentlymart/go-cidr v1.0.1 // indirect
@@ -114,7 +119,6 @@ require (
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/imdario/mergo v0.3.16 // indirect github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
@@ -133,12 +137,12 @@ require (
github.com/moby/sys/sequential v0.6.0 // indirect github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/user v0.3.0 // indirect github.com/moby/sys/user v0.3.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect
github.com/moby/term v0.5.0 // indirect github.com/moby/term v0.5.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/common v0.55.0 // indirect
@@ -166,13 +170,12 @@ require (
go.opentelemetry.io/otel/sdk/metric v1.31.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.31.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect
golang.org/x/crypto v0.31.0 // indirect golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
golang.org/x/net v0.33.0 // indirect golang.org/x/net v0.33.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/time v0.6.0 // indirect golang.org/x/time v0.6.0 // indirect
golang.org/x/tools v0.25.0 // indirect golang.org/x/tools v0.27.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/klog/v2 v2.130.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect
@@ -182,3 +185,12 @@ require (
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect
) )
exclude (
// FIXME(thaJeztah): remoove this once kubernetes updated their dependencies to no longer need this.
//
// For additional details, see this PR and links mentioned in that PR:
// https://github.com/kubernetes-sigs/kustomize/pull/5830#issuecomment-2569960859
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
)

78
go.sum
View File

@@ -2,8 +2,8 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8af
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 h1:dIScnXFlF784X79oi7MzVT6GWqr/W1uUt0pB5CsDs9M= github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 h1:dIScnXFlF784X79oi7MzVT6GWqr/W1uUt0pB5CsDs9M=
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2/go.mod h1:gCLVsLfv1egrcZu+GoJATN5ts75F2s62ih/457eWzOw= github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2/go.mod h1:gCLVsLfv1egrcZu+GoJATN5ts75F2s62ih/457eWzOw=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
@@ -77,16 +77,16 @@ github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
github.com/compose-spec/compose-go/v2 v2.4.7 h1:WNpz5bIbKG+G+w9pfu72B1ZXr+Og9jez8TMEo8ecXPk= github.com/compose-spec/compose-go/v2 v2.4.8 h1:7Myl8wDRl/4mRz77S+eyDJymGGEHu0diQdGSSeyq90A=
github.com/compose-spec/compose-go/v2 v2.4.7/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc= github.com/compose-spec/compose-go/v2 v2.4.8/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0= github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0= github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0= github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0=
github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc= github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc=
github.com/containerd/containerd/v2 v2.0.2 h1:GmH/tRBlTvrXOLwSpWE2vNAm8+MqI6nmxKpKBNKY8Wc= github.com/containerd/containerd/v2 v2.0.3 h1:zBKgwgZsuu+LPCMzCLgA4sC4MiZzZ59ZT31XkmiISQM=
github.com/containerd/containerd/v2 v2.0.2/go.mod h1:wIqEvQ/6cyPFUGJ5yMFanspPabMLor+bF865OHvNTTI= github.com/containerd/containerd/v2 v2.0.3/go.mod h1:5j9QUUaV/cy9ZeAx4S+8n9ffpf+iYnEj4jiExgcbuLY=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
@@ -117,21 +117,20 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v27.5.0+incompatible h1:aMphQkcGtpHixwwhAXJT1rrK/detk2JIvDaFkLctbGM= github.com/docker/cli v28.0.1+incompatible h1:g0h5NQNda3/CxIsaZfH4Tyf6vpxFth7PYl3hgCPOKzs=
github.com/docker/cli v27.5.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v28.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli-docs-tool v0.9.0 h1:CVwQbE+ZziwlPqrJ7LRyUF6GvCA+6gj7MTCsayaK9t0= github.com/docker/cli-docs-tool v0.9.0 h1:CVwQbE+ZziwlPqrJ7LRyUF6GvCA+6gj7MTCsayaK9t0=
github.com/docker/cli-docs-tool v0.9.0/go.mod h1:ClrwlNW+UioiRyH9GiAOe1o3J/TsY3Tr1ipoypjAUtc= github.com/docker/cli-docs-tool v0.9.0/go.mod h1:ClrwlNW+UioiRyH9GiAOe1o3J/TsY3Tr1ipoypjAUtc=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v27.5.0+incompatible h1:um++2NcQtGRTz5eEgO6aJimo6/JxrTXC941hd05JO6U= github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0=
github.com/docker/docker v27.5.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
@@ -153,8 +152,6 @@ github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE= github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE=
github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
@@ -298,8 +295,8 @@ github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/z
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/buildkit v0.19.0-rc2 h1:7sAuQ5bDNIbdfmc7UDbrWJ2UPOR5w9rNWgnrEoC5aoo= github.com/moby/buildkit v0.20.1 h1:sT0ZXhhNo5rVbMcYfgttma3TdUHfO5JjFA0UAL8p9fY=
github.com/moby/buildkit v0.19.0-rc2/go.mod h1:4WYJLet/NI2p1o2rPQ6CIFpyyyvwvPz/TVISmwqqpHI= github.com/moby/buildkit v0.20.1/go.mod h1:Rq9nB/fJImdk6QeM0niKtOHJqwKeYMrK847hTTDVuA4=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
@@ -318,8 +315,8 @@ github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -352,23 +349,22 @@ github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQ
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 h1:DmNGcqH3WDbV5k8OJ+esPWbqUOX5rMLR2PMvziDMJi0=
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626/go.mod h1:BRHJJd0E+cx42OybVYSgUvZmU0B8P9gZuRXlZUP7TKI=
github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8= github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8=
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
@@ -437,8 +433,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c= github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw= github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205 h1:eUk79E1w8yMtXeHSzjKorxuC8qJOnyXQnLaJehxpJaI= github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205 h1:eUk79E1w8yMtXeHSzjKorxuC8qJOnyXQnLaJehxpJaI=
@@ -447,6 +441,8 @@ github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a h1:EfGw4G0x/8qXW
github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a/go.mod h1:Dl/9oEjK7IqnjAm21Okx/XIxUCFJzvh+XdVHUlBwXTw= github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a/go.mod h1:Dl/9oEjK7IqnjAm21Okx/XIxUCFJzvh+XdVHUlBwXTw=
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 h1:7I5c2Ig/5FgqkYOh/N87NzoyI9U15qUPXhDD8uCupv8= github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 h1:7I5c2Ig/5FgqkYOh/N87NzoyI9U15qUPXhDD8uCupv8=
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE= github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE=
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250211190051-7d4944a45bb6 h1:RT/a0RvdX84iwtOrUK45+wjcNpaG+hS7n7XFYqj4axg=
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250211190051-7d4944a45bb6/go.mod h1:3Ez1Paeg+0Ghu3KwpEGC1HgZ4CHDlg+Ez/5Baeomk54=
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0= github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk= github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw= github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw=
@@ -490,6 +486,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0 h1:UGZ1QwZWY67Z6BmckTU+9Rxn04m2bD3gD6Mk0OIOCPk=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0/go.mod h1:fcwWuDuaObkkChiDlhEpSq9+X1C0omv+s5mBtToAQ64=
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
@@ -512,12 +510,12 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -549,8 +547,8 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
@@ -565,20 +563,20 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg= google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 h1:2oV8dfuIkM1Ti7DwXc0BJfnwr9csz4TDXI9EmiI+Rbw=
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38/go.mod h1:vuAjtvlwkDKF6L1GQ0SokiRLCGFfeBUXWr/aFFkHACc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI= google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
@@ -628,7 +626,3 @@ sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+s
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
tags.cncf.io/container-device-interface v0.8.0 h1:8bCFo/g9WODjWx3m6EYl3GfUG31eKJbaggyBDxEldRc=
tags.cncf.io/container-device-interface v0.8.0/go.mod h1:Apb7N4VdILW0EVdEMRYXIDVRZfNJZ+kmEUss2kRRQ6Y=
tags.cncf.io/container-device-interface/specs-go v0.8.0 h1:QYGFzGxvYK/ZLMrjhvY0RjpUavIn4KcmRmVP/JjdBTA=
tags.cncf.io/container-device-interface/specs-go v0.8.0/go.mod h1:BhJIkjjPh4qpys+qm4DAYtUyryaTDg9zris+AczXyws=

View File

@@ -9,10 +9,13 @@ Vagrant.configure("2") do |config|
config.vm.provision "init", type: "shell", run: "once" do |sh| config.vm.provision "init", type: "shell", run: "once" do |sh|
sh.inline = <<~SHELL sh.inline = <<~SHELL
set -x
pkg bootstrap pkg bootstrap
pkg install -y go123 git pkg install -y git
ln -s /usr/local/bin/go123 /usr/local/bin/go
go install gotest.tools/gotestsum@#{ENV['GOTESTSUM_VERSION']} fetch https://go.dev/dl/go#{ENV['GO_VERSION']}.freebsd-amd64.tar.gz
tar -C /usr/local -xzf go#{ENV['GO_VERSION']}.freebsd-amd64.tar.gz
ln -s /usr/local/go/bin/go /usr/local/bin/go
SHELL SHELL
end end
end end

32
hack/Vagrantfile.netbsd Normal file
View File

@@ -0,0 +1,32 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
config.vm.box = "generic/netbsd9"
config.vm.boot_timeout = 900
config.vm.synced_folder ".", "/vagrant", type: "rsync"
config.ssh.keep_alive = true
config.vm.provision "init", type: "shell", run: "once" do |sh|
sh.inline = <<~SHELL
set -x
mkdir -p /var/tmp
chmod 1777 /var/tmp
pkgin -y install git mozilla-rootcerts
mozilla-rootcerts install
ftp https://go.dev/dl/go#{ENV['GO_VERSION']}.netbsd-amd64.tar.gz
tar -C /var/tmp -xzf go#{ENV['GO_VERSION']}.netbsd-amd64.tar.gz
cat << 'EOF' > /usr/bin/go-wrapper
#!/bin/sh
export TMPDIR="/var/tmp"
exec /var/tmp/go/bin/go "$@"
EOF
chmod +x /usr/bin/go-wrapper
ln -s /usr/bin/go-wrapper /usr/bin/go
SHELL
end
end

View File

@@ -10,12 +10,12 @@ Vagrant.configure("2") do |config|
config.vm.provision "init", type: "shell", run: "once" do |sh| config.vm.provision "init", type: "shell", run: "once" do |sh|
sh.inline = <<~SHELL sh.inline = <<~SHELL
set -x
pkg_add -x git pkg_add -x git
ftp https://go.dev/dl/go1.23.3.openbsd-amd64.tar.gz ftp https://go.dev/dl/go#{ENV['GO_VERSION']}.openbsd-amd64.tar.gz
tar -C /usr/local -xzf go1.23.3.openbsd-amd64.tar.gz tar -C /usr/local -xzf go#{ENV['GO_VERSION']}.openbsd-amd64.tar.gz
ln -s /usr/local/go/bin/go /usr/local/bin/go ln -s /usr/local/go/bin/go /usr/local/bin/go
go install gotest.tools/gotestsum@#{ENV['GOTESTSUM_VERSION']}
SHELL SHELL
end end
end end

View File

@@ -5,9 +5,10 @@ ARG ALPINE_VERSION=3.21
ARG XX_VERSION=1.6.1 ARG XX_VERSION=1.6.1
ARG GOLANGCI_LINT_VERSION=1.62.0 ARG GOLANGCI_LINT_VERSION=1.62.0
ARG GOPLS_VERSION=v0.26.0 # v0.31 requires go1.24
ARG GOPLS_VERSION=v0.30.0
# disabled: deprecated unusedvariable simplifyrange # disabled: deprecated unusedvariable simplifyrange
ARG GOPLS_ANALYZERS="embeddirective fillreturns infertypeargs nonewvars norangeoverfunc noresultvalues simplifycompositelit simplifyslice undeclaredname unusedparams useany" ARG GOPLS_ANALYZERS="embeddirective fillreturns hostport infertypeargs modernize nonewvars noresultvalues simplifycompositelit simplifyslice unusedparams yield"
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx

View File

@@ -2,6 +2,8 @@
set -eu -o pipefail set -eu -o pipefail
: "${GITHUB_ACTIONS=}"
: "${BUILDX_CMD=docker buildx}" : "${BUILDX_CMD=docker buildx}"
: "${TEST_COVERAGE=}" : "${TEST_COVERAGE=}"
@@ -37,7 +39,15 @@ if [ "$TEST_COVERAGE" = "1" ]; then
export GO_TEST_COVERPROFILE="/testreports/coverage-report$TEST_REPORT_SUFFIX.txt" export GO_TEST_COVERPROFILE="/testreports/coverage-report$TEST_REPORT_SUFFIX.txt"
fi fi
cid=$(docker create --rm --privileged \ dockerConfigMount=""
if [ "$GITHUB_ACTIONS" = "true" ]; then
dockerConfigPath="$HOME/.docker/config.json"
if [ -f "$dockerConfigPath" ]; then
dockerConfigMount="-v $dockerConfigPath:/root/.docker/config.json:ro"
fi
fi
cid=$(docker create --rm --privileged $dockerConfigMount \
-v /tmp $testReportsVol \ -v /tmp $testReportsVol \
--volumes-from=$cacheVolume \ --volumes-from=$cacheVolume \
-e GITHUB_REF \ -e GITHUB_REF \

View File

@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"sync" "sync"
"github.com/docker/buildx/util/confutil" "github.com/docker/buildx/util/confutil"
@@ -14,6 +15,7 @@ import (
) )
const ( const (
version = 2
refsDir = "refs" refsDir = "refs"
groupDir = "__group__" groupDir = "__group__"
) )
@@ -31,12 +33,8 @@ type State struct {
} }
type StateGroup struct { type StateGroup struct {
// Definition is the raw representation of the group (bake definition)
Definition []byte
// Targets are the targets invoked // Targets are the targets invoked
Targets []string `json:",omitempty"` Targets []string `json:",omitempty"`
// Inputs are the user inputs (bake overrides)
Inputs []string `json:",omitempty"`
// Refs are used to track all the refs that belong to the same group // Refs are used to track all the refs that belong to the same group
Refs []string Refs []string
} }
@@ -52,9 +50,7 @@ func New(cfg *confutil.Config) (*LocalState, error) {
if err := cfg.MkdirAll(refsDir, 0700); err != nil { if err := cfg.MkdirAll(refsDir, 0700); err != nil {
return nil, err return nil, err
} }
return &LocalState{ return &LocalState{cfg: cfg}, nil
cfg: cfg,
}, nil
} }
func (ls *LocalState) ReadRef(builderName, nodeName, id string) (*State, error) { func (ls *LocalState) ReadRef(builderName, nodeName, id string) (*State, error) {
@@ -87,8 +83,12 @@ func (ls *LocalState) SaveRef(builderName, nodeName, id string, st State) error
return ls.cfg.AtomicWriteFile(filepath.Join(refDir, id), dt, 0644) return ls.cfg.AtomicWriteFile(filepath.Join(refDir, id), dt, 0644)
} }
func (ls *LocalState) GroupDir() string {
return filepath.Join(ls.cfg.Dir(), refsDir, groupDir)
}
func (ls *LocalState) ReadGroup(id string) (*StateGroup, error) { func (ls *LocalState) ReadGroup(id string) (*StateGroup, error) {
dt, err := os.ReadFile(filepath.Join(ls.cfg.Dir(), refsDir, groupDir, id)) dt, err := os.ReadFile(filepath.Join(ls.GroupDir(), id))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -208,7 +208,7 @@ func (ls *LocalState) removeGroup(id string) error {
if id == "" { if id == "" {
return errors.Errorf("group ref empty") return errors.Errorf("group ref empty")
} }
f := filepath.Join(ls.cfg.Dir(), refsDir, groupDir, id) f := filepath.Join(ls.GroupDir(), id)
if _, err := os.Lstat(f); err != nil { if _, err := os.Lstat(f); err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
return err return err
@@ -230,3 +230,16 @@ func (ls *LocalState) validate(builderName, nodeName, id string) error {
} }
return nil return nil
} }
func (ls *LocalState) readVersion() int {
if vdt, err := os.ReadFile(filepath.Join(ls.cfg.Dir(), refsDir, "version")); err == nil {
if v, err := strconv.Atoi(string(vdt)); err == nil {
return v
}
}
return 1
}
func (ls *LocalState) writeVersion(version int) error {
return ls.cfg.AtomicWriteFile(filepath.Join(refsDir, "version"), []byte(strconv.Itoa(version)), 0600)
}

View File

@@ -68,9 +68,7 @@ var (
testStateGroupID = "kvqs0sgly2rmitz84r25u9qd0" testStateGroupID = "kvqs0sgly2rmitz84r25u9qd0"
testStateGroup = StateGroup{ testStateGroup = StateGroup{
Definition: []byte(`{"group":{"default":{"targets":["pre-checkin"]},"pre-checkin":{"targets":["vendor-update","format","build"]}},"target":{"build":{"context":".","dockerfile":"dev.Dockerfile","target":"build-update","platforms":["linux/amd64"],"output":["."]},"format":{"context":".","dockerfile":"dev.Dockerfile","target":"format-update","platforms":["linux/amd64"],"output":["."]},"vendor-update":{"context":".","dockerfile":"dev.Dockerfile","target":"vendor-update","platforms":["linux/amd64"],"output":["."]}}}`),
Targets: []string{"pre-checkin"}, Targets: []string{"pre-checkin"},
Inputs: []string{"*.platform=linux/amd64"},
Refs: []string{"builder/builder0/hx2qf1w11qvz1x3k471c5i8xw", "builder/builder0/968zj0g03jmlx0s8qslnvh6rl", "builder/builder0/naf44f9i1710lf7y12lv5hb1z"}, Refs: []string{"builder/builder0/hx2qf1w11qvz1x3k471c5i8xw", "builder/builder0/968zj0g03jmlx0s8qslnvh6rl", "builder/builder0/naf44f9i1710lf7y12lv5hb1z"},
} }

56
localstate/migrate.go Normal file
View File

@@ -0,0 +1,56 @@
package localstate
import (
"encoding/json"
"os"
"path/filepath"
"github.com/pkg/errors"
)
func (ls *LocalState) MigrateIfNeeded() error {
currentVersion := ls.readVersion()
if currentVersion == version {
return nil
}
migrations := map[int]func(*LocalState) error{
2: (*LocalState).migration2,
}
for v := currentVersion + 1; v <= version; v++ {
migration, found := migrations[v]
if !found {
return errors.Errorf("localstate migration v%d not found", v)
}
if err := migration(ls); err != nil {
return errors.Wrapf(err, "localstate migration v%d failed", v)
}
}
return ls.writeVersion(version)
}
func (ls *LocalState) migration2() error {
return filepath.Walk(ls.GroupDir(), func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
dt, err := os.ReadFile(path)
if err != nil {
return err
}
var stg StateGroup
if err := json.Unmarshal(dt, &stg); err != nil {
return err
}
mdt, err := json.Marshal(stg)
if err != nil {
return err
}
if err := os.WriteFile(path, mdt, 0600); err != nil {
return err
}
return nil
})
}

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"slices"
"github.com/docker/buildx/monitor/types" "github.com/docker/buildx/monitor/types"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -50,14 +51,7 @@ func (cm *AttachCmd) Exec(ctx context.Context, args []string) error {
if err != nil { if err != nil {
return errors.Errorf("failed to get the list of sessions: %v", err) return errors.Errorf("failed to get the list of sessions: %v", err)
} }
found := false if !slices.Contains(refs, ref) {
for _, s := range refs {
if s == ref {
found = true
break
}
}
if !found {
return errors.Errorf("unknown ID: %q", ref) return errors.Errorf("unknown ID: %q", ref)
} }
cm.m.Detach() // Finish existing attach cm.m.Detach() // Finish existing attach

View File

@@ -66,7 +66,7 @@ func (cm *ReloadCmd) Exec(ctx context.Context, args []string) error {
if err != nil { if err != nil {
var be *controllererrors.BuildError var be *controllererrors.BuildError
if errors.As(err, &be) { if errors.As(err, &be) {
ref = be.Ref ref = be.SessionID
resultUpdated = true resultUpdated = true
} else { } else {
fmt.Printf("failed to reload: %v\n", err) fmt.Printf("failed to reload: %v\n", err)

View File

@@ -2,6 +2,7 @@ package store
import ( import (
"fmt" "fmt"
"slices"
"time" "time"
"github.com/containerd/platforms" "github.com/containerd/platforms"
@@ -44,7 +45,7 @@ func (ng *NodeGroup) Leave(name string) error {
if len(ng.Nodes) == 1 { if len(ng.Nodes) == 1 {
return errors.Errorf("can not leave last node, do you want to rm instance instead?") return errors.Errorf("can not leave last node, do you want to rm instance instead?")
} }
ng.Nodes = append(ng.Nodes[:i], ng.Nodes[i+1:]...) ng.Nodes = slices.Delete(ng.Nodes, i, i+1)
return nil return nil
} }

View File

@@ -39,7 +39,7 @@ func ValidateName(s string) (string, error) {
func GenerateName(txn *Txn) (string, error) { func GenerateName(txn *Txn) (string, error) {
var name string var name string
for i := 0; i < 6; i++ { for i := range 6 {
name = namesgenerator.GetRandomName(i) name = namesgenerator.GetRandomName(i)
if _, err := txn.NodeGroupByName(name); err != nil { if _, err := txn.NodeGroupByName(name); err != nil {
if !os.IsNotExist(errors.Cause(err)) { if !os.IsNotExist(errors.Cause(err)) {

View File

@@ -38,6 +38,7 @@ func bakeCmd(sb integration.Sandbox, opts ...cmdOpt) (string, error) {
var bakeTests = []func(t *testing.T, sb integration.Sandbox){ var bakeTests = []func(t *testing.T, sb integration.Sandbox){
testBakePrint, testBakePrint,
testBakePrintSensitive, testBakePrintSensitive,
testBakePrintOverrideEmpty,
testBakeLocal, testBakeLocal,
testBakeLocalMulti, testBakeLocalMulti,
testBakeRemote, testBakeRemote,
@@ -286,6 +287,47 @@ RUN echo "Hello ${HELLO}"
} }
} }
func testBakePrintOverrideEmpty(t *testing.T, sb integration.Sandbox) {
dockerfile := []byte(`
FROM scratch
COPY foo /foo
`)
bakefile := []byte(`
target "default" {
cache-to = ["type=gha,mode=min,scope=integration-tests"]
}
`)
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--print", "--set", "*.cache-to="))
stdout := bytes.Buffer{}
stderr := bytes.Buffer{}
cmd.Stdout = &stdout
cmd.Stderr = &stderr
require.NoError(t, cmd.Run(), stdout.String(), stderr.String())
require.JSONEq(t, `{
"group": {
"default": {
"targets": [
"default"
]
}
},
"target": {
"default": {
"context": ".",
"dockerfile": "Dockerfile"
}
}
}`, stdout.String())
}
func testBakeLocal(t *testing.T, sb integration.Sandbox) { func testBakeLocal(t *testing.T, sb integration.Sandbox) {
dockerfile := []byte(` dockerfile := []byte(`
FROM scratch FROM scratch
@@ -871,6 +913,7 @@ target "default" {
}) })
} }
} }
func testBakeSetNonExistingOutsideNoParallel(t *testing.T, sb integration.Sandbox) { func testBakeSetNonExistingOutsideNoParallel(t *testing.T, sb integration.Sandbox) {
for _, ent := range []bool{true, false} { for _, ent := range []bool{true, false} {
t.Run(fmt.Sprintf("ent=%v", ent), func(t *testing.T) { t.Run(fmt.Sprintf("ent=%v", ent), func(t *testing.T) {
@@ -973,11 +1016,11 @@ FROM scratch
COPY foo /foo COPY foo /foo
`) `)
destDir := t.TempDir() destDir := t.TempDir()
bakefile := []byte(fmt.Sprintf(` bakefile := fmt.Appendf(nil, `
target "default" { target "default" {
output = ["type=local,dest=%s/not/exists"] output = ["type=local,dest=%s/not/exists"]
} }
`, destDir)) `, destDir)
dir := tmpdir( dir := tmpdir(
t, t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600), fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
@@ -1007,11 +1050,11 @@ FROM scratch
COPY foo /foo COPY foo /foo
`) `)
destDir := t.TempDir() destDir := t.TempDir()
bakefile := []byte(fmt.Sprintf(` bakefile := fmt.Appendf(nil, `
target "default" { target "default" {
output = ["type=local,dest=%s"] output = ["type=local,dest=%s"]
} }
`, destDir)) `, destDir)
dir := tmpdir( dir := tmpdir(
t, t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600), fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
@@ -1108,11 +1151,11 @@ COPY Dockerfile /foo
keyDir := t.TempDir() keyDir := t.TempDir()
err := writeTempPrivateKey(filepath.Join(keyDir, "id_rsa")) err := writeTempPrivateKey(filepath.Join(keyDir, "id_rsa"))
require.NoError(t, err) require.NoError(t, err)
bakefile := []byte(fmt.Sprintf(` bakefile := fmt.Appendf(nil, `
target "default" { target "default" {
ssh = ["key=%s"] ssh = ["key=%s"]
} }
`, filepath.Join(keyDir, "id_rsa"))) `, filepath.Join(keyDir, "id_rsa"))
dir := tmpdir( dir := tmpdir(
t, t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600), fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
@@ -1272,7 +1315,7 @@ target "default" {
type mdT struct { type mdT struct {
Default struct { Default struct {
BuildRef string `json:"buildx.build.ref"` BuildRef string `json:"buildx.build.ref"`
BuildProvenance map[string]interface{} `json:"buildx.build.provenance"` BuildProvenance map[string]any `json:"buildx.build.provenance"`
} `json:"default"` } `json:"default"`
} }
var md mdT var md mdT

View File

@@ -805,7 +805,7 @@ func buildMetadataProvenance(t *testing.T, sb integration.Sandbox, metadataMode
type mdT struct { type mdT struct {
BuildRef string `json:"buildx.build.ref"` BuildRef string `json:"buildx.build.ref"`
BuildProvenance map[string]interface{} `json:"buildx.build.provenance"` BuildProvenance map[string]any `json:"buildx.build.provenance"`
} }
var md mdT var md mdT
err = json.Unmarshal(dt, &md) err = json.Unmarshal(dt, &md)

View File

@@ -50,7 +50,7 @@ func withDir(dir string) cmdOpt {
func buildxCmd(sb integration.Sandbox, opts ...cmdOpt) *exec.Cmd { func buildxCmd(sb integration.Sandbox, opts ...cmdOpt) *exec.Cmd {
cmd := exec.Command("buildx") cmd := exec.Command("buildx")
cmd.Env = append([]string{}, os.Environ()...) cmd.Env = os.Environ()
for _, opt := range opts { for _, opt := range opts {
opt(cmd) opt(cmd)
} }
@@ -77,7 +77,7 @@ func buildxCmd(sb integration.Sandbox, opts ...cmdOpt) *exec.Cmd {
func dockerCmd(sb integration.Sandbox, opts ...cmdOpt) *exec.Cmd { func dockerCmd(sb integration.Sandbox, opts ...cmdOpt) *exec.Cmd {
cmd := exec.Command("docker") cmd := exec.Command("docker")
cmd.Env = append([]string{}, os.Environ()...) cmd.Env = os.Environ()
for _, opt := range opts { for _, opt := range opts {
opt(cmd) opt(cmd)
} }
@@ -214,7 +214,7 @@ func skipNoCompatBuildKit(t *testing.T, sb integration.Sandbox, constraint strin
} }
} }
func ptrstr(s interface{}) *string { func ptrstr(s any) *string {
var n *string var n *string
if reflect.ValueOf(s).Kind() == reflect.String { if reflect.ValueOf(s).Kind() == reflect.String {
ss := s.(string) ss := s.(string)

View File

@@ -45,7 +45,7 @@ func testRmMulti(t *testing.T, sb integration.Sandbox) {
} }
var builderNames []string var builderNames []string
for i := 0; i < 3; i++ { for range 3 {
out, err := createCmd(sb, withArgs("--driver", "docker-container")) out, err := createCmd(sb, withArgs("--driver", "docker-container"))
require.NoError(t, err, out) require.NoError(t, err, out)
builderName := strings.TrimSpace(out) builderName := strings.TrimSpace(out)

View File

@@ -2,6 +2,7 @@ package workers
import ( import (
"os" "os"
"slices"
"strings" "strings"
"github.com/moby/buildkit/util/testutil/integration" "github.com/moby/buildkit/util/testutil/integration"
@@ -49,23 +50,14 @@ func (s *backend) ExtraEnv() []string {
func (s backend) Supports(feature string) bool { func (s backend) Supports(feature string) bool {
if enabledFeatures := os.Getenv("BUILDKIT_TEST_ENABLE_FEATURES"); enabledFeatures != "" { if enabledFeatures := os.Getenv("BUILDKIT_TEST_ENABLE_FEATURES"); enabledFeatures != "" {
for _, enabledFeature := range strings.Split(enabledFeatures, ",") { if slices.Contains(strings.Split(enabledFeatures, ","), feature) {
if feature == enabledFeature {
return true return true
} }
} }
}
if disabledFeatures := os.Getenv("BUILDKIT_TEST_DISABLE_FEATURES"); disabledFeatures != "" { if disabledFeatures := os.Getenv("BUILDKIT_TEST_DISABLE_FEATURES"); disabledFeatures != "" {
for _, disabledFeature := range strings.Split(disabledFeatures, ",") { if slices.Contains(strings.Split(disabledFeatures, ","), feature) {
if feature == disabledFeature {
return false return false
} }
} }
} return !slices.Contains(s.unsupportedFeatures, feature)
for _, unsupportedFeature := range s.unsupportedFeatures {
if feature == unsupportedFeature {
return false
}
}
return true
} }

View File

@@ -90,8 +90,8 @@ func (a *Attest) ToPB() *controllerapi.Attest {
} }
func (a *Attest) MarshalJSON() ([]byte, error) { func (a *Attest) MarshalJSON() ([]byte, error) {
m := make(map[string]interface{}, len(a.Attrs)+2) m := make(map[string]any, len(a.Attrs)+2)
for k, v := range m { for k, v := range a.Attrs {
m[k] = v m[k] = v
} }
m["type"] = a.Type m["type"] = a.Type
@@ -102,7 +102,7 @@ func (a *Attest) MarshalJSON() ([]byte, error) {
} }
func (a *Attest) UnmarshalJSON(data []byte) error { func (a *Attest) UnmarshalJSON(data []byte) error {
var m map[string]interface{} var m map[string]any
if err := json.Unmarshal(data, &m); err != nil { if err := json.Unmarshal(data, &m); err != nil {
return err return err
} }
@@ -148,9 +148,8 @@ func (a *Attest) UnmarshalText(text []byte) error {
if !ok { if !ok {
return errors.Errorf("invalid value %s", field) return errors.Errorf("invalid value %s", field)
} }
key = strings.TrimSpace(strings.ToLower(key))
switch key { switch strings.TrimSpace(strings.ToLower(key)) {
case "type": case "type":
a.Type = value a.Type = value
case "disabled": case "disabled":

View File

@@ -22,18 +22,19 @@ func (e *Attests) FromCtyValue(in cty.Value, p cty.Path) error {
return p.NewErrorf("%s", convert.MismatchMessage(got, want)) return p.NewErrorf("%s", convert.MismatchMessage(got, want))
} }
func (e *Attests) fromCtyValue(in cty.Value, p cty.Path) error { func (e *Attests) fromCtyValue(in cty.Value, p cty.Path) (retErr error) {
*e = make([]*Attest, 0, in.LengthInt()) *e = make([]*Attest, 0, in.LengthInt())
for elem := in.ElementIterator(); elem.Next(); {
_, value := elem.Element()
yield := func(value cty.Value) bool {
entry := &Attest{} entry := &Attest{}
if err := entry.FromCtyValue(value, p); err != nil { if retErr = entry.FromCtyValue(value, p); retErr != nil {
return err return false
} }
*e = append(*e, entry) *e = append(*e, entry)
return true
} }
return nil eachElement(in)(yield)
return retErr
} }
func (e Attests) ToCtyValue() cty.Value { func (e Attests) ToCtyValue() cty.Value {
@@ -64,6 +65,10 @@ func (e *Attest) FromCtyValue(in cty.Value, p cty.Path) error {
e.Attrs = map[string]string{} e.Attrs = map[string]string{}
for it := conv.ElementIterator(); it.Next(); { for it := conv.ElementIterator(); it.Next(); {
k, v := it.Element() k, v := it.Element()
if !v.IsKnown() {
continue
}
switch key := k.AsString(); key { switch key := k.AsString(); key {
case "type": case "type":
e.Type = v.AsString() e.Type = v.AsString()

View File

@@ -0,0 +1,117 @@
package buildflags
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
"github.com/zclconf/go-cty/cty"
)
func TestAttests(t *testing.T) {
t.Run("MarshalJSON", func(t *testing.T) {
attests := Attests{
{Type: "provenance", Attrs: map[string]string{"mode": "max"}},
{Type: "sbom", Disabled: true},
{Type: "sbom", Attrs: map[string]string{
"generator": "scanner",
"ENV1": `"foo,bar"`,
"Env2": "hello",
}},
}
expected := `[{"type":"provenance","mode":"max"},{"type":"sbom","disabled":true},{"ENV1":"\"foo,bar\"","Env2":"hello","generator":"scanner","type":"sbom"}]`
actual, err := json.Marshal(attests)
require.NoError(t, err)
require.JSONEq(t, expected, string(actual))
})
t.Run("UnmarshalJSON", func(t *testing.T) {
in := `[{"type":"provenance","mode":"max"},{"type":"sbom","disabled":true},{"ENV1":"\"foo,bar\"","Env2":"hello","generator":"scanner","type":"sbom"}]`
var actual Attests
err := json.Unmarshal([]byte(in), &actual)
require.NoError(t, err)
expected := Attests{
{Type: "provenance", Attrs: map[string]string{"mode": "max"}},
{Type: "sbom", Disabled: true, Attrs: map[string]string{}},
{Type: "sbom", Disabled: false, Attrs: map[string]string{
"generator": "scanner",
"ENV1": `"foo,bar"`,
"Env2": "hello",
}},
}
require.Equal(t, expected, actual)
})
t.Run("FromCtyValue", func(t *testing.T) {
in := cty.TupleVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"type": cty.StringVal("provenance"),
"mode": cty.StringVal("max"),
}),
cty.ObjectVal(map[string]cty.Value{
"type": cty.StringVal("sbom"),
"generator": cty.StringVal("scan"),
"ENV1": cty.StringVal(`foo,bar`),
"Env2": cty.StringVal(`hello`),
}),
cty.StringVal("type=sbom,disabled=true"),
cty.StringVal(`type=sbom,generator=scan,"FOO=bar,baz",Hello=World`),
})
var actual Attests
err := actual.FromCtyValue(in, nil)
require.NoError(t, err)
expected := Attests{
{Type: "provenance", Attrs: map[string]string{"mode": "max"}},
{Type: "sbom", Attrs: map[string]string{
"generator": "scan",
"ENV1": "foo,bar",
"Env2": "hello",
}},
{Type: "sbom", Disabled: true, Attrs: map[string]string{}},
{Type: "sbom", Attrs: map[string]string{
"generator": "scan",
"FOO": "bar,baz",
"Hello": "World",
}},
}
require.Equal(t, expected, actual)
})
t.Run("ToCtyValue", func(t *testing.T) {
attests := Attests{
{Type: "provenance", Attrs: map[string]string{"mode": "max"}},
{Type: "sbom", Disabled: true},
{Type: "sbom", Attrs: map[string]string{
"generator": "scan",
"ENV1": `"foo,bar"`,
"Env2": "hello",
}},
}
actual := attests.ToCtyValue()
expected := cty.ListVal([]cty.Value{
cty.MapVal(map[string]cty.Value{
"type": cty.StringVal("provenance"),
"mode": cty.StringVal("max"),
}),
cty.MapVal(map[string]cty.Value{
"type": cty.StringVal("sbom"),
"disabled": cty.StringVal("true"),
}),
cty.MapVal(map[string]cty.Value{
"type": cty.StringVal("sbom"),
"generator": cty.StringVal("scan"),
"ENV1": cty.StringVal(`"foo,bar"`),
"Env2": cty.StringVal("hello"),
}),
})
result := actual.Equals(expected)
require.True(t, result.True())
})
}

View File

@@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"maps" "maps"
"os" "os"
"strconv"
"strings" "strings"
awsconfig "github.com/aws/aws-sdk-go-v2/config" awsconfig "github.com/aws/aws-sdk-go-v2/config"
@@ -149,7 +150,7 @@ func (e *CacheOptionsEntry) UnmarshalText(text []byte) error {
return e.validate(text) return e.validate(text)
} }
func (e *CacheOptionsEntry) validate(gv interface{}) error { func (e *CacheOptionsEntry) validate(gv any) error {
if e.Type == "" { if e.Type == "" {
var text []byte var text []byte
switch gv := gv.(type) { switch gv := gv.(type) {
@@ -167,34 +168,73 @@ func (e *CacheOptionsEntry) validate(gv interface{}) error {
return nil return nil
} }
func ParseCacheEntry(in []string) ([]*controllerapi.CacheOptionsEntry, error) { func ParseCacheEntry(in []string) (CacheOptions, error) {
if len(in) == 0 { if len(in) == 0 {
return nil, nil return nil, nil
} }
opts := make(CacheOptions, 0, len(in)) opts := make(CacheOptions, 0, len(in))
for _, in := range in { for _, in := range in {
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 {
opt := CacheOptionsEntry{}
if err := opt.UnmarshalText([]byte(field)); err != nil {
return nil, err
}
opts = append(opts, &opt)
}
continue
}
var out CacheOptionsEntry var out CacheOptionsEntry
if err := out.UnmarshalText([]byte(in)); err != nil { if err := out.UnmarshalText([]byte(in)); err != nil {
return nil, err return nil, err
} }
opts = append(opts, &out) opts = append(opts, &out)
} }
return opts.ToPB(), nil return opts, nil
} }
func addGithubToken(ci *controllerapi.CacheOptionsEntry) { func addGithubToken(ci *controllerapi.CacheOptionsEntry) {
if ci.Type != "gha" { if ci.Type != "gha" {
return return
} }
version, ok := ci.Attrs["version"]
if !ok {
// https://github.com/actions/toolkit/blob/2b08dc18f261b9fdd978b70279b85cbef81af8bc/packages/cache/src/internal/config.ts#L19
if v, ok := os.LookupEnv("ACTIONS_CACHE_SERVICE_V2"); ok {
if b, err := strconv.ParseBool(v); err == nil && b {
version = "2"
}
}
}
if _, ok := ci.Attrs["token"]; !ok { if _, ok := ci.Attrs["token"]; !ok {
if v, ok := os.LookupEnv("ACTIONS_RUNTIME_TOKEN"); ok { if v, ok := os.LookupEnv("ACTIONS_RUNTIME_TOKEN"); ok {
ci.Attrs["token"] = v ci.Attrs["token"] = v
} }
} }
if _, ok := ci.Attrs["url_v2"]; !ok && version == "2" {
// https://github.com/actions/toolkit/blob/2b08dc18f261b9fdd978b70279b85cbef81af8bc/packages/cache/src/internal/config.ts#L34-L35
if v, ok := os.LookupEnv("ACTIONS_RESULTS_URL"); ok {
ci.Attrs["url_v2"] = v
}
}
if _, ok := ci.Attrs["url"]; !ok { if _, ok := ci.Attrs["url"]; !ok {
// https://github.com/actions/toolkit/blob/2b08dc18f261b9fdd978b70279b85cbef81af8bc/packages/cache/src/internal/config.ts#L28-L33
if v, ok := os.LookupEnv("ACTIONS_CACHE_URL"); ok { if v, ok := os.LookupEnv("ACTIONS_CACHE_URL"); ok {
ci.Attrs["url"] = v ci.Attrs["url"] = v
} else if v, ok := os.LookupEnv("ACTIONS_RESULTS_URL"); ok {
ci.Attrs["url"] = v
} }
} }
} }
@@ -234,5 +274,5 @@ func isActive(pb *controllerapi.CacheOptionsEntry) bool {
if pb.Type != "gha" { if pb.Type != "gha" {
return true return true
} }
return pb.Attrs["token"] != "" && pb.Attrs["url"] != "" return pb.Attrs["token"] != "" && (pb.Attrs["url"] != "" || pb.Attrs["url_v2"] != "")
} }

View File

@@ -21,22 +21,30 @@ func (o *CacheOptions) FromCtyValue(in cty.Value, p cty.Path) error {
return p.NewErrorf("%s", convert.MismatchMessage(got, want)) return p.NewErrorf("%s", convert.MismatchMessage(got, want))
} }
func (o *CacheOptions) fromCtyValue(in cty.Value, p cty.Path) error { func (o *CacheOptions) fromCtyValue(in cty.Value, p cty.Path) (retErr error) {
*o = make([]*CacheOptionsEntry, 0, in.LengthInt()) *o = make([]*CacheOptionsEntry, 0, in.LengthInt())
for elem := in.ElementIterator(); elem.Next(); {
_, value := elem.Element()
if isEmpty(value) { yield := func(value cty.Value) bool {
continue // Special handling for a string type to handle ref only format.
if value.Type() == cty.String {
var entries CacheOptions
entries, retErr = ParseCacheEntry([]string{value.AsString()})
if retErr != nil {
return false
}
*o = append(*o, entries...)
return true
} }
entry := &CacheOptionsEntry{} entry := &CacheOptionsEntry{}
if err := entry.FromCtyValue(value, p); err != nil { if retErr = entry.FromCtyValue(value, p); retErr != nil {
return err return false
} }
*o = append(*o, entry) *o = append(*o, entry)
return true
} }
return nil eachElement(in)(yield)
return retErr
} }
func (o CacheOptions) ToCtyValue() cty.Value { func (o CacheOptions) ToCtyValue() cty.Value {
@@ -52,13 +60,6 @@ func (o CacheOptions) ToCtyValue() cty.Value {
} }
func (o *CacheOptionsEntry) FromCtyValue(in cty.Value, p cty.Path) error { 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)) conv, err := convert.Convert(in, cty.Map(cty.String))
if err != nil { if err != nil {
return err return err

Some files were not shown because too many files have changed in this diff Show More