Compare commits

...

212 Commits

Author SHA1 Message Date
Nicolas De Loof
ee33143026 capture git fetch output when debug output is enabled
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-04-08 12:03:41 +02:00
dependabot[bot]
cb0b5f6e27 build(deps): bump golang.org/x/sync from 0.12.0 to 0.13.0
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.12.0 to 0.13.0.
- [Commits](https://github.com/golang/sync/compare/v0.12.0...v0.13.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-version: 0.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-08 11:51:03 +02:00
dependabot[bot]
1384853538 build(deps): bump golang.org/x/sys from 0.31.0 to 0.32.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.31.0 to 0.32.0.
- [Commits](https://github.com/golang/sys/compare/v0.31.0...v0.32.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-version: 0.32.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-08 11:39:33 +02:00
Guillaume Lours
096b1e32d3 plugin provider support: check docker model runner status
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-04-08 11:19:01 +02:00
Guillaume Lours
bf71138df6 cleanup runPluging function
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-04-08 11:19:01 +02:00
sigi-glovebox
a1f673dcf5 Update secret detector to fix vulnerability https://github.com/golang-jwt/jwt/security/advisories/GHSA-mh63-6h87-95cp
Signed-off-by: sigi-glovebox <sigi@gloveboxapp.com>
2025-04-03 21:06:26 +02:00
Guillaume Lours
02c747a7de bump compose-go to custom version of v2.5.0
should be replace by v2.5.1 it will be released

Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-04-03 15:13:44 +02:00
Nicolas De Loof
88f4f265db communicate with plugin using json events
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-04-03 15:13:44 +02:00
Nicolas De Loof
e67348222f DRAFT external services plugin support
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-04-03 15:13:44 +02:00
Suleiman Dibirov
b543380708 feat(run): Add --quiet and --quiet-build options for the run command
Signed-off-by: Suleiman Dibirov <idsulik@gmail.com>
2025-04-03 14:50:38 +02:00
Guillaume Lours
2e75185a07 bump golang to 1.23.8
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-04-02 16:31:43 +02:00
Guillaume Lours
7bedb5a02c bump golangci-lint to version v2.0.2
and apply migration script

Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-03-31 18:50:15 +02:00
Nicolas De Loof
f9cd4d0b1d bump docker,cli,buildx
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-31 11:05:19 +02:00
Nicolas De Loof
0badcf3c8d include implicit build dependencies in build command
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-28 10:25:41 +01:00
k-kbk
ec49db98d4 fix: replace docker-compose.yml with compose.yaml
Signed-off-by: k-kbk <kkbk0077@gmail.com>
2025-03-27 15:44:00 +01:00
k-kbk
e5a353b34d fix: replace docker-compose.yml with compose.yaml
Signed-off-by: k-kbk <kkbk0077@gmail.com>
2025-03-27 15:44:00 +01:00
Nicolas De Loof
43e456145c fix scale completion
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-26 17:44:54 +01:00
Nicolas De Loof
75368c7859 introduce build --print to dump equivalent bakefile
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-26 12:24:31 +01:00
Suleiman Dibirov
6e814eac35 fix(secrets): Reverted secrets file mode 440 -> 444
Signed-off-by: Suleiman Dibirov <idsulik@gmail.com>
2025-03-25 07:00:24 +01:00
Nicolas De Loof
a0d1c3f944 introduce config --no-env-resolution
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-24 15:46:04 +01:00
Nicolas De Loof
0c5bd16da1 bake parses "${}" in DockerfileInline as a variable
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-24 11:36:42 +01:00
Remco Kranenburg
b0badf1eb0 Set watch option --prune=true as default
Signed-off-by: Remco Kranenburg <remco.kranenburg@crunchr.com>
2025-03-19 17:48:05 +01:00
Nicolas De Loof
342a2a9e71 Fix support for depends_on.restart in up and restart commands
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-19 15:29:50 +01:00
dependabot[bot]
7814e5798c build(deps): bump github.com/containerd/containerd/v2
Bumps [github.com/containerd/containerd/v2](https://github.com/containerd/containerd) from 2.0.3 to 2.0.4.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v2.0.3...v2.0.4)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-18 11:02:40 +01:00
dependabot[bot]
42b2e11094 build(deps): bump github.com/docker/buildx from 0.21.2 to 0.21.3
Bumps [github.com/docker/buildx](https://github.com/docker/buildx) from 0.21.2 to 0.21.3.
- [Release notes](https://github.com/docker/buildx/releases)
- [Commits](https://github.com/docker/buildx/compare/v0.21.2...v0.21.3)

---
updated-dependencies:
- dependency-name: github.com/docker/buildx
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-18 10:45:25 +01:00
Nicolas De Loof
6a8c0988cf run only loads required service env_file and ignores others
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-18 09:39:15 +01:00
Matiboux
9129abe516 Fix edge-case bug path prefix check for watch & bind mounts
Signed-off-by: Matiboux <matiboux@gmail.com>
2025-03-17 17:40:40 +01:00
Nicolas De Loof
f38f3f754c PWD
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-17 17:26:45 +01:00
Nicolas De Loof
ea07ba8e2a fix support for secret set by env inside included file
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-14 16:21:45 +01:00
Max Proske
432ae23b0e Test commandName subcommand order
Signed-off-by: Max Proske <max@mproske.com>
2025-03-14 10:00:45 +01:00
Guillaume Lours
b6f313b8a5 bump compose-go to version v2.4.9
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-03-13 16:54:22 +01:00
Guillaume Lours
13618756dc make publish a regular command of Compose
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-03-13 16:17:00 +01:00
Dominik Menke
6c1e21572a lint: address gofumpt issues
Signed-off-by: Dominik Menke <dom@digineo.de>
2025-03-13 14:23:51 +01:00
Dominik Menke
33e863ac6c fix linting issue
Signed-off-by: Dominik Menke <dom@digineo.de>
2025-03-13 14:23:51 +01:00
Dominik Menke
f70209cf15 review: move Summary/Replica collection from cmd/ to pkg/
Signed-off-by: Dominik Menke <dom@digineo.de>
2025-03-13 14:23:51 +01:00
Dominik Menke
62e832eb50 compose top: reduce tabwriter padding
Signed-off-by: Dominik Menke <dom@digineo.de>
2025-03-13 14:23:51 +01:00
Dominik Menke
80e8fda14f compose top: ensure CMD is right-most column
Signed-off-by: Dominik Menke <dom@digineo.de>
2025-03-13 14:23:51 +01:00
Dominik Menke
375a279785 top: expose container labels
Signed-off-by: Dominik Menke <dom@digineo.de>
2025-03-13 14:23:51 +01:00
Dominik Menke
a766e1669a condense output of compose top
This changes the output format of `compose top` and inlines the service
container name into the table.

Previously, `compose top` had printed something like:

  <service name>
  UID    PID   ...
  root   1     ...

Now, the output looks more like this:

  SERVICE   UID    PID   ...
  <name>    root   1     ...

Signed-off-by: Dominik Menke <dom@digineo.de>
2025-03-13 14:23:51 +01:00
Matt Landis
793c6f1715 add cli.isatty attribute to spans generated by compose
Signed-off-by: Matt Landis <matt.landis@docker.com>
2025-03-13 09:06:15 +01:00
dependabot[bot]
8e3e1f7f8b build(deps): bump tags.cncf.io/container-device-interface
Bumps [tags.cncf.io/container-device-interface](https://github.com/cncf-tags/container-device-interface) from 0.8.1 to 1.0.0.
- [Release notes](https://github.com/cncf-tags/container-device-interface/releases)
- [Changelog](https://github.com/cncf-tags/container-device-interface/blob/main/RELEASE.md)
- [Commits](https://github.com/cncf-tags/container-device-interface/compare/v0.8.1...v1.0.0)

---
updated-dependencies:
- dependency-name: tags.cncf.io/container-device-interface
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-12 15:50:14 +01:00
Nicolas De Loof
83cafe2838 Add support to pass env-from-file to docker compose run
Signed-off-by: Vedant Koditkar <vedant.koditkar@outlook.com>
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-12 09:04:39 +01:00
Guillaume Lours
55b5f233c2 use Defang secret-detector to identify potential secret leaks before publishing OCI artifacts
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-03-11 15:02:37 +01:00
Nicolas De Loof
c3a0c35681 implement extends.file replace without yqlib
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-11 14:18:41 +01:00
Nicolas De Loof
8615e9a7c1 deprecate --y, prefer --yes
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-11 09:01:36 +01:00
Nicolas De Loof
b23728941d only load env_file after services have been selected
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-10 08:32:03 +01:00
dependabot[bot]
1a7343bc88 build(deps): bump github.com/moby/buildkit from 0.20.0 to 0.20.1
Bumps [github.com/moby/buildkit](https://github.com/moby/buildkit) from 0.20.0 to 0.20.1.
- [Release notes](https://github.com/moby/buildkit/releases)
- [Commits](https://github.com/moby/buildkit/compare/v0.20.0...v0.20.1)

---
updated-dependencies:
- dependency-name: github.com/moby/buildkit
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-07 16:44:02 +01:00
Guillaume Lours
41e6094041 add warning message when a remote configuration include an another remote config
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-03-07 16:30:32 +01:00
Nicolas De Loof
66a47169d5 Publish compose file with required siblings used by extends
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-07 13:58:10 +01:00
dependabot[bot]
4c72d3a0e3 build(deps): bump golang.org/x/sys from 0.30.0 to 0.31.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.30.0 to 0.31.0.
- [Commits](https://github.com/golang/sys/compare/v0.30.0...v0.31.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-06 19:07:25 +01:00
dependabot[bot]
59f39b9990 build(deps): bump google.golang.org/grpc from 1.70.0 to 1.71.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.70.0 to 1.71.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.70.0...v1.71.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-06 18:55:59 +01:00
dependabot[bot]
7ab65ba127 build(deps): bump golang.org/x/sync from 0.11.0 to 0.12.0
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.11.0 to 0.12.0.
- [Commits](https://github.com/golang/sync/compare/v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-06 18:44:23 +01:00
Guillaume Lours
d9f05d72d2 improve message suggesting using bake
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-03-06 18:12:43 +01:00
Guillaume Lours
7b88c5b0ed display interpolation variables and their values when running a remote stack
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-03-06 09:46:37 +01:00
dependabot[bot]
eaf9800948 build(deps): bump github.com/opencontainers/image-spec
Bumps [github.com/opencontainers/image-spec](https://github.com/opencontainers/image-spec) from 1.1.0 to 1.1.1.
- [Release notes](https://github.com/opencontainers/image-spec/releases)
- [Changelog](https://github.com/opencontainers/image-spec/blob/main/RELEASES.md)
- [Commits](https://github.com/opencontainers/image-spec/compare/v1.1.0...v1.1.1)

---
updated-dependencies:
- dependency-name: github.com/opencontainers/image-spec
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-05 10:19:45 +01:00
Nicolas De Loof
4c2ecb542f reject compose file with bind mounts
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-04 16:10:53 +01:00
Nicolas De Loof
bcd000ab40 refuse to publish compose file with local include
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-04 15:55:17 +01:00
dependabot[bot]
8092ce9414 build(deps): bump github.com/docker/buildx from 0.21.1 to 0.21.2
Bumps [github.com/docker/buildx](https://github.com/docker/buildx) from 0.21.1 to 0.21.2.
- [Release notes](https://github.com/docker/buildx/releases)
- [Commits](https://github.com/docker/buildx/compare/v0.21.1...v0.21.2)

---
updated-dependencies:
- dependency-name: github.com/docker/buildx
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-04 10:44:04 +01:00
dependabot[bot]
97595066e3 build(deps): bump github.com/docker/docker
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 28.0.0+incompatible to 28.0.1+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v28.0.0...v28.0.1)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-04 07:52:24 +01:00
dependabot[bot]
508309414f build(deps): bump github.com/docker/cli
Bumps [github.com/docker/cli](https://github.com/docker/cli) from 28.0.0+incompatible to 28.0.1+incompatible.
- [Commits](https://github.com/docker/cli/compare/v28.0.0...v28.0.1)

---
updated-dependencies:
- dependency-name: github.com/docker/cli
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-04 07:52:00 +01:00
Guillaume Lours
b6c8a2b9fc display the location of OCI or GIT Compose stack download
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-03-03 22:09:06 +01:00
Nicolas De Loof
19571c2c81 e2e test for watch.include
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-03 21:59:26 +01:00
Nicolas De Loof
0ef7bbcddc introduce watch.include
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-03-03 21:59:26 +01:00
Guillaume Lours
66dfa7d181 block the publication of an OCI artifact if one or more services contain only a build section
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-03-03 16:23:21 +01:00
Max Proske
876ecc48be Test version command
Signed-off-by: Max Proske <max@mproske.com>
2025-02-26 16:35:08 +01:00
Nicolas De Loof
c7bf302c23 wrap builder execution within a project/build span
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-02-26 11:23:01 +01:00
Nicolas De Loof
7b3bdbe037 otel attribute to track builder implementation selected
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-02-26 11:23:01 +01:00
dependabot[bot]
094b48fd74 build(deps): bump github.com/google/go-cmp from 0.6.0 to 0.7.0
Bumps [github.com/google/go-cmp](https://github.com/google/go-cmp) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/google/go-cmp/releases)
- [Commits](https://github.com/google/go-cmp/compare/v0.6.0...v0.7.0)

---
updated-dependencies:
- dependency-name: github.com/google/go-cmp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-26 09:31:01 +01:00
dependabot[bot]
43c52e2a80 build(deps): bump tags.cncf.io/container-device-interface
Bumps [tags.cncf.io/container-device-interface](https://github.com/cncf-tags/container-device-interface) from 0.8.0 to 0.8.1.
- [Release notes](https://github.com/cncf-tags/container-device-interface/releases)
- [Commits](https://github.com/cncf-tags/container-device-interface/compare/v0.8.0...v0.8.1)

---
updated-dependencies:
- dependency-name: tags.cncf.io/container-device-interface
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-26 09:30:52 +01:00
Nicolas De Loof
6c1ee1069b support refresh pull policy
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-02-25 17:05:23 +01:00
Nicolas De Loof
e38b729a30 fix service: additional_contexts running internal buildkit client
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-02-25 09:47:02 +01:00
Andrew Kramer
145bb8466d Update yaml docs
Signed-off-by: Andrew Kramer <andrew.d.kramer.80@gmail.com>
2025-02-22 11:17:19 +01:00
Andrew Kramer
acac184135 Link to configuration file docs
Signed-off-by: Andrew Kramer <andrew.d.kramer.80@gmail.com>
2025-02-22 11:17:19 +01:00
Simon Ser
3292740c19 build: only print COMPOSE_BAKE recommendation when disabled
docker-compose now prints a recommendation to set COMPOSE_BAKE=true
when service deps are used. However, when the user opts-in to bake,
the recommendation is still printed.

Only print the recommendation when bake is disabled.

Signed-off-by: Simon Ser <contact@emersion.fr>
2025-02-22 11:14:36 +01:00
Nicolas De Loof
cae8e84636 require go 1.23|1.24 (stable)
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-02-21 16:54:16 +01:00
Nicolas De Loof
da2eff4ba7 add support for gw_priority, enable_ipv4 (requires docker v28.0)
This adds support for the GwPriority API field in API v1.48 (docker v28.0).
It can be set on both docker container run and docker network connect.

This option is used by the Engine to determine which network provides
the default gateway for a container.

It also adds support for enable_ipv4, which allows enabling (default)
or disabling IPv4 address assignment for a network.

Co-authored-by: Nicolas De Loof <nicolas.deloof@gmail.com>
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-21 15:54:41 +01:00
Sebastiaan van Stijn
20f780e95a make the mocks generator happy
Seems like it resolves the alias, and uses that instead, instead of
the actual type.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-21 15:23:25 +01:00
Sebastiaan van Stijn
cf2fc2005c go.mod: docker/docker, docker/cli v28.0.0, buildx v0.21.1
full diff:

- https://github.com/docker/docker/compare/v27.5.1...v28.0.0
- https://github.com/docker/cli/compare/v27.5.1...v28.0.0
- https://github.com/docker/buildx/compare/v0.20.1...v0.21.1

Co-authored-by: Nicolas De Loof <nicolas.deloof@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-21 15:23:25 +01:00
Sebastiaan van Stijn
d0398a4681 go.mod: github.com/moby/buildkit v0.20.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-21 15:23:25 +01:00
Sebastiaan van Stijn
ac40aae4c3 go.mod: golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-21 15:23:25 +01:00
Sebastiaan van Stijn
f25fea5e6d pkg/compose: stopDependentContainers: rename var that shadowed
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-21 15:23:25 +01:00
Nicolas De Loof
b27f56eb19 fix error message when detach is implied by wait
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-02-21 09:48:56 +01:00
Max Proske
4e593ed074 Fix pull --parallel and --no-parallel deprecation warnings missing
Signed-off-by: Max Proske <max@mproske.com>
2025-02-20 14:09:58 +01:00
Guillaume Lours
d956ff13da don't display bake suggestion when using --progress with quiet or json option
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-02-18 18:45:52 +01:00
Nicolas De Loof
5f7c9a2b4b report error using non-file secret|config with read-only service
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-02-18 11:33:13 +01:00
dependabot[bot]
fd0c23a1c9 build(deps): bump google.golang.org/grpc from 1.68.1 to 1.70.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.68.1 to 1.70.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.68.1...v1.70.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-18 11:04:32 +01:00
Nicolas De Loof
7aa64ae9cb run watch standalone if menu fails to start
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-02-18 10:41:23 +01:00
dependabot[bot]
c23eea9341 build(deps): bump github.com/spf13/cobra from 1.8.1 to 1.9.1
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.8.1 to 1.9.1.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.8.1...v1.9.1)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-17 20:08:53 +01:00
Sebastiaan van Stijn
036da47950 go.mod: remove toolchain directive
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-12 08:19:25 +01:00
Nicolas De Loof
33172d5e4d let user know bake is now supported
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-02-12 08:18:20 +01:00
dependabot[bot]
3f1a6b72a5 build(deps): bump gotest.tools/v3 from 3.5.1 to 3.5.2
Bumps [gotest.tools/v3](https://github.com/gotestyourself/gotest.tools) from 3.5.1 to 3.5.2.
- [Release notes](https://github.com/gotestyourself/gotest.tools/releases)
- [Commits](https://github.com/gotestyourself/gotest.tools/compare/v3.5.1...v3.5.2)

---
updated-dependencies:
- dependency-name: gotest.tools/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-11 10:53:38 +01:00
dependabot[bot]
f9a6e6c414 build(deps): bump golang.org/x/sync from 0.10.0 to 0.11.0
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.10.0 to 0.11.0.
- [Commits](https://github.com/golang/sync/compare/v0.10.0...v0.11.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-11 10:53:02 +01:00
dependabot[bot]
18ef6e592b build(deps): bump github.com/spf13/pflag from 1.0.5 to 1.0.6
Bumps [github.com/spf13/pflag](https://github.com/spf13/pflag) from 1.0.5 to 1.0.6.
- [Release notes](https://github.com/spf13/pflag/releases)
- [Commits](https://github.com/spf13/pflag/compare/v1.0.5...v1.0.6)

---
updated-dependencies:
- dependency-name: github.com/spf13/pflag
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-11 10:50:47 +01:00
Max Proske
2884d6df0d Fix ls --quiet help description
Signed-off-by: Max Proske <max@mproske.com>
2025-02-10 17:30:39 +01:00
Sebastiaan van Stijn
4459012a4f Dockerfile: update golangci-lint to v1.63.4
make sure it's compatible with go1.23

full diff: https://github.com/golangci/golangci-lint/compare/v1.60.2...v1.63.4
Changelog: https://golangci-lint.run/product/changelog/#v1634

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-10 17:03:36 +01:00
Sebastiaan van Stijn
6f1f76c0e6 pkg/e2e: fix contains: use assert.Contains (testifylint)
pkg/e2e/ps_test.go:50:5: contains: use assert.Contains (testifylint)
                    assert.True(t, strings.Contains(line, "127.0.0.1:8001->8000/tcp"))
                    ^
    pkg/e2e/ps_test.go:54:5: contains: use assert.Contains (testifylint)
                    assert.True(t, strings.Contains(line, "80/tcp, 443/tcp, 8080/tcp"))
                    ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-10 17:03:36 +01:00
Nicolas De Loof
ed72c21871 use BlockUntilContext
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-02-10 15:24:46 +01:00
dependabot[bot]
fa4cfb652e build(deps): bump github.com/jonboulle/clockwork from 0.4.0 to 0.5.0
Bumps [github.com/jonboulle/clockwork](https://github.com/jonboulle/clockwork) from 0.4.0 to 0.5.0.
- [Release notes](https://github.com/jonboulle/clockwork/releases)
- [Commits](https://github.com/jonboulle/clockwork/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: github.com/jonboulle/clockwork
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 15:24:46 +01:00
dependabot[bot]
200638b024 build(deps): bump github.com/otiai10/copy from 1.14.0 to 1.14.1
Bumps [github.com/otiai10/copy](https://github.com/otiai10/copy) from 1.14.0 to 1.14.1.
- [Release notes](https://github.com/otiai10/copy/releases)
- [Commits](https://github.com/otiai10/copy/compare/v1.14.0...v1.14.1)

---
updated-dependencies:
- dependency-name: github.com/otiai10/copy
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 15:04:10 +01:00
dependabot[bot]
a0320f12ef build(deps): bump golang.org/x/sys from 0.28.0 to 0.30.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.28.0 to 0.30.0.
- [Commits](https://github.com/golang/sys/compare/v0.28.0...v0.30.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 15:03:31 +01:00
Nicolas De Loof
f8a912ab9a bump compose-go v2.4.8
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-02-10 15:02:01 +01:00
Sebastiaan van Stijn
c23a7e7281 golangci-lint: enable copyloopvar linter
capturing loop variables is no longer needed in go1.22 and higher;
https://go.dev/blog/loopvar-preview

This path enables the copyloopvar linter, which finds places where capturing
is no longer needed, and removes locations where they could be removed.

Also made some minor changes, and renamed some vars in places where
we could use a shorter name that's less likely to conflict with imports.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-10 13:46:41 +01:00
Sebastiaan van Stijn
49575ef499 gha: add docker engine v28.x to the test-matrix
currently v28.0.0-rc.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-09 17:38:01 +01:00
Sebastiaan van Stijn
faa46d374d go.mod: github.com/moby/term v0.5.2
- update github.com/Azure/go-ansiterm to v0.0.0-20250102033503-faa5f7b0171c
  to fix OSC string terminator parsing.
- add security policy
- update github actions and test against go1.22, go1.23

full diff: https://github.com/moby/term/compare/v0.5.0...v0.5.2

update github.com/Azure/go-ansiterm faa5f7b0171c

- fix OSC string terminator parsing

diff: d185dfc1b5...faa5f7b017

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-09 17:37:45 +01:00
Max Proske
6ecb8d40ae Full test coverage for convert compatibility cmd
Signed-off-by: Max Proske <max@mproske.com>
2025-02-05 18:08:54 +01:00
Nicolas De Loof
a6a39422e4 remove cli.args opentelemetry attribute
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-02-05 16:29:45 +01:00
Max Proske
40cd08f318 Update link in stats --help output
Signed-off-by: Max Proske <max@mproske.com>
2025-02-04 18:12:17 +01:00
Nicolas De Loof
5e2abb6c26 support additional_context reference to another service
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-02-03 17:23:35 +01:00
Nicolas De Loof
4db5fcd569 fix bake uses selected builder
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-31 17:05:11 +01:00
Nicolas De Loof
f14c15fa57 capture error message reported by bake and forward to compose
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-31 17:05:11 +01:00
Nicolas De Loof
8d68ef587f fix exporter to only load image for default platform
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-31 17:05:11 +01:00
Nicolas De Loof
cde9ae5952 pass --allow for filesystem read access
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-31 17:05:11 +01:00
Guillaume Lours
806ac91cf6 add warning when trying to publish env variables with OCI artifact
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-01-30 17:37:07 +01:00
Nicolas De Loof
1c073c0a04 watch should ony build selected services
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-30 13:47:21 +01:00
Guillaume Lours
840288895e add --with-env flag to publish command
this flag allow publishing env variables in the Compose OCI artifact

Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-01-29 15:45:00 +01:00
Nicolas De Loof
4b70ff0ccd fix support for ssh key from CLI flags
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-29 11:28:41 +01:00
Nicolas De Loof
23351ece81 remove exit code per error type used by legacy metrics system
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-28 10:26:41 +01:00
Nicolas De Loof
7c7407672a bump docker to v27.5.1
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-23 15:31:01 +01:00
Nicolas De Loof
25cfa66a91 bump buildx v0.20.1 and docker v27.5.0
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-23 09:13:37 +01:00
CrazyMax
f160333e9e ci: use main branch for docs upstream validation workflow
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-01-22 19:27:02 +01:00
Nicolas De Loof
d04b3f48e4 e2e test covering multi-service rebuild with common resources
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-22 12:05:58 +01:00
Nicolas De Loof
ed10804e0f manage watch applied to mulitple services
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-22 12:05:58 +01:00
Rafael Buchbinder
52578c0998 Properly handle "builtin" seccomp profile
Like in CLI [1] the "builtin" seccomp profile should be handled the same
as "unconfined".

[1] f4a68da195/cli/command/container/opts.go (L929)

Signed-off-by: Rafael Buchbinder <rafi@rbk.io>
2025-01-20 15:06:09 +01:00
Guillaume Lours
bd2b49a1cf add codeowners file
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-01-20 14:09:53 +01:00
CrazyMax
433a60e122 ci: fix provenance for binaries and generate sbom
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-01-20 09:46:05 +01:00
Nicolas De Loof
489fe9cf02 add support for BUILDKIT_PROGRESS
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-16 12:06:09 +01:00
Guillaume Lours
ef1931c8de add missing tag for build during merge workflow
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-01-15 18:26:03 +01:00
Guillaume Lours
9be7a3c9a1 ci: re-use local source to build binary images
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-01-15 18:02:23 +01:00
CrazyMax
666996bee2 ci: use local source for binary builds
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-01-15 16:50:20 +01:00
CrazyMax
083f676214 ci: update bake-action to v6
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-01-13 12:41:14 +01:00
Nicolas De Loof
e81de103db simplification
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-10 12:21:13 +01:00
Nicolas De Loof
fa39503469 image can be set to a local ID, that isn't a valid docker ref
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-09 09:55:30 +01:00
Nicolas De Loof
a351585024 can't render progress concurrently with buildkit
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-08 16:12:54 +01:00
Nicolas De Loof
b6db1380ec exclude one-off container running convergence
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-08 10:57:46 +01:00
Florian Apolloner
2ebb475433 Only override service mac if set on the main network.
Signed-off-by: Florian Apolloner <florian@apolloner.eu>
2025-01-07 16:16:33 +01:00
Guillaume Lours
d474515d45 remove engine v25 from e2e test matrix
The 1st version available for Ubuntu 24.x is Docker Engine v26

Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-01-07 14:59:47 +01:00
Nicolas De Loof
2b21c5df93 fix relative path in compose file
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-07 14:59:47 +01:00
Guillaume Lours
1f3c10eb4b bump compose-go to v2.4.7
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-01-07 14:59:47 +01:00
Guillaume Lours
68ad165a59 replace tibdex/github-app-token by official GitHub create-github-app-token
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-01-07 12:12:22 +01:00
Guillaume Lours
3060ed2795 bump golang.org/x/net to v0.33.0 to fix potential security issue
https://github.com/golang/go/issues/70906

Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-01-07 12:11:44 +01:00
Nicolas De Loof
be09b2e8ce checkExpectedVolumes must ignore anonymous volumes
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-01-06 09:40:02 +01:00
Guillaume Tardif
571a1af013 When retrying to resolveOrCreateNetwork, retry with a valid network name
Signed-off-by: Guillaume Tardif <guillaume.tardif@gmail.com>
2025-01-06 08:28:05 +01:00
Nicolas De Loof
8f644eea73 only check bind mount conflict if sync action is involved
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-19 11:23:50 +01:00
Guillaume Lours
56e92e34b6 use the 3 latest major versions of the engine to run e2e step
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2024-12-18 16:29:41 +01:00
Guillaume Lours
a42a04dfe8 bump Golang version to v1.22.10 and update CI actions
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2024-12-18 16:09:48 +01:00
Guillaume Lours
34bcd03a76 add --pull to run command
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2024-12-18 12:20:02 +01:00
Nicolas De Loof
ed61e42f93 CI to validate fmt
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-17 16:50:14 +01:00
Nicolas De Loof
65696bb1cb make fmt so any contributor can enforce formatting
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-17 16:50:14 +01:00
Sebastiaan van Stijn
446e00520c format code with gofumpt
Format the code  with gofumpt to prevent my IDE from reformatting
every time I open a file. gofumpt provides a superset of gofmt,
so should not impact users that are not using gofumpt.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-12-17 16:50:14 +01:00
Nicolas De Loof
c01c9c29f4 e2e test to prevent future regression
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-16 15:46:47 +01:00
Nicolas De Loof
038c81f34e only check volume mounts for updated config
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-16 10:26:24 +01:00
Nicolas De Loof
a20b69ac5b e2e test for recreate volume
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-13 09:52:22 +01:00
dependabot[bot]
977530c22a build(deps): bump google.golang.org/grpc from 1.68.0 to 1.68.1
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.68.0 to 1.68.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.68.0...v1.68.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-12 19:25:02 +01:00
dependabot[bot]
d4db8b6b12 build(deps): bump golang.org/x/crypto from 0.27.0 to 0.31.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.27.0 to 0.31.0.
- [Commits](https://github.com/golang/crypto/compare/v0.27.0...v0.31.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-12 19:12:27 +01:00
dependabot[bot]
f8ce0f04e6 build(deps): bump golang.org/x/sys from 0.27.0 to 0.28.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.27.0 to 0.28.0.
- [Commits](https://github.com/golang/sys/compare/v0.27.0...v0.28.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-12 19:11:45 +01:00
Nicolas De Loof
8e0520e71e prompt user to confirm volume recreation
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-12 19:11:00 +01:00
Joana Hrotko
332311358e Recreate container on volume configuration change
Signed-off-by: Joana Hrotko <joana.hrotko@docker.com>
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-12 19:11:00 +01:00
Nicolas De Loof
df9e420ddd introduce watch restart action
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-12 09:56:23 +01:00
Guillaume Lours
142f5dba84 bump otel dependencies to v1.28.0 and v0.53.0 to align with buildx, buildkit and engine versions
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2024-12-11 15:57:53 +01:00
Nicolas De Loof
700c586bcf bump docker/buildx to latest release
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-11 15:33:58 +01:00
Nicolas De Loof
fc566509d5 fix support for service.mac_address
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-10 15:59:26 +01:00
Sebastiaan van Stijn
e73c2303fb update xx to v1.6.1 for compatibility with alpine 3.21 and file 5.46+
This fixes compatibility with alpine 3.21 and file 5.46+

- Fix additional possible `xx-cc`/`xx-cargo` compatibility issue with Alpine 3.21
- Support for Alpine 3.21
- Fix `xx-verify` with `file` 5.46+
- Fix possible error taking lock in `xx-apk` in latest Alpine without `coreutils`

full diff: https://github.com/tonistiigi/xx/compare/v1.2.1...v1.6.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-12-10 15:09:02 +01:00
dependabot[bot]
624303233a build(deps): bump golang.org/x/sync from 0.9.0 to 0.10.0
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.9.0 to 0.10.0.
- [Commits](https://github.com/golang/sync/compare/v0.9.0...v0.10.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-09 12:05:24 +01:00
Nicolas De loof
a1729c52de Update pkg/e2e/watch_test.go
Co-authored-by: Guillaume Lours <705411+glours@users.noreply.github.com>
Signed-off-by: Nicolas De loof <nicolas.deloof@gmail.com>
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-06 16:46:42 +01:00
Nicolas De Loof
254224c182 first watch action for a file event wins
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-06 16:46:42 +01:00
Nicolas De Loof
0861e68450 fix
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-06 16:46:42 +01:00
Nicolas De Loof
af5b748500 revisit TestDebounceBatching
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-06 16:46:42 +01:00
Nicolas De Loof
32a22c1f4f introduce sync+exec watch action
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-06 16:46:42 +01:00
Nicolas De Loof
e6ea8fb962 log configuration error as a watch log event
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-06 16:30:14 +01:00
Nicolas De Loof
043465448f do not require a build section but for rebuild action
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-06 16:12:05 +01:00
Nicolas De Loof
1d08390864 pull --quiet should not drop status message, only progress
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-06 12:24:29 +01:00
Nicolas De Loof
69a83d1303 use latest engine tags
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-05 14:53:24 +01:00
Nicolas De Loof
781b9f1cd5 Bump buildx to 0.19.1
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-05 14:53:24 +01:00
Guillaume Lours
cbff0e5559 be sure everything has been cleanup at the end of each tests
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2024-12-04 15:28:16 +01:00
Guillaume Lours
e4222bff53 add local config.json to test configuration dir if exists
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2024-12-04 15:28:16 +01:00
Nicolas De Loof
25197fe6dd disable failing TestBuildSSH test
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-04 15:14:56 +01:00
Nicolas De Loof
85cdaf9ddf fix build with bake
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-12-03 12:05:40 +01:00
Guillaume Lours
a8469db83f bump containerd to v1.7,24
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2024-11-27 15:23:27 +01:00
Guillaume Lours
08488dae59 bump google.golang.org/grpc to v1.68.0
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2024-11-27 15:23:27 +01:00
dependabot[bot]
cc3a216f2f build(deps): bump github.com/moby/buildkit from 0.17.1 to 0.17.2
Bumps [github.com/moby/buildkit](https://github.com/moby/buildkit) from 0.17.1 to 0.17.2.
- [Release notes](https://github.com/moby/buildkit/releases)
- [Commits](https://github.com/moby/buildkit/compare/v0.17.1...v0.17.2)

---
updated-dependencies:
- dependency-name: github.com/moby/buildkit
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-27 14:37:20 +01:00
dependabot[bot]
6e818b9ae0 build(deps): bump github.com/compose-spec/compose-go/v2
Bumps [github.com/compose-spec/compose-go/v2](https://github.com/compose-spec/compose-go) from 2.4.5-0.20241111154218-9d02caaf8465 to 2.4.5.
- [Release notes](https://github.com/compose-spec/compose-go/releases)
- [Commits](https://github.com/compose-spec/compose-go/commits/v2.4.5)

---
updated-dependencies:
- dependency-name: github.com/compose-spec/compose-go/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-27 14:21:24 +01:00
Nicolas De Loof
6b3e57503e only stop dependent containers ... if there's some
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-11-27 09:58:38 +01:00
Nicolas De Loof
8e497a1289 disable TestNetworkConfigChanged which is unstable on CI
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-11-27 09:51:07 +01:00
Nicolas De Loof
5aed704379 only check attached networks on running containers
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-11-27 09:51:07 +01:00
MohammadHasan Akbari
1ff9b758d2 fix: commit tests
Signed-off-by: MohammadHasan Akbari <jarqvi.jarqvi@gmail.com>
2024-11-27 07:39:46 +01:00
MohammadHasan Akbari
9eaba55973 feat: add commit command
Signed-off-by: MohammadHasan Akbari <jarqvi.jarqvi@gmail.com>
2024-11-27 07:39:46 +01:00
Nicolas De Loof
a85f8a40a9 run build tests against bake
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-11-26 15:20:50 +01:00
Nicolas De Loof
095f65cb42 delegate build to buildx bake
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-11-26 08:51:34 +01:00
dependabot[bot]
208e57ded8 build(deps): bump github.com/stretchr/testify from 1.9.0 to 1.10.0
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.9.0 to 1.10.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.9.0...v1.10.0)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-26 08:44:16 +01:00
Nicolas De Loof
2d148faedf use service.stop to stop dependent containers
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-11-25 11:56:22 +01:00
Trevor Foster
43ac1e31c6 Update wait-timeout flag usage to include the unit
Signed-off-by: Trevor Foster <trevorfoster19@gmail.com>
2024-11-25 11:22:18 +01:00
Sebastiaan van Stijn
5561a778c9 go.mod: github.com/docker/cli v27.4.0-rc.2
full diff: https://github.com/docker/cli/compare/8d1bacae3e49...v27.4.0-rc.2

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-20 09:55:29 +01:00
Sebastiaan van Stijn
ae48f488d1 go.mod: github.com/docker/docker v27.4.0-rc.2
full diff: https://github.com/docker/docker/compare/v27.4.0-rc.1...v27.4.0-rc.2

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-20 09:55:29 +01:00
Sebastiaan van Stijn
5e3a095380 go.mod: github.com/docker/cli 8d1bacae3e49 (v27.4.0-rc.2-dev)
full diff: https://github.com/docker/cli/compare/v27.4.0-rc.1...8d1bacae3e49ed1d096eede8eef4ae851d7f2eae

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-15 16:30:26 +01:00
Sebastiaan van Stijn
a2a3eb72e2 go.mod: github.com/docker/cli v27.4.0-rc.1
- full diff: https://github.com/docker/cli/compare/cb3048fbebb1...v27.4.0-rc.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-15 16:30:26 +01:00
Sebastiaan van Stijn
3513b42423 go.mod: github.com/docker/docker v27.4.0-rc.1
- full diff: https://github.com/docker/docker/compare/v27.3.1...v27.4.0-rc.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-15 16:30:26 +01:00
Nicolas De loof
d4fa63fdcb Update pkg/compose/convergence.go
Co-authored-by: Guillaume Lours <705411+glours@users.noreply.github.com>
Signed-off-by: Nicolas De loof <nicolas.deloof@gmail.com>
2024-11-15 15:35:49 +01:00
Nicolas De Loof
c21d4cfb40 detect network config changes and recreate if needed
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-11-15 15:35:49 +01:00
Sebastiaan van Stijn
61f1d4f69b go.mod: github.com/docker/buildx v0.18.0
full diff: https://github.com/docker/buildx/compare/v0.17.1..v0.18.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-15 11:57:00 +01:00
Sebastiaan van Stijn
f7cce281de go.mod: github.com/moby/buildkit v0.17.1
full diff: https://github.com/moby/buildkit/compare/v0.16.0..v0.17.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-15 11:57:00 +01:00
Sebastiaan van Stijn
bcaacc7f23 gha: test against docker engine v27.4.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-15 11:48:29 +01:00
Guillaume Lours
3f5898f8d0 push empty descriptor layer when using OCI version 1.1 for Compose artifact
it fixes a repository creation issue when pushing the 1st time a Compose OCI artifact on the Hub

Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2024-11-13 13:36:31 +01:00
Guillaume Lours
2bb67f2700 remove ddev e2e tests
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2024-11-13 12:52:48 +01:00
Nicolas De Loof
bf521fe3ab implement remove-orphans on run
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-11-12 14:28:26 +01:00
Matthieu MOREL
11e9621da5 ci: enable testifylint linter
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2024-11-12 11:12:32 +01:00
Felix Fontein
a9de9abcfb Emit events for building images
Signed-off-by: Felix Fontein <felix@fontein.de>
2024-11-12 10:59:41 +01:00
koooge
799ab842a0 Fix compose images that reutn a different image with the same ID
Signed-off-by: koooge <koooooge@gmail.com>
2024-11-12 09:51:33 +01:00
Nicolas De Loof
2f65ace2aa remove obsolete containers first on scale down
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2024-11-12 09:47:36 +01:00
Guillaume Lours
aa0a4189ee pass stal bot inactivity limit from 6 to 3 months
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2024-11-12 09:37:21 +01:00
Suleiman Dibirov
eba3ff8f37 fix(config): Print service names with --no-interpolate
Signed-off-by: Suleiman Dibirov <idsulik@gmail.com>
2024-11-11 16:42:55 +01:00
dependabot[bot]
6313365ba4 build(deps): bump golang.org/x/sys from 0.26.0 to 0.27.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.26.0 to 0.27.0.
- [Commits](https://github.com/golang/sys/compare/v0.26.0...v0.27.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-08 11:20:24 +01:00
dependabot[bot]
dbd51745c4 build(deps): bump golang.org/x/sync from 0.8.0 to 0.9.0
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.8.0 to 0.9.0.
- [Commits](https://github.com/golang/sync/compare/v0.8.0...v0.9.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-08 10:49:59 +01:00
225 changed files with 6438 additions and 2304 deletions

2
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1,2 @@
# global rules
* @docker/compose-maintainers

2
.github/stale.yml vendored
View File

@ -1,7 +1,7 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 180
daysUntilStale: 90
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.

View File

@ -56,7 +56,7 @@ jobs:
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
-
name: Run
run: |
@ -71,29 +71,45 @@ jobs:
matrix:
platform: ${{ fromJson(needs.prepare.outputs.matrix) }}
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Prepare
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
-
name: Build
uses: docker/bake-action@v2
uses: docker/bake-action@v6
with:
source: .
targets: release
provenance: mode=max
sbom: true
set: |
*.platform=${{ matrix.platform }}
*.cache-from=type=gha,scope=binary-${{ env.PLATFORM_PAIR }}
*.cache-to=type=gha,scope=binary-${{ env.PLATFORM_PAIR }},mode=max
-
name: Rename provenance and sbom
working-directory: ./bin/release
run: |
binname=$(find . -name 'docker-compose-*')
filename=$(basename "$binname" | sed -E 's/\.exe$//')
mv "provenance.json" "${filename}.provenance.json"
mv "sbom-binary.spdx.json" "${filename}.sbom.json"
find . -name 'sbom*.json' -exec rm {} \;
-
name: List artifacts
run: |
tree -nh ./bin/release
-
name: Upload artifacts
uses: actions/upload-artifact@v4
@ -105,15 +121,12 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
-
name: Test
uses: docker/bake-action@v2
uses: docker/bake-action@v6
with:
targets: test
set: |
@ -141,19 +154,19 @@ jobs:
- plugin
- standalone
engine:
- 24.0.9
- 25.0.5
- 26.1.4
- 27.3.0
- 26
- 27
- 28
steps:
- name: Prepare
run: |
mode=${{ matrix.mode }}
engine=${{ matrix.engine }}
echo "MODE_ENGINE_PAIR=${mode}-${engine}" >> $GITHUB_ENV
-
name: Checkout
- name: Checkout
uses: actions/checkout@v4
- name: Install Docker ${{ matrix.engine }}
run: |
sudo systemctl stop docker.service
@ -161,22 +174,24 @@ jobs:
sudo apt-get install curl
curl -fsSL https://test.docker.com -o get-docker.sh
sudo sh ./get-docker.sh --version ${{ matrix.engine }}
- name: Check Docker Version
run: docker --version
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Set up Go
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
check-latest: true
cache: true
-
name: Build
uses: docker/bake-action@v2
- name: Build
uses: docker/bake-action@v6
with:
source: .
targets: binary-with-coverage
set: |
*.cache-from=type=gha,scope=binary-linux-amd64
@ -184,37 +199,37 @@ jobs:
*.cache-to=type=gha,scope=binary-e2e-${{ matrix.mode }},mode=max
env:
BUILD_TAGS: e2e
-
name: Setup tmate session
- name: Setup tmate session
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled }}
uses: mxschmitt/action-tmate@8b4e4ac71822ed7e0ad5fb3d1c33483e9e8fb270 # v3.11
with:
limit-access-to-actor: true
github-token: ${{ secrets.GITHUB_TOKEN }}
-
name: Test plugin mode
- name: Test plugin mode
if: ${{ matrix.mode == 'plugin' }}
run: |
rm -rf ./bin/coverage/e2e
mkdir -p ./bin/coverage/e2e
make e2e-compose GOCOVERDIR=bin/coverage/e2e TEST_FLAGS="-v"
-
name: Gather coverage data
- name: Gather coverage data
if: ${{ matrix.mode == 'plugin' }}
uses: actions/upload-artifact@v4
with:
name: coverage-data-e2e-${{ env.MODE_ENGINE_PAIR }}
path: bin/coverage/e2e/
if-no-files-found: error
-
name: Test standalone mode
- name: Test standalone mode
if: ${{ matrix.mode == 'standalone' }}
run: |
rm -f /usr/local/bin/docker-compose
cp bin/build/docker-compose /usr/local/bin
make e2e-compose-standalone
-
name: e2e Test Summary
- name: e2e Test Summary
uses: test-summary/action@v2
with:
paths: /tmp/report/report.xml
@ -284,7 +299,11 @@ jobs:
find . -type f -print0 | sort -z | xargs -r0 shasum -a 256 -b | sed 's# \*\./# *#' > $RUNNER_TEMP/checksums.txt
shasum -a 256 -U -c $RUNNER_TEMP/checksums.txt
mv $RUNNER_TEMP/checksums.txt .
cat checksums.txt | while read sum file; do echo "$sum $file" > ${file#\*}.sha256; done
cat checksums.txt | while read sum file; do
if [[ "${file#\*}" == docker-compose-* && "${file#\*}" != *.provenance.json && "${file#\*}" != *.sbom.json ]]; then
echo "$sum $file" > ${file#\*}.sha256
fi
done
-
name: License
run: cp packaging/* ./bin/release/

View File

@ -44,7 +44,7 @@ jobs:
retention-days: 1
validate:
uses: docker/docs/.github/workflows/validate-upstream.yml@919a9b9104a34a40b30d116529bcce589a544d1c # pin for artifact v4 support: https://github.com/docker/docs/pull/19220
uses: docker/docs/.github/workflows/validate-upstream.yml@main
needs:
- docs-yaml
with:

View File

@ -82,16 +82,23 @@ jobs:
-
name: Checkout
uses: actions/checkout@v4
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERPUBLICBOT_USERNAME }}
password: ${{ secrets.DOCKERPUBLICBOT_WRITE_PAT }}
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
-
name: Docker meta
id: meta
uses: docker/metadata-action@v4
uses: docker/metadata-action@v5
with:
images: |
${{ env.REPO_SLUG }}
@ -99,28 +106,22 @@ jobs:
type=ref,event=tag
type=edge
bake-target: meta-helper
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERPUBLICBOT_USERNAME }}
password: ${{ secrets.DOCKERPUBLICBOT_WRITE_PAT }}
-
name: Build and push image
uses: docker/bake-action@v2
uses: docker/bake-action@v6
id: bake
with:
source: .
files: |
./docker-bake.hcl
${{ steps.meta.outputs.bake-file }}
targets: image-cross
push: ${{ github.event_name != 'pull_request' }}
sbom: true
provenance: mode=max
set: |
*.cache-from=type=gha,scope=bin-image
*.cache-to=type=gha,scope=bin-image,mode=max
*.attest=type=sbom
*.attest=type=provenance,mode=max,builder-id=https://github.com/${{ env.GITHUB_REPOSITORY }}/actions/runs/${{ env.GITHUB_RUN_ID }}
desktop-edge-test:
runs-on: ubuntu-latest
@ -129,14 +130,16 @@ jobs:
-
name: Generate Token
id: generate_token
uses: tibdex/github-app-token@v1
uses: actions/create-github-app-token@v1
with:
app_id: ${{ vars.DOCKERDESKTOP_APP_ID }}
private_key: ${{ secrets.DOCKERDESKTOP_APP_PRIVATEKEY }}
repository: docker/${{ secrets.DOCKERDESKTOP_REPO }}
app-id: ${{ vars.DOCKERDESKTOP_APP_ID }}
private-key: ${{ secrets.DOCKERDESKTOP_APP_PRIVATEKEY }}
owner: docker
repositories: |
${{ secrets.DOCKERDESKTOP_REPO }}
-
name: Trigger Docker Desktop e2e with edge version
uses: actions/github-script@v6
uses: actions/github-script@v7
with:
github-token: ${{ steps.generate_token.outputs.token }}
script: |

View File

@ -22,12 +22,12 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # tag=v3.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # tag=v4.4.2
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@99c53751e09b9529366343771cc321ec74e9bd3d # tag=v2.0.6
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # tag=v2.4.0
with:
results_file: results.sarif
results_format: sarif
@ -41,7 +41,7 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # tag=v3.0.0
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # tag=v4.5.0
with:
name: SARIF file
path: results.sarif
@ -49,6 +49,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # tag=v1.0.26
uses: github/codeql-action/upload-sarif@3096afedf9873361b2b2f65e1445b13272c83eb8 # tag=v2.20.00
with:
sarif_file: results.sarif

View File

@ -1,73 +1,83 @@
version: "2"
run:
concurrency: 2
timeout: 10m
linters:
enable-all: false
disable-all: true
default: none
enable:
- copyloopvar
- depguard
- errcheck
- errorlint
- gocritic
- gocyclo
- gofmt
- goimports
- gomodguard
- revive
- gosimple
- govet
- ineffassign
- lll
- misspell
- nakedret
- nolintlint
- revive
- staticcheck
- typecheck
- testifylint
- unconvert
- unparam
- unused
linters-settings:
revive:
rules:
- name: package-comments
disabled: true
depguard:
rules:
all:
deny:
- pkg: io/ioutil
desc: 'io/ioutil package has been deprecated'
- pkg: gopkg.in/yaml.v2
desc: 'compose-go uses yaml.v3'
gomodguard:
blocked:
modules:
- github.com/pkg/errors:
recommendations:
- errors
- fmt
versions:
- github.com/distribution/distribution:
reason: "use distribution/reference"
- gotest.tools:
version: "< 3.0.0"
reason: "deprecated, pre-modules version"
gocritic:
# Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.
# Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
enabled-tags:
- diagnostic
- opinionated
- style
disabled-checks:
- paramTypeCombine
- unnamedResult
- whyNoLint
gocyclo:
min-complexity: 16
lll:
line-length: 200
settings:
depguard:
rules:
all:
deny:
- pkg: io/ioutil
desc: io/ioutil package has been deprecated
- pkg: gopkg.in/yaml.v2
desc: compose-go uses yaml.v3
gocritic:
disabled-checks:
- paramTypeCombine
- unnamedResult
- whyNoLint
enabled-tags:
- diagnostic
- opinionated
- style
gocyclo:
min-complexity: 16
gomodguard:
blocked:
modules:
- github.com/pkg/errors:
recommendations:
- errors
- fmt
versions:
- github.com/distribution/distribution:
reason: use distribution/reference
- gotest.tools:
version: < 3.0.0
reason: deprecated, pre-modules version
lll:
line-length: 200
revive:
rules:
- name: package-comments
disabled: true
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
issues:
# golangci hides some golint warnings (the warning about exported things
# without documentation for example), this will make it show them anyway.
exclude-use-default: false
max-issues-per-linter: 0
max-same-issues: 0
formatters:
enable:
- gofumpt
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$

View File

@ -2,10 +2,10 @@
### Prerequisites
* Windows:
* [Docker Desktop](https://hub.docker.com/editions/community/docker-ce-desktop-windows)
* [Docker Desktop](https://docs.docker.com/desktop/setup/install/windows-install/)
* make
* macOS:
* [Docker Desktop](https://hub.docker.com/editions/community/docker-ce-desktop-mac)
* [Docker Desktop](https://docs.docker.com/desktop/setup/install/mac-install/)
* make
* Linux:
* [Docker 20.10 or later](https://docs.docker.com/engine/install/)

View File

@ -2,7 +2,7 @@
Want to hack on Docker? Awesome! We have a contributor's guide that explains
[setting up a Docker development environment and the contribution
process](https://docs.docker.com/contribute/overview/).
process](https://docs.docker.com/contribute/).
This page contains information about reporting issues as well as some tips and
guidelines useful to experienced open source contributors. Finally, make sure
@ -95,7 +95,7 @@ don't get discouraged!
<tr>
<td>Community Slack</td>
<td>
The Docker Community has a dedicated Slack chat to discuss features and issues. You can sign-up <a href="https://www.docker.com/docker-community" target="_blank">with this link</a>.
The Docker Community has a dedicated Slack chat to discuss features and issues. You can sign-up <a href="https://www.docker.com/community/" target="_blank">with this link</a>.
</td>
</tr>
<tr>
@ -118,7 +118,7 @@ don't get discouraged!
<td>Stack Overflow</td>
<td>
Stack Overflow has over 17000 Docker questions listed. We regularly
monitor <a href="https://stackoverflow.com/search?tab=newest&q=docker" target="_blank">Docker questions</a>
monitor <a href="https://stackoverflow.com/questions/tagged/docker" target="_blank">Docker questions</a>
and so do many other knowledgeable Docker users.
</td>
</tr>
@ -200,7 +200,7 @@ For more details, see the [MAINTAINERS](MAINTAINERS) page.
The sign-off is a simple line at the end of the explanation for the patch. Your
signature certifies that you wrote the patch or otherwise have the right to pass
it on as an open-source patch. The rules are pretty simple: if you can certify
the below (from [developercertificate.org](http://developercertificate.org/)):
the below (from [developercertificate.org](https://developercertificate.org/)):
```
Developer Certificate of Origin
@ -252,7 +252,7 @@ commit automatically with `git commit -s`.
### How can I become a maintainer?
The procedures for adding new maintainers are explained in the global
[MAINTAINERS](https://github.com/docker/opensource/blob/master/MAINTAINERS)
[MAINTAINERS](https://github.com/docker/opensource/blob/main/MAINTAINERS)
file in the
[https://github.com/docker/opensource/](https://github.com/docker/opensource/)
repository.
@ -311,8 +311,8 @@ The rules:
2. All code should pass the default levels of
[`golint`](https://github.com/golang/lint).
3. All code should follow the guidelines covered in [Effective
Go](http://golang.org/doc/effective_go.html) and [Go Code Review
Comments](https://github.com/golang/go/wiki/CodeReviewComments).
Go](https://go.dev/doc/effective_go) and [Go Code Review
Comments](https://go.dev/wiki/CodeReviewComments).
4. Include code comments. Tell us the why, the history and the context.
5. Document _all_ declarations and methods, even private ones. Declare
expectations, caveats and anything else that may be important. If a type
@ -334,6 +334,6 @@ The rules:
guidelines. Since you've read all the rules, you now know that.
If you are having trouble getting into the mood of idiomatic Go, we recommend
reading through [Effective Go](https://golang.org/doc/effective_go.html). The
[Go Blog](https://blog.golang.org) is also a great resource. Drinking the
reading through [Effective Go](https://go.dev/doc/effective_go). The
[Go Blog](https://go.dev/blog/) is also a great resource. Drinking the
kool-aid is a lot easier than going thirsty.

View File

@ -15,9 +15,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
ARG GO_VERSION=1.22.8
ARG XX_VERSION=1.2.1
ARG GOLANGCI_LINT_VERSION=v1.60.2
ARG GO_VERSION=1.23.8
ARG XX_VERSION=1.6.1
ARG GOLANGCI_LINT_VERSION=v2.0.2
ARG ADDLICENSE_VERSION=v1.0.0
ARG BUILD_TAGS="e2e"

View File

@ -116,6 +116,11 @@ cache-clear: ## Clear the builder cache
lint: ## run linter(s)
$(BUILDX_CMD) bake lint
.PHONY: fmt
fmt:
gofumpt --version >/dev/null 2>&1 || go install mvdan.cc/gofumpt@latest
gofumpt -w .
.PHONY: docs
docs: ## generate documentation
$(eval $@_TMP_OUT := $(shell mktemp -d -t compose-output.XXXXXXXXXX))

View File

@ -8,7 +8,7 @@
- [Legacy](#legacy)
# Docker Compose v2
[![GitHub release](https://img.shields.io/github/release/docker/compose.svg?style=flat-square)](https://github.com/docker/compose/releases/latest)
[![GitHub release](https://img.shields.io/github/v/release/docker/compose.svg?style=flat-square)](https://github.com/docker/compose/releases/latest)
[![PkgGoDev](https://img.shields.io/badge/go.dev-docs-007d9c?style=flat-square&logo=go&logoColor=white)](https://pkg.go.dev/github.com/docker/compose/v2)
[![Build Status](https://img.shields.io/github/actions/workflow/status/docker/compose/ci.yml?label=ci&logo=github&style=flat-square)](https://github.com/docker/compose/actions?query=workflow%3Aci)
[![Go Report Card](https://goreportcard.com/badge/github.com/docker/compose/v2?style=flat-square)](https://goreportcard.com/report/github.com/docker/compose/v2)
@ -28,7 +28,7 @@ single command: `docker compose up`.
### Windows and macOS
Docker Compose is included in
[Docker Desktop](https://www.docker.com/products/docker-desktop)
[Docker Desktop](https://www.docker.com/products/docker-desktop/)
for Windows and macOS.
### Linux

View File

@ -55,8 +55,8 @@ func Setup(cmd *cobra.Command, dockerCli command.Cli, args []string) error {
ctx,
"cli/"+strings.Join(commandName(cmd), "-"),
)
cmdSpan.SetAttributes(attribute.StringSlice("cli.args", args))
cmdSpan.SetAttributes(attribute.StringSlice("cli.flags", getFlags(cmd.Flags())))
cmdSpan.SetAttributes(attribute.Bool("cli.isatty", dockerCli.In().IsTerminal()))
cmd.SetContext(ctx)
wrapRunE(cmd, cmdSpan, tracingShutdown)
@ -115,13 +115,14 @@ func wrapRunE(c *cobra.Command, cmdSpan trace.Span, tracingShutdown tracing.Shut
}
}
// commandName returns the path components for a given command.
// commandName returns the path components for a given command,
// in reverse alphabetical order for consistent usage metrics.
//
// The root Compose command and anything before (i.e. "docker")
// are not included.
//
// For example:
// - docker compose alpha watch -> [alpha, watch]
// - docker compose alpha watch -> [watch, alpha]
// - docker-compose up -> [up]
func commandName(cmd *cobra.Command) []string {
var name []string

View File

@ -20,6 +20,8 @@ import (
"reflect"
"testing"
commands "github.com/docker/compose/v2/cmd/compose"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
)
@ -60,5 +62,51 @@ func TestGetFlags(t *testing.T) {
}
})
}
}
func TestCommandName(t *testing.T) {
tests := []struct {
name string
setupCmd func() *cobra.Command
want []string
}{
{
name: "docker compose alpha watch -> [watch, alpha]",
setupCmd: func() *cobra.Command {
dockerCmd := &cobra.Command{Use: "docker"}
composeCmd := &cobra.Command{Use: commands.PluginName}
alphaCmd := &cobra.Command{Use: "alpha"}
watchCmd := &cobra.Command{Use: "watch"}
dockerCmd.AddCommand(composeCmd)
composeCmd.AddCommand(alphaCmd)
alphaCmd.AddCommand(watchCmd)
return watchCmd
},
want: []string{"watch", "alpha"},
},
{
name: "docker-compose up -> [up]",
setupCmd: func() *cobra.Command {
dockerComposeCmd := &cobra.Command{Use: commands.PluginName}
upCmd := &cobra.Command{Use: "up"}
dockerComposeCmd.AddCommand(upCmd)
return upCmd
},
want: []string{"up"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd := tt.setupCmd()
got := commandName(cmd)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("commandName() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -17,6 +17,9 @@
package compatibility
import (
"errors"
"os"
"os/exec"
"testing"
"gotest.tools/v3/assert"
@ -24,9 +27,10 @@ import (
func Test_convert(t *testing.T) {
tests := []struct {
name string
args []string
want []string
name string
args []string
want []string
wantErr bool
}{
{
name: "compose only",
@ -93,11 +97,36 @@ func Test_convert(t *testing.T) {
args: []string{"--project-name", "compose", "down", "--remove-orphans"},
want: []string{"compose", "--project-name", "compose", "down", "--remove-orphans"},
},
{
name: "completion command",
args: []string{"__complete", "up"},
want: []string{"__complete", "compose", "up"},
},
{
name: "string flag without argument",
args: []string{"--log-level"},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Convert(tt.args)
assert.DeepEqual(t, tt.want, got)
if tt.wantErr {
if os.Getenv("BE_CRASHER") == "1" {
Convert(tt.args)
return
}
cmd := exec.Command(os.Args[0], "-test.run=^"+t.Name()+"$")
cmd.Env = append(os.Environ(), "BE_CRASHER=1")
err := cmd.Run()
var e *exec.ExitError
if errors.As(err, &e) && !e.Success() {
return
}
t.Fatalf("process ran with err %v, want exit status 1", err)
} else {
got := Convert(tt.args)
assert.DeepEqual(t, tt.want, got)
}
})
}
}

View File

@ -27,6 +27,7 @@ import (
"github.com/docker/cli/cli/command"
cliopts "github.com/docker/cli/opts"
ui "github.com/docker/compose/v2/pkg/progress"
"github.com/docker/compose/v2/pkg/utils"
buildkit "github.com/moby/buildkit/util/progress/progressui"
"github.com/spf13/cobra"
@ -44,6 +45,7 @@ type buildOptions struct {
ssh string
builder string
deps bool
print bool
}
func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions, error) {
@ -76,6 +78,8 @@ func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions,
Quiet: opts.quiet,
Services: services,
Deps: opts.deps,
Memory: int64(opts.memory),
Print: opts.print,
SSHs: SSHKeys,
Builder: builderName,
}, nil
@ -131,6 +135,7 @@ func buildCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
flags.VarP(&opts.memory, "memory", "m", "Set memory limit for the build container. Not supported by BuildKit.")
flags.StringVar(&p.Progress, "progress", string(buildkit.AutoMode), fmt.Sprintf(`Set type of ui output (%s)`, strings.Join(printerModes, ", ")))
flags.MarkHidden("progress") //nolint:errcheck
flags.BoolVar(&opts.print, "print", false, "Print equivalent bake file")
return cmd
}
@ -141,6 +146,8 @@ func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Service, o
return err
}
services = addBuildDependencies(services, project)
if err := applyPlatforms(project, false); err != nil {
return err
}
@ -150,6 +157,23 @@ func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Service, o
return err
}
apiBuildOptions.Memory = int64(opts.memory)
return backend.Build(ctx, project, apiBuildOptions)
}
func addBuildDependencies(services []string, project *types.Project) []string {
servicesWithDependencies := utils.NewSet(services...)
for _, service := range services {
build := project.Services[service].Build
if build != nil {
for _, target := range build.AdditionalContexts {
if s, found := strings.CutPrefix(target, types.ServicePrefix); found {
servicesWithDependencies.Add(s)
}
}
}
}
if len(servicesWithDependencies) > len(services) {
return addBuildDependencies(servicesWithDependencies.Elements(), project)
}
return servicesWithDependencies.Elements()
}

57
cmd/compose/build_test.go Normal file
View File

@ -0,0 +1,57 @@
/*
Copyright 2020 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package compose
import (
"slices"
"testing"
"github.com/compose-spec/compose-go/v2/types"
"gotest.tools/v3/assert"
)
func Test_addBuildDependencies(t *testing.T) {
project := &types.Project{Services: types.Services{
"test": types.ServiceConfig{
Build: &types.BuildConfig{
AdditionalContexts: map[string]string{
"foo": "service:foo",
"bar": "service:bar",
},
},
},
"foo": types.ServiceConfig{
Build: &types.BuildConfig{
AdditionalContexts: map[string]string{
"zot": "service:zot",
},
},
},
"bar": types.ServiceConfig{
Build: &types.BuildConfig{},
},
"zot": types.ServiceConfig{
Build: &types.BuildConfig{},
},
}}
services := addBuildDependencies([]string{"test"}, project)
expected := []string{"test", "foo", "bar", "zot"}
slices.Sort(services)
slices.Sort(expected)
assert.DeepEqual(t, services, expected)
}

93
cmd/compose/commit.go Normal file
View File

@ -0,0 +1,93 @@
/*
Copyright 2020 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package compose
import (
"context"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/opts"
"github.com/docker/compose/v2/pkg/api"
"github.com/spf13/cobra"
)
type commitOptions struct {
*ProjectOptions
service string
reference string
pause bool
comment string
author string
changes opts.ListOpts
index int
}
func commitCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
options := commitOptions{
ProjectOptions: p,
}
cmd := &cobra.Command{
Use: "commit [OPTIONS] SERVICE [REPOSITORY[:TAG]]",
Short: "Create a new image from a service container's changes",
Args: cobra.RangeArgs(1, 2),
PreRunE: Adapt(func(ctx context.Context, args []string) error {
options.service = args[0]
if len(args) > 1 {
options.reference = args[1]
}
return nil
}),
RunE: Adapt(func(ctx context.Context, args []string) error {
return runCommit(ctx, dockerCli, backend, options)
}),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
flags := cmd.Flags()
flags.IntVar(&options.index, "index", 0, "index of the container if service has multiple replicas.")
flags.BoolVarP(&options.pause, "pause", "p", true, "Pause container during commit")
flags.StringVarP(&options.comment, "message", "m", "", "Commit message")
flags.StringVarP(&options.author, "author", "a", "", `Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")`)
options.changes = opts.NewListOpts(nil)
flags.VarP(&options.changes, "change", "c", "Apply Dockerfile instruction to the created image")
return cmd
}
func runCommit(ctx context.Context, dockerCli command.Cli, backend api.Service, options commitOptions) error {
projectName, err := options.toProjectName(ctx, dockerCli)
if err != nil {
return err
}
commitOptions := api.CommitOptions{
Service: options.service,
Reference: options.reference,
Pause: options.pause,
Comment: options.comment,
Author: options.author,
Changes: options.changes,
Index: options.index,
}
return backend.Commit(ctx, projectName, commitOptions)
}

View File

@ -90,3 +90,13 @@ func completeProfileNames(dockerCli command.Cli, p *ProjectOptions) validArgsFn
return values, cobra.ShellCompDirectiveNoFileComp
}
}
func completeScaleArgs(cli command.Cli, p *ProjectOptions) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
completions, directive := completeServiceNames(cli, p)(cmd, args, toComplete)
for i, completion := range completions {
completions[i] = completion + "="
}
return completions, directive
}
}

View File

@ -44,7 +44,6 @@ import (
"github.com/docker/compose/v2/internal/experimental"
"github.com/docker/compose/v2/internal/tracing"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/compose"
ui "github.com/docker/compose/v2/pkg/progress"
"github.com/docker/compose/v2/pkg/remote"
"github.com/docker/compose/v2/pkg/utils"
@ -73,17 +72,16 @@ const (
)
// rawEnv load a dot env file using docker/cli key=value parser, without attempt to interpolate or evaluate values
func rawEnv(r io.Reader, filename string, lookup func(key string) (string, bool)) (map[string]string, error) {
func rawEnv(r io.Reader, filename string, vars map[string]string, lookup func(key string) (string, bool)) error {
lines, err := kvfile.ParseFromReader(r, lookup)
if err != nil {
return nil, fmt.Errorf("failed to parse env_file %s: %w", filename, err)
return fmt.Errorf("failed to parse env_file %s: %w", filename, err)
}
vars := types.Mapping{}
for _, line := range lines {
key, value, _ := strings.Cut(line, "=")
vars[key] = value
}
return vars, nil
return nil
}
func init() {
@ -121,17 +119,9 @@ func AdaptCmd(fn CobraCommand) func(cmd *cobra.Command, args []string) error {
}()
err := fn(ctx, cmd, args)
var composeErr compose.Error
if api.IsErrCanceled(err) || errors.Is(ctx.Err(), context.Canceled) {
err = dockercli.StatusError{
StatusCode: 130,
Status: compose.CanceledStatus,
}
}
if errors.As(err, &composeErr) {
err = dockercli.StatusError{
StatusCode: composeErr.GetMetricsFailureCategory().ExitCode,
Status: err.Error(),
}
}
if ui.Mode == ui.ModeJSON {
@ -179,7 +169,7 @@ func (o *ProjectOptions) WithServices(dockerCli command.Cli, fn ProjectServicesF
return Adapt(func(ctx context.Context, args []string) error {
options := []cli.ProjectOptionsFn{
cli.WithResolvedPaths(true),
cli.WithDiscardEnvFile,
cli.WithoutEnvironmentResolution,
}
project, metrics, err := o.ToProject(ctx, dockerCli, args, options...)
@ -189,6 +179,11 @@ func (o *ProjectOptions) WithServices(dockerCli command.Cli, fn ProjectServicesF
ctx = context.WithValue(ctx, tracing.MetricsKey{}, metrics)
project, err = project.WithServicesEnvironmentResolved(true)
if err != nil {
return err
}
return fn(ctx, project, args)
})
}
@ -307,7 +302,7 @@ func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, s
options, err := o.toProjectOptions(po...)
if err != nil {
return nil, metrics, compose.WrapComposeError(err)
return nil, metrics, err
}
options.WithListeners(func(event string, metadata map[string]any) {
@ -339,7 +334,7 @@ func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, s
project, err := options.LoadProject(ctx)
if err != nil {
return nil, metrics, compose.WrapComposeError(err)
return nil, metrics, err
}
if project.Name == "" {
@ -381,17 +376,24 @@ func (o *ProjectOptions) remoteLoaders(dockerCli command.Cli) []loader.ResourceL
if o.Offline {
return nil
}
git := remote.NewGitRemoteLoader(o.Offline)
git := remote.NewGitRemoteLoader(dockerCli, o.Offline)
oci := remote.NewOCIRemoteLoader(dockerCli, o.Offline)
return []loader.ResourceLoader{git, oci}
}
func (o *ProjectOptions) toProjectOptions(po ...cli.ProjectOptionsFn) (*cli.ProjectOptions, error) {
pwd, err := os.Getwd()
if err != nil {
return nil, err
}
return cli.NewProjectOptions(o.ConfigPaths,
append(po,
cli.WithWorkingDirectory(o.ProjectDir),
// First apply os.Environment, always win
cli.WithOsEnv,
// set PWD as this variable is not consistently supported on Windows
cli.WithEnv([]string{"PWD=" + pwd}),
// Load PWD/.env if present and no explicit --env-file has been set
cli.WithEnvFiles(o.EnvFiles...),
// read dot env file to populate project environment
@ -453,7 +455,7 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
}
_ = cmd.Help()
return dockercli.StatusError{
StatusCode: compose.CommandSyntaxFailure.ExitCode,
StatusCode: 1,
Status: fmt.Sprintf("unknown docker command: %q", "compose "+args[0]),
}
},
@ -547,10 +549,7 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
}
composeCmd := cmd
for {
if composeCmd.Name() == PluginName {
break
}
for composeCmd.Name() != PluginName {
if !composeCmd.HasParent() {
return fmt.Errorf("error parsing command line, expected %q", PluginName)
}
@ -617,6 +616,7 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
execCommand(&opts, dockerCli, backend),
attachCommand(&opts, dockerCli, backend),
exportCommand(&opts, dockerCli, backend),
commitCommand(&opts, dockerCli, backend),
pauseCommand(&opts, dockerCli, backend),
unpauseCommand(&opts, dockerCli, backend),
topCommand(&opts, dockerCli, backend),
@ -633,6 +633,7 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
scaleCommand(&opts, dockerCli, backend),
statsCommand(&opts, dockerCli),
watchCommand(&opts, dockerCli, backend),
publishCommand(&opts, dockerCli, backend),
alphaCommand(&opts, dockerCli, backend),
)

View File

@ -47,6 +47,7 @@ type configOptions struct {
noInterpolate bool
noNormalize bool
noResolvePath bool
noResolveEnv bool
services bool
volumes bool
profiles bool
@ -135,6 +136,7 @@ func configCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
flags.BoolVar(&opts.noNormalize, "no-normalize", false, "Don't normalize compose model")
flags.BoolVar(&opts.noResolvePath, "no-path-resolution", false, "Don't resolve file paths")
flags.BoolVar(&opts.noConsistency, "no-consistency", false, "Don't check model consistency - warning: may produce invalid Compose output")
flags.BoolVar(&opts.noResolveEnv, "no-env-resolution", false, "Don't resolve service env files")
flags.BoolVar(&opts.services, "services", false, "Print the service names, one per line.")
flags.BoolVar(&opts.volumes, "volumes", false, "Print the volume names, one per line.")
@ -190,6 +192,13 @@ func runConfigInterpolate(ctx context.Context, dockerCli command.Cli, opts confi
}
}
if !opts.noResolveEnv {
project, err = project.WithServicesEnvironmentResolved(true)
if err != nil {
return nil, err
}
}
if !opts.noConsistency {
err := project.CheckContainerNameUnicity()
if err != nil {
@ -279,6 +288,22 @@ func formatModel(model map[string]any, format string) (content []byte, err error
}
func runServices(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
if opts.noInterpolate {
// we can't use ToProject, so the model we render here is only partially resolved
data, err := opts.ToModel(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
if err != nil {
return err
}
if _, ok := data["services"]; ok {
for serviceName := range data["services"].(map[string]any) {
_, _ = fmt.Fprintln(dockerCli.Out(), serviceName)
}
}
return nil
}
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
if err != nil {
return err
@ -287,6 +312,7 @@ func runServices(ctx context.Context, dockerCli command.Cli, opts configOptions)
_, _ = fmt.Fprintln(dockerCli.Out(), serviceName)
return nil
})
return err
}
@ -331,7 +357,6 @@ func runHash(ctx context.Context, dockerCli command.Cli, opts configOptions) err
}
hash, err := compose.ServiceHash(s)
if err != nil {
return err
}

View File

@ -26,7 +26,9 @@ import (
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/command"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/docker/compose/v2/pkg/api"
)
@ -46,6 +48,7 @@ type createOptions struct {
timeout int
quietPull bool
scale []string
AssumeYes bool
}
func createCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
@ -80,6 +83,15 @@ func createCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service
flags.BoolVar(&opts.noRecreate, "no-recreate", false, "If containers already exist, don't recreate them. Incompatible with --force-recreate.")
flags.BoolVar(&opts.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file")
flags.StringArrayVar(&opts.scale, "scale", []string{}, "Scale SERVICE to NUM instances. Overrides the `scale` setting in the Compose file if present.")
flags.BoolVarP(&opts.AssumeYes, "yes", "y", false, `Assume "yes" as answer to all prompts and run non-interactively`)
flags.SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
// assumeYes was introduced by mistake as `--y`
if name == "y" {
logrus.Warn("--y is deprecated, please use --yes instead")
name = "yes"
}
return pflag.NormalizedName(name)
})
return cmd
}
@ -107,6 +119,7 @@ func runCreate(ctx context.Context, _ command.Cli, backend api.Service, createOp
Inherit: !createOpts.noInherit,
Timeout: createOpts.GetTimeout(),
QuietPull: createOpts.quietPull,
AssumeYes: createOpts.AssumeYes,
})
}
@ -195,7 +208,9 @@ func applyScaleOpts(project *types.Project, opts []string) error {
}
func (opts createOptions) isPullPolicyValid() bool {
pullPolicies := []string{types.PullPolicyAlways, types.PullPolicyNever, types.PullPolicyBuild,
types.PullPolicyMissing, types.PullPolicyIfNotPresent}
pullPolicies := []string{
types.PullPolicyAlways, types.PullPolicyNever, types.PullPolicyBuild,
types.PullPolicyMissing, types.PullPolicyIfNotPresent,
}
return slices.Contains(pullPolicies, opts.Pull)
}

View File

@ -40,7 +40,9 @@ func TestRunCreate(t *testing.T) {
)
createOpts := createOptions{}
buildOpts := buildOptions{}
buildOpts := buildOptions{
ProjectOptions: &ProjectOptions{},
}
project := sampleProject()
err := runCreate(ctx, nil, backend, createOpts, buildOpts, project, nil)
require.NoError(t, err)
@ -58,7 +60,9 @@ func TestRunCreate_Build(t *testing.T) {
createOpts := createOptions{
Build: true,
}
buildOpts := buildOptions{}
buildOpts := buildOptions{
ProjectOptions: &ProjectOptions{},
}
project := sampleProject()
err := runCreate(ctx, nil, backend, createOpts, buildOpts, project, nil)
require.NoError(t, err)

View File

@ -86,7 +86,7 @@ func runExec(ctx context.Context, dockerCli command.Cli, backend api.Service, op
if err != nil {
return err
}
projectOptions, err := opts.composeOptions.toProjectOptions()
projectOptions, err := opts.composeOptions.toProjectOptions() //nolint:staticcheck
if err != nil {
return err
}

View File

@ -50,7 +50,7 @@ func listCommand(dockerCli command.Cli, backend api.Service) *cobra.Command {
ValidArgsFunction: noCompletion(),
}
lsCmd.Flags().StringVar(&lsOpts.Format, "format", "table", "Format the output. Values: [table | json]")
lsCmd.Flags().BoolVarP(&lsOpts.Quiet, "quiet", "q", false, "Only display IDs")
lsCmd.Flags().BoolVarP(&lsOpts.Quiet, "quiet", "q", false, "Only display project names")
lsCmd.Flags().Var(&lsOpts.Filter, "filter", "Filter output based on conditions provided")
lsCmd.Flags().BoolVarP(&lsOpts.All, "all", "a", false, "Show all stopped Compose projects")

View File

@ -17,9 +17,21 @@
package compose
import (
"context"
"fmt"
"io"
"os"
"sort"
"strings"
"text/tabwriter"
"github.com/compose-spec/compose-go/v2/cli"
"github.com/compose-spec/compose-go/v2/template"
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/internal/tracing"
ui "github.com/docker/compose/v2/pkg/progress"
"github.com/docker/compose/v2/pkg/prompt"
"github.com/docker/compose/v2/pkg/utils"
)
@ -72,3 +84,208 @@ func applyPlatforms(project *types.Project, buildForSinglePlatform bool) error {
}
return nil
}
// isRemoteConfig checks if the main compose file is from a remote source (OCI or Git)
func isRemoteConfig(dockerCli command.Cli, options buildOptions) bool {
if len(options.ConfigPaths) == 0 {
return false
}
remoteLoaders := options.remoteLoaders(dockerCli)
for _, loader := range remoteLoaders {
if loader.Accept(options.ConfigPaths[0]) {
return true
}
}
return false
}
// checksForRemoteStack handles environment variable prompts for remote configurations
func checksForRemoteStack(ctx context.Context, dockerCli command.Cli, project *types.Project, options buildOptions, assumeYes bool, cmdEnvs []string) error {
if !isRemoteConfig(dockerCli, options) {
return nil
}
if metrics, ok := ctx.Value(tracing.MetricsKey{}).(tracing.Metrics); ok && metrics.CountIncludesRemote > 0 {
if err := confirmRemoteIncludes(dockerCli, options, assumeYes); err != nil {
return err
}
}
displayLocationRemoteStack(dockerCli, project, options)
return promptForInterpolatedVariables(ctx, dockerCli, options.ProjectOptions, assumeYes, cmdEnvs)
}
// Prepare the values map and collect all variables info
type varInfo struct {
name string
value string
source string
required bool
defaultValue string
}
// promptForInterpolatedVariables displays all variables and their values at once,
// then prompts for confirmation
func promptForInterpolatedVariables(ctx context.Context, dockerCli command.Cli, projectOptions *ProjectOptions, assumeYes bool, cmdEnvs []string) error {
if assumeYes {
return nil
}
varsInfo, noVariables, err := extractInterpolationVariablesFromModel(ctx, dockerCli, projectOptions, cmdEnvs)
if err != nil {
return err
}
if noVariables {
return nil
}
displayInterpolationVariables(dockerCli.Out(), varsInfo)
// Prompt for confirmation
userInput := prompt.NewPrompt(dockerCli.In(), dockerCli.Out())
msg := "\nDo you want to proceed with these variables? [Y/n]: "
confirmed, err := userInput.Confirm(msg, true)
if err != nil {
return err
}
if !confirmed {
return fmt.Errorf("operation cancelled by user")
}
return nil
}
func extractInterpolationVariablesFromModel(ctx context.Context, dockerCli command.Cli, projectOptions *ProjectOptions, cmdEnvs []string) ([]varInfo, bool, error) {
cmdEnvMap := extractEnvCLIDefined(cmdEnvs)
// Create a model without interpolation to extract variables
opts := configOptions{
noInterpolate: true,
ProjectOptions: projectOptions,
}
model, err := opts.ToModel(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
if err != nil {
return nil, false, err
}
// Extract variables that need interpolation
variables := template.ExtractVariables(model, template.DefaultPattern)
if len(variables) == 0 {
return nil, true, nil
}
var varsInfo []varInfo
proposedValues := make(map[string]string)
for name, variable := range variables {
info := varInfo{
name: name,
required: variable.Required,
defaultValue: variable.DefaultValue,
}
// Determine value and source based on priority
if value, exists := cmdEnvMap[name]; exists {
info.value = value
info.source = "command-line"
proposedValues[name] = value
} else if value, exists := os.LookupEnv(name); exists {
info.value = value
info.source = "environment"
proposedValues[name] = value
} else if variable.DefaultValue != "" {
info.value = variable.DefaultValue
info.source = "compose file"
proposedValues[name] = variable.DefaultValue
} else {
info.value = "<unset>"
info.source = "none"
}
varsInfo = append(varsInfo, info)
}
return varsInfo, false, nil
}
func extractEnvCLIDefined(cmdEnvs []string) map[string]string {
// Parse command-line environment variables
cmdEnvMap := make(map[string]string)
for _, env := range cmdEnvs {
parts := strings.SplitN(env, "=", 2)
if len(parts) == 2 {
cmdEnvMap[parts[0]] = parts[1]
}
}
return cmdEnvMap
}
func displayInterpolationVariables(writer io.Writer, varsInfo []varInfo) {
// Display all variables in a table format
_, _ = fmt.Fprintln(writer, "\nFound the following variables in configuration:")
w := tabwriter.NewWriter(writer, 0, 0, 3, ' ', 0)
_, _ = fmt.Fprintln(w, "VARIABLE\tVALUE\tSOURCE\tREQUIRED\tDEFAULT")
sort.Slice(varsInfo, func(a, b int) bool {
return varsInfo[a].name < varsInfo[b].name
})
for _, info := range varsInfo {
required := "no"
if info.required {
required = "yes"
}
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n",
info.name,
info.value,
info.source,
required,
info.defaultValue,
)
}
_ = w.Flush()
}
func displayLocationRemoteStack(dockerCli command.Cli, project *types.Project, options buildOptions) {
mainComposeFile := options.ProjectOptions.ConfigPaths[0] //nolint:staticcheck
if ui.Mode != ui.ModeQuiet && ui.Mode != ui.ModeJSON {
_, _ = fmt.Fprintf(dockerCli.Out(), "Your compose stack %q is stored in %q\n", mainComposeFile, project.WorkingDir)
}
}
func confirmRemoteIncludes(dockerCli command.Cli, options buildOptions, assumeYes bool) error {
if assumeYes {
return nil
}
var remoteIncludes []string
remoteLoaders := options.ProjectOptions.remoteLoaders(dockerCli) //nolint:staticcheck
for _, cf := range options.ProjectOptions.ConfigPaths { //nolint:staticcheck
for _, loader := range remoteLoaders {
if loader.Accept(cf) {
remoteIncludes = append(remoteIncludes, cf)
break
}
}
}
if len(remoteIncludes) == 0 {
return nil
}
_, _ = fmt.Fprintln(dockerCli.Out(), "\nWarning: This Compose project includes files from remote sources:")
for _, include := range remoteIncludes {
_, _ = fmt.Fprintf(dockerCli.Out(), " - %s\n", include)
}
_, _ = fmt.Fprintln(dockerCli.Out(), "\nRemote includes could potentially be malicious. Make sure you trust the source.")
msg := "Do you want to continue? [y/N]: "
confirmed, err := prompt.NewPrompt(dockerCli.In(), dockerCli.Out()).Confirm(msg, false)
if err != nil {
return err
}
if !confirmed {
return fmt.Errorf("operation cancelled by user")
}
return nil
}

View File

@ -17,10 +17,20 @@
package compose
import (
"bytes"
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"testing"
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/streams"
"github.com/docker/compose/v2/pkg/mocks"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
)
func TestApplyPlatforms_InferFromRuntime(t *testing.T) {
@ -128,3 +138,257 @@ func TestApplyPlatforms_UnsupportedPlatform(t *testing.T) {
`service "test" build.platforms does not support value set by DOCKER_DEFAULT_PLATFORM: commodore/64`)
})
}
func TestIsRemoteConfig(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
cli := mocks.NewMockCli(ctrl)
tests := []struct {
name string
configPaths []string
want bool
}{
{
name: "empty config paths",
configPaths: []string{},
want: false,
},
{
name: "local file",
configPaths: []string{"docker-compose.yaml"},
want: false,
},
{
name: "OCI reference",
configPaths: []string{"oci://registry.example.com/stack:latest"},
want: true,
},
{
name: "GIT reference",
configPaths: []string{"git://github.com/user/repo.git"},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
opts := buildOptions{
ProjectOptions: &ProjectOptions{
ConfigPaths: tt.configPaths,
},
}
got := isRemoteConfig(cli, opts)
require.Equal(t, tt.want, got)
})
}
}
func TestDisplayLocationRemoteStack(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
cli := mocks.NewMockCli(ctrl)
buf := new(bytes.Buffer)
cli.EXPECT().Out().Return(streams.NewOut(buf)).AnyTimes()
project := &types.Project{
Name: "test-project",
WorkingDir: "/tmp/test",
}
options := buildOptions{
ProjectOptions: &ProjectOptions{
ConfigPaths: []string{"oci://registry.example.com/stack:latest"},
},
}
displayLocationRemoteStack(cli, project, options)
output := buf.String()
require.Equal(t, output, fmt.Sprintf("Your compose stack %q is stored in %q\n", "oci://registry.example.com/stack:latest", "/tmp/test"))
}
func TestDisplayInterpolationVariables(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
// Create a temporary directory for the test
tmpDir, err := os.MkdirTemp("", "compose-test")
require.NoError(t, err)
defer func() { _ = os.RemoveAll(tmpDir) }()
// Create a temporary compose file
composeContent := `
services:
app:
image: nginx
environment:
- TEST_VAR=${TEST_VAR:?required} # required with default
- API_KEY=${API_KEY:?} # required without default
- DEBUG=${DEBUG:-true} # optional with default
- UNSET_VAR # optional without default
`
composePath := filepath.Join(tmpDir, "docker-compose.yml")
err = os.WriteFile(composePath, []byte(composeContent), 0o644)
require.NoError(t, err)
buf := new(bytes.Buffer)
cli := mocks.NewMockCli(ctrl)
cli.EXPECT().Out().Return(streams.NewOut(buf)).AnyTimes()
// Create ProjectOptions with the temporary compose file
projectOptions := &ProjectOptions{
ConfigPaths: []string{composePath},
}
// Set up the context with necessary environment variables
ctx := context.Background()
_ = os.Setenv("TEST_VAR", "test-value")
_ = os.Setenv("API_KEY", "123456")
defer func() {
_ = os.Unsetenv("TEST_VAR")
_ = os.Unsetenv("API_KEY")
}()
// Extract variables from the model
info, noVariables, err := extractInterpolationVariablesFromModel(ctx, cli, projectOptions, []string{})
require.NoError(t, err)
require.False(t, noVariables)
// Display the variables
displayInterpolationVariables(cli.Out(), info)
// Expected output format with proper spacing
expected := "\nFound the following variables in configuration:\n" +
"VARIABLE VALUE SOURCE REQUIRED DEFAULT\n" +
"API_KEY 123456 environment yes \n" +
"DEBUG true compose file no true\n" +
"TEST_VAR test-value environment yes \n"
// Normalize spaces and newlines for comparison
normalizeSpaces := func(s string) string {
// Replace multiple spaces with a single space
s = strings.Join(strings.Fields(strings.TrimSpace(s)), " ")
return s
}
actualOutput := buf.String()
// Compare normalized strings
require.Equal(t,
normalizeSpaces(expected),
normalizeSpaces(actualOutput),
"\nExpected:\n%s\nGot:\n%s", expected, actualOutput)
}
func TestConfirmRemoteIncludes(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
cli := mocks.NewMockCli(ctrl)
tests := []struct {
name string
opts buildOptions
assumeYes bool
userInput string
wantErr bool
errMessage string
wantPrompt bool
wantOutput string
}{
{
name: "no remote includes",
opts: buildOptions{
ProjectOptions: &ProjectOptions{
ConfigPaths: []string{
"docker-compose.yaml",
"./local/path/compose.yaml",
},
},
},
assumeYes: false,
wantErr: false,
wantPrompt: false,
},
{
name: "assume yes with remote includes",
opts: buildOptions{
ProjectOptions: &ProjectOptions{
ConfigPaths: []string{
"oci://registry.example.com/stack:latest",
"git://github.com/user/repo.git",
},
},
},
assumeYes: true,
wantErr: false,
wantPrompt: false,
},
{
name: "user confirms remote includes",
opts: buildOptions{
ProjectOptions: &ProjectOptions{
ConfigPaths: []string{
"oci://registry.example.com/stack:latest",
"git://github.com/user/repo.git",
},
},
},
assumeYes: false,
userInput: "y\n",
wantErr: false,
wantPrompt: true,
wantOutput: "\nWarning: This Compose project includes files from remote sources:\n" +
" - oci://registry.example.com/stack:latest\n" +
" - git://github.com/user/repo.git\n" +
"\nRemote includes could potentially be malicious. Make sure you trust the source.\n" +
"Do you want to continue? [y/N]: ",
},
{
name: "user rejects remote includes",
opts: buildOptions{
ProjectOptions: &ProjectOptions{
ConfigPaths: []string{
"oci://registry.example.com/stack:latest",
},
},
},
assumeYes: false,
userInput: "n\n",
wantErr: true,
errMessage: "operation cancelled by user",
wantPrompt: true,
wantOutput: "\nWarning: This Compose project includes files from remote sources:\n" +
" - oci://registry.example.com/stack:latest\n" +
"\nRemote includes could potentially be malicious. Make sure you trust the source.\n" +
"Do you want to continue? [y/N]: ",
},
}
buf := new(bytes.Buffer)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cli.EXPECT().Out().Return(streams.NewOut(buf)).AnyTimes()
if tt.wantPrompt {
inbuf := io.NopCloser(bytes.NewBufferString(tt.userInput))
cli.EXPECT().In().Return(streams.NewIn(inbuf)).AnyTimes()
}
err := confirmRemoteIncludes(cli, tt.opts, tt.assumeYes)
if tt.wantErr {
require.Error(t, err)
require.Equal(t, tt.errMessage, err.Error())
} else {
require.NoError(t, err)
}
if tt.wantOutput != "" {
require.Equal(t, tt.wantOutput, buf.String())
}
buf.Reset()
})
}
}

View File

@ -27,6 +27,7 @@ import (
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
)
@ -74,13 +75,13 @@ func TestPsTable(t *testing.T) {
cli.EXPECT().Out().Return(stdout).AnyTimes()
cli.EXPECT().ConfigFile().Return(&configfile.ConfigFile{}).AnyTimes()
err = runPs(ctx, cli, backend, nil, opts)
assert.NoError(t, err)
require.NoError(t, err)
_, err = f.Seek(0, 0)
assert.NoError(t, err)
require.NoError(t, err)
output, err := os.ReadFile(out)
assert.NoError(t, err)
require.NoError(t, err)
assert.Contains(t, string(output), "8080/tcp, 8443/tcp")
}

View File

@ -18,17 +18,22 @@ package compose
import (
"context"
"errors"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
"github.com/docker/compose/v2/pkg/api"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
type publishOptions struct {
*ProjectOptions
resolveImageDigests bool
ociVersion string
withEnvironment bool
assumeYes bool
}
func publishCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
@ -41,22 +46,39 @@ func publishCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Servic
RunE: Adapt(func(ctx context.Context, args []string) error {
return runPublish(ctx, dockerCli, backend, opts, args[0])
}),
Args: cobra.ExactArgs(1),
Args: cli.ExactArgs(1),
}
flags := cmd.Flags()
flags.BoolVar(&opts.resolveImageDigests, "resolve-image-digests", false, "Pin image tags to digests")
flags.StringVar(&opts.ociVersion, "oci-version", "", "OCI Image/Artifact specification version (automatically determined by default)")
flags.StringVar(&opts.ociVersion, "oci-version", "", "OCI image/artifact specification version (automatically determined by default)")
flags.BoolVar(&opts.withEnvironment, "with-env", false, "Include environment variables in the published OCI artifact")
flags.BoolVarP(&opts.assumeYes, "yes", "y", false, `Assume "yes" as answer to all prompts`)
flags.SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
// assumeYes was introduced by mistake as `--y`
if name == "y" {
logrus.Warn("--y is deprecated, please use --yes instead")
name = "yes"
}
return pflag.NormalizedName(name)
})
return cmd
}
func runPublish(ctx context.Context, dockerCli command.Cli, backend api.Service, opts publishOptions, repository string) error {
project, _, err := opts.ToProject(ctx, dockerCli, nil)
project, metrics, err := opts.ToProject(ctx, dockerCli, nil)
if err != nil {
return err
}
if metrics.CountIncludesLocal > 0 {
return errors.New("cannot publish compose file with local includes")
}
return backend.Publish(ctx, project, repository, api.PublishOptions{
ResolveImageDigests: opts.resolveImageDigests,
OCIVersion: api.OCIVersion(opts.ociVersion),
WithEnvironment: opts.withEnvironment,
AssumeYes: opts.assumeYes,
})
}

View File

@ -48,12 +48,15 @@ func pullCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
cmd := &cobra.Command{
Use: "pull [OPTIONS] [SERVICE...]",
Short: "Pull service images",
PreRunE: Adapt(func(ctx context.Context, args []string) error {
if opts.noParallel {
PreRunE: func(cmd *cobra.Command, args []string) error {
if cmd.Flags().Changed("no-parallel") {
fmt.Fprint(os.Stderr, aec.Apply("option '--no-parallel' is DEPRECATED and will be ignored.\n", aec.RedF))
}
if cmd.Flags().Changed("parallel") {
fmt.Fprint(os.Stderr, aec.Apply("option '--parallel' is DEPRECATED and will be ignored.\n", aec.RedF))
}
return nil
}),
},
RunE: Adapt(func(ctx context.Context, args []string) error {
return runPull(ctx, dockerCli, backend, opts, args)
}),
@ -64,7 +67,7 @@ func pullCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
cmd.Flags().BoolVar(&opts.includeDeps, "include-deps", false, "Also pull services declared as dependencies")
cmd.Flags().BoolVar(&opts.parallel, "parallel", true, "DEPRECATED pull multiple images in parallel")
flags.MarkHidden("parallel") //nolint:errcheck
cmd.Flags().BoolVar(&opts.parallel, "no-parallel", true, "DEPRECATED disable parallel pulling")
cmd.Flags().BoolVar(&opts.noParallel, "no-parallel", true, "DEPRECATED disable parallel pulling")
flags.MarkHidden("no-parallel") //nolint:errcheck
cmd.Flags().BoolVar(&opts.ignorePullFailures, "ignore-pull-failures", false, "Pull what it can and ignores images with pull failures")
cmd.Flags().BoolVar(&opts.noBuildable, "ignore-buildable", false, "Ignore images that can be built")

View File

@ -19,8 +19,10 @@ package compose
import (
"context"
"fmt"
"os"
"strings"
"github.com/compose-spec/compose-go/v2/dotenv"
"github.com/compose-spec/compose-go/v2/format"
xprogress "github.com/moby/buildkit/util/progress/progressui"
"github.com/sirupsen/logrus"
@ -44,6 +46,7 @@ type runOptions struct {
Service string
Command []string
environment []string
envFiles []string
Detach bool
Remove bool
noTty bool
@ -63,6 +66,8 @@ type runOptions struct {
name string
noDeps bool
ignoreOrphans bool
removeOrphans bool
quiet bool
quietPull bool
}
@ -115,6 +120,29 @@ func (options runOptions) apply(project *types.Project) (*types.Project, error)
return project, nil
}
func (options runOptions) getEnvironment() (types.Mapping, error) {
environment := types.NewMappingWithEquals(options.environment).Resolve(os.LookupEnv).ToMapping()
for _, file := range options.envFiles {
f, err := os.Open(file)
if err != nil {
return nil, err
}
vars, err := dotenv.ParseWithLookup(f, func(k string) (string, bool) {
value, ok := environment[k]
return value, ok
})
if err != nil {
return nil, nil
}
for k, v := range vars {
if _, ok := environment[k]; !ok {
environment[k] = v
}
}
}
return environment, nil
}
func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
options := runOptions{
composeOptions: &composeOptions{
@ -153,10 +181,24 @@ func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
options.noTty = !options.tty
}
}
if options.quiet {
progress.Mode = progress.ModeQuiet
devnull, err := os.Open(os.DevNull)
if err != nil {
return err
}
os.Stdout = devnull
}
createOpts.pullChanged = cmd.Flags().Changed("pull")
return nil
}),
RunE: Adapt(func(ctx context.Context, args []string) error {
project, _, err := p.ToProject(ctx, dockerCli, []string{options.Service}, cgo.WithResolvedPaths(true), cgo.WithDiscardEnvFile)
project, _, err := p.ToProject(ctx, dockerCli, []string{options.Service}, cgo.WithResolvedPaths(true), cgo.WithoutEnvironmentResolution)
if err != nil {
return err
}
project, err = project.WithServicesEnvironmentResolved(true)
if err != nil {
return err
}
@ -173,6 +215,7 @@ func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
flags := cmd.Flags()
flags.BoolVarP(&options.Detach, "detach", "d", false, "Run container in background and print container ID")
flags.StringArrayVarP(&options.environment, "env", "e", []string{}, "Set environment variables")
flags.StringArrayVar(&options.envFiles, "env-from-file", []string{}, "Set environment variables from file")
flags.StringArrayVarP(&options.labels, "label", "l", []string{}, "Add or override a label")
flags.BoolVar(&options.Remove, "rm", false, "Automatically remove the container when it exits")
flags.BoolVarP(&options.noTty, "no-TTY", "T", !dockerCli.Out().IsTerminal(), "Disable pseudo-TTY allocation (default: auto-detected)")
@ -187,9 +230,12 @@ func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
flags.StringArrayVarP(&options.publish, "publish", "p", []string{}, "Publish a container's port(s) to the host")
flags.BoolVar(&options.useAliases, "use-aliases", false, "Use the service's network useAliases in the network(s) the container connects to")
flags.BoolVarP(&options.servicePorts, "service-ports", "P", false, "Run command with all service's ports enabled and mapped to the host")
flags.StringVar(&createOpts.Pull, "pull", "policy", `Pull image before running ("always"|"missing"|"never")`)
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Don't print anything to STDOUT")
flags.BoolVar(&buildOpts.quiet, "quiet-build", false, "Suppress progress output from the build process")
flags.BoolVar(&options.quietPull, "quiet-pull", false, "Pull without printing progress information")
flags.BoolVar(&createOpts.Build, "build", false, "Build image before starting container")
flags.BoolVar(&createOpts.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file")
flags.BoolVar(&options.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file")
cmd.Flags().BoolVarP(&options.interactive, "interactive", "i", true, "Keep STDIN open even if not attached")
cmd.Flags().BoolVarP(&options.tty, "tty", "t", true, "Allocate a pseudo-TTY")
@ -221,6 +267,10 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
return err
}
if err := checksForRemoteStack(ctx, dockerCli, project, buildOpts, createOpts.AssumeYes, []string{}); err != nil {
return err
}
err = progress.Run(ctx, func(ctx context.Context) error {
var buildForDeps *api.BuildOptions
if !createOpts.noBuild {
@ -257,6 +307,11 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
buildForRun = &bo
}
environment, err := options.getEnvironment()
if err != nil {
return err
}
// start container and attach to container streams
runOpts := api.RunOptions{
Build: buildForRun,
@ -271,7 +326,7 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
User: options.user,
CapAdd: options.capAdd.GetAll(),
CapDrop: options.capDrop.GetAll(),
Environment: options.environment,
Environment: environment.Values(),
Entrypoint: options.entrypointCmd,
Labels: labels,
UseNetworkAliases: options.useAliases,
@ -314,6 +369,7 @@ func startDependencies(ctx context.Context, backend api.Service, project types.P
err := backend.Create(ctx, &project, api.CreateOptions{
Build: buildOpts,
IgnoreOrphans: options.ignoreOrphans,
RemoveOrphans: options.removeOrphans,
QuietPull: options.quietPull,
})
if err != nil {

View File

@ -51,7 +51,7 @@ func scaleCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
}
return runScale(ctx, dockerCli, backend, opts, serviceTuples)
}),
ValidArgsFunction: completeServiceNames(dockerCli, p),
ValidArgsFunction: completeScaleArgs(dockerCli, p),
}
flags := scaleCmd.Flags()
flags.BoolVar(&opts.noDeps, "no-deps", false, "Don't start linked services")
@ -92,7 +92,6 @@ func parseServicesReplicasArgs(args []string) (map[string]int, error) {
return nil, fmt.Errorf("invalid scale specifier: %s", arg)
}
intValue, err := strconv.Atoi(val)
if err != nil {
return nil, fmt.Errorf("invalid scale specifier: can't parse replica value as int: %v", arg)
}

View File

@ -56,7 +56,7 @@ func statsCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
'table TEMPLATE': Print output in table format using the given Go template
'json': Print in JSON format
'TEMPLATE': Print output using the given Go template.
Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates`)
Refer to https://docs.docker.com/engine/cli/formatting/ for more information about formatting output with templates`)
flags.BoolVar(&opts.noStream, "no-stream", false, "Disable streaming stats and only pull the first result")
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate output")
return cmd

View File

@ -49,6 +49,11 @@ func topCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
return topCmd
}
type (
topHeader map[string]int // maps a proc title to its output index
topEntries map[string]string
)
func runTop(ctx context.Context, dockerCli command.Cli, backend api.Service, opts topOptions, services []string) error {
projectName, err := opts.toProjectName(ctx, dockerCli)
if err != nil {
@ -63,30 +68,76 @@ func runTop(ctx context.Context, dockerCli command.Cli, backend api.Service, opt
return containers[i].Name < containers[j].Name
})
for _, container := range containers {
_, _ = fmt.Fprintf(dockerCli.Out(), "%s\n", container.Name)
err := psPrinter(dockerCli.Out(), func(w io.Writer) {
for _, proc := range container.Processes {
info := []interface{}{}
for _, p := range proc {
info = append(info, p)
}
_, _ = fmt.Fprintf(w, strings.Repeat("%s\t", len(info))+"\n", info...)
header, entries := collectTop(containers)
return topPrint(dockerCli.Out(), header, entries)
}
func collectTop(containers []api.ContainerProcSummary) (topHeader, []topEntries) {
// map column name to its header (should keep working if backend.Top returns
// varying columns for different containers)
header := topHeader{"SERVICE": 0, "#": 1}
// assume one process per container and grow if needed
entries := make([]topEntries, 0, len(containers))
for _, container := range containers {
for _, proc := range container.Processes {
entry := topEntries{
"SERVICE": container.Service,
"#": container.Replica,
}
_, _ = fmt.Fprintln(w)
},
container.Titles...)
if err != nil {
return err
for i, title := range container.Titles {
if _, exists := header[title]; !exists {
header[title] = len(header)
}
entry[title] = proc[i]
}
entries = append(entries, entry)
}
}
return nil
// ensure CMD is the right-most column
if pos, ok := header["CMD"]; ok {
maxPos := pos
for h, i := range header {
if i > maxPos {
maxPos = i
}
if i > pos {
header[h] = i - 1
}
}
header["CMD"] = maxPos
}
return header, entries
}
func psPrinter(out io.Writer, printer func(writer io.Writer), headers ...string) error {
w := tabwriter.NewWriter(out, 5, 1, 3, ' ', 0)
_, _ = fmt.Fprintln(w, strings.Join(headers, "\t"))
printer(w)
func topPrint(out io.Writer, headers topHeader, rows []topEntries) error {
if len(rows) == 0 {
return nil
}
w := tabwriter.NewWriter(out, 4, 1, 2, ' ', 0)
// write headers in the order we've encountered them
h := make([]string, len(headers))
for title, index := range headers {
h[index] = title
}
_, _ = fmt.Fprintln(w, strings.Join(h, "\t"))
for _, row := range rows {
// write proc data in header order
r := make([]string, len(headers))
for title, index := range headers {
if v, ok := row[title]; ok {
r[index] = v
} else {
r[index] = "-"
}
}
_, _ = fmt.Fprintln(w, strings.Join(r, "\t"))
}
return w.Flush()
}

329
cmd/compose/top_test.go Normal file
View File

@ -0,0 +1,329 @@
/*
Copyright 2024 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package compose
import (
"bytes"
"strings"
"testing"
"github.com/docker/compose/v2/pkg/api"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var topTestCases = []struct {
name string
titles []string
procs [][]string
header topHeader
entries []topEntries
output string
}{
{
name: "noprocs",
titles: []string{"UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"},
procs: [][]string{},
header: topHeader{"SERVICE": 0, "#": 1},
entries: []topEntries{},
output: "",
},
{
name: "simple",
titles: []string{"UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"},
procs: [][]string{{"root", "1", "1", "0", "12:00", "?", "00:00:01", "/entrypoint"}},
header: topHeader{
"SERVICE": 0,
"#": 1,
"UID": 2,
"PID": 3,
"PPID": 4,
"C": 5,
"STIME": 6,
"TTY": 7,
"TIME": 8,
"CMD": 9,
},
entries: []topEntries{
{
"SERVICE": "simple",
"#": "1",
"UID": "root",
"PID": "1",
"PPID": "1",
"C": "0",
"STIME": "12:00",
"TTY": "?",
"TIME": "00:00:01",
"CMD": "/entrypoint",
},
},
output: trim(`
SERVICE # UID PID PPID C STIME TTY TIME CMD
simple 1 root 1 1 0 12:00 ? 00:00:01 /entrypoint
`),
},
{
name: "noppid",
titles: []string{"UID", "PID", "C", "STIME", "TTY", "TIME", "CMD"},
procs: [][]string{{"root", "1", "0", "12:00", "?", "00:00:02", "/entrypoint"}},
header: topHeader{
"SERVICE": 0,
"#": 1,
"UID": 2,
"PID": 3,
"C": 4,
"STIME": 5,
"TTY": 6,
"TIME": 7,
"CMD": 8,
},
entries: []topEntries{
{
"SERVICE": "noppid",
"#": "1",
"UID": "root",
"PID": "1",
"C": "0",
"STIME": "12:00",
"TTY": "?",
"TIME": "00:00:02",
"CMD": "/entrypoint",
},
},
output: trim(`
SERVICE # UID PID C STIME TTY TIME CMD
noppid 1 root 1 0 12:00 ? 00:00:02 /entrypoint
`),
},
{
name: "extra-hdr",
titles: []string{"UID", "GID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"},
procs: [][]string{{"root", "1", "1", "1", "0", "12:00", "?", "00:00:03", "/entrypoint"}},
header: topHeader{
"SERVICE": 0,
"#": 1,
"UID": 2,
"GID": 3,
"PID": 4,
"PPID": 5,
"C": 6,
"STIME": 7,
"TTY": 8,
"TIME": 9,
"CMD": 10,
},
entries: []topEntries{
{
"SERVICE": "extra-hdr",
"#": "1",
"UID": "root",
"GID": "1",
"PID": "1",
"PPID": "1",
"C": "0",
"STIME": "12:00",
"TTY": "?",
"TIME": "00:00:03",
"CMD": "/entrypoint",
},
},
output: trim(`
SERVICE # UID GID PID PPID C STIME TTY TIME CMD
extra-hdr 1 root 1 1 1 0 12:00 ? 00:00:03 /entrypoint
`),
},
{
name: "multiple",
titles: []string{"UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"},
procs: [][]string{
{"root", "1", "1", "0", "12:00", "?", "00:00:04", "/entrypoint"},
{"root", "123", "1", "0", "12:00", "?", "00:00:42", "sleep infinity"},
},
header: topHeader{
"SERVICE": 0,
"#": 1,
"UID": 2,
"PID": 3,
"PPID": 4,
"C": 5,
"STIME": 6,
"TTY": 7,
"TIME": 8,
"CMD": 9,
},
entries: []topEntries{
{
"SERVICE": "multiple",
"#": "1",
"UID": "root",
"PID": "1",
"PPID": "1",
"C": "0",
"STIME": "12:00",
"TTY": "?",
"TIME": "00:00:04",
"CMD": "/entrypoint",
},
{
"SERVICE": "multiple",
"#": "1",
"UID": "root",
"PID": "123",
"PPID": "1",
"C": "0",
"STIME": "12:00",
"TTY": "?",
"TIME": "00:00:42",
"CMD": "sleep infinity",
},
},
output: trim(`
SERVICE # UID PID PPID C STIME TTY TIME CMD
multiple 1 root 1 1 0 12:00 ? 00:00:04 /entrypoint
multiple 1 root 123 1 0 12:00 ? 00:00:42 sleep infinity
`),
},
}
// TestRunTopCore only tests the core functionality of runTop: formatting
// and printing of the output of (api.Service).Top().
func TestRunTopCore(t *testing.T) {
t.Parallel()
all := []api.ContainerProcSummary{}
for _, tc := range topTestCases {
summary := api.ContainerProcSummary{
Name: "not used",
Titles: tc.titles,
Processes: tc.procs,
Service: tc.name,
Replica: "1",
}
all = append(all, summary)
t.Run(tc.name, func(t *testing.T) {
header, entries := collectTop([]api.ContainerProcSummary{summary})
assert.Equal(t, tc.header, header)
assert.Equal(t, tc.entries, entries)
var buf bytes.Buffer
err := topPrint(&buf, header, entries)
require.NoError(t, err)
assert.Equal(t, tc.output, buf.String())
})
}
t.Run("all", func(t *testing.T) {
header, entries := collectTop(all)
assert.Equal(t, topHeader{
"SERVICE": 0,
"#": 1,
"UID": 2,
"PID": 3,
"PPID": 4,
"C": 5,
"STIME": 6,
"TTY": 7,
"TIME": 8,
"GID": 9,
"CMD": 10,
}, header)
assert.Equal(t, []topEntries{
{
"SERVICE": "simple",
"#": "1",
"UID": "root",
"PID": "1",
"PPID": "1",
"C": "0",
"STIME": "12:00",
"TTY": "?",
"TIME": "00:00:01",
"CMD": "/entrypoint",
}, {
"SERVICE": "noppid",
"#": "1",
"UID": "root",
"PID": "1",
"C": "0",
"STIME": "12:00",
"TTY": "?",
"TIME": "00:00:02",
"CMD": "/entrypoint",
}, {
"SERVICE": "extra-hdr",
"#": "1",
"UID": "root",
"GID": "1",
"PID": "1",
"PPID": "1",
"C": "0",
"STIME": "12:00",
"TTY": "?",
"TIME": "00:00:03",
"CMD": "/entrypoint",
}, {
"SERVICE": "multiple",
"#": "1",
"UID": "root",
"PID": "1",
"PPID": "1",
"C": "0",
"STIME": "12:00",
"TTY": "?",
"TIME": "00:00:04",
"CMD": "/entrypoint",
}, {
"SERVICE": "multiple",
"#": "1",
"UID": "root",
"PID": "123",
"PPID": "1",
"C": "0",
"STIME": "12:00",
"TTY": "?",
"TIME": "00:00:42",
"CMD": "sleep infinity",
},
}, entries)
var buf bytes.Buffer
err := topPrint(&buf, header, entries)
require.NoError(t, err)
assert.Equal(t, trim(`
SERVICE # UID PID PPID C STIME TTY TIME GID CMD
simple 1 root 1 1 0 12:00 ? 00:00:01 - /entrypoint
noppid 1 root 1 - 0 12:00 ? 00:00:02 - /entrypoint
extra-hdr 1 root 1 1 0 12:00 ? 00:00:03 1 /entrypoint
multiple 1 root 1 1 0 12:00 ? 00:00:04 - /entrypoint
multiple 1 root 123 1 0 12:00 ? 00:00:42 - sleep infinity
`), buf.String())
})
}
func trim(s string) string {
var out bytes.Buffer
for _, line := range strings.Split(strings.TrimSpace(s), "\n") {
out.WriteString(strings.TrimSpace(line))
out.WriteRune('\n')
}
return out.String()
}

View File

@ -26,10 +26,12 @@ import (
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/cmd/formatter"
xprogress "github.com/moby/buildkit/util/progress/progressui"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/docker/compose/v2/cmd/formatter"
"github.com/docker/compose/v2/pkg/api"
ui "github.com/docker/compose/v2/pkg/progress"
"github.com/docker/compose/v2/pkg/utils"
@ -167,10 +169,18 @@ func upCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *c
flags.StringArrayVar(&up.noAttach, "no-attach", []string{}, "Do not attach (stream logs) to the specified services")
flags.BoolVar(&up.attachDependencies, "attach-dependencies", false, "Automatically attach to log output of dependent services")
flags.BoolVar(&up.wait, "wait", false, "Wait for services to be running|healthy. Implies detached mode.")
flags.IntVar(&up.waitTimeout, "wait-timeout", 0, "Maximum duration to wait for the project to be running|healthy")
flags.IntVar(&up.waitTimeout, "wait-timeout", 0, "Maximum duration in seconds to wait for the project to be running|healthy")
flags.BoolVarP(&up.watch, "watch", "w", false, "Watch source code and rebuild/refresh containers when files are updated.")
flags.BoolVar(&up.navigationMenu, "menu", false, "Enable interactive shortcuts when running attached. Incompatible with --detach. Can also be enable/disable by setting COMPOSE_MENU environment var.")
flags.BoolVarP(&create.AssumeYes, "yes", "y", false, `Assume "yes" as answer to all prompts and run non-interactively`)
flags.SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
// assumeYes was introduced by mistake as `--y`
if name == "y" {
logrus.Warn("--y is deprecated, please use --yes instead")
name = "yes"
}
return pflag.NormalizedName(name)
})
return upCmd
}
@ -192,7 +202,11 @@ func validateFlags(up *upOptions, create *createOptions) error {
return fmt.Errorf("--build and --no-build are incompatible")
}
if up.Detach && (up.attachDependencies || up.cascadeStop || up.cascadeFail || len(up.attach) > 0 || up.watch) {
return fmt.Errorf("--detach cannot be combined with --abort-on-container-exit, --abort-on-container-failure, --attach, --attach-dependencies or --watch")
if up.wait {
return fmt.Errorf("--wait cannot be combined with --abort-on-container-exit, --abort-on-container-failure, --attach, --attach-dependencies or --watch")
} else {
return fmt.Errorf("--detach cannot be combined with --abort-on-container-exit, --abort-on-container-failure, --attach, --attach-dependencies or --watch")
}
}
if create.noInherit && create.noRecreate {
return fmt.Errorf("--no-recreate and --renew-anon-volumes are incompatible")
@ -219,6 +233,10 @@ func runUp(
project *types.Project,
services []string,
) error {
if err := checksForRemoteStack(ctx, dockerCli, project, buildOptions, createOptions.AssumeYes, []string{}); err != nil {
return err
}
err := createOptions.Apply(project)
if err != nil {
return err
@ -255,6 +273,7 @@ func runUp(
Inherit: !createOptions.noInherit,
Timeout: createOptions.GetTimeout(),
QuietPull: createOptions.quietPull,
AssumeYes: createOptions.AssumeYes,
}
if upOptions.noStart {

View File

@ -47,5 +47,4 @@ func TestApplyScaleOpt(t *testing.T) {
assert.NilError(t, err)
assert.Equal(t, *bar.Scale, 3)
assert.Equal(t, *bar.Deploy.Replicas, 3)
}

View File

@ -0,0 +1,76 @@
/*
Copyright 2025 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package compose
import (
"bytes"
"testing"
"github.com/docker/cli/cli/streams"
"github.com/docker/compose/v2/internal"
"github.com/docker/compose/v2/pkg/mocks"
"go.uber.org/mock/gomock"
"gotest.tools/v3/assert"
)
func TestVersionCommand(t *testing.T) {
originalVersion := internal.Version
defer func() {
internal.Version = originalVersion
}()
internal.Version = "v9.9.9-test"
tests := []struct {
name string
args []string
want string
}{
{
name: "default",
args: []string{},
want: "Docker Compose version v9.9.9-test\n",
},
{
name: "short flag",
args: []string{"--short"},
want: "9.9.9-test\n",
},
{
name: "json flag",
args: []string{"--format", "json"},
want: `{"version":"v9.9.9-test"}` + "\n",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
buf := new(bytes.Buffer)
cli := mocks.NewMockCli(ctrl)
cli.EXPECT().Out().Return(streams.NewOut(buf)).AnyTimes()
cmd := versionCommand(cli)
cmd.SetArgs(test.args)
err := cmd.Execute()
assert.NilError(t, err)
assert.Equal(t, test.want, buf.String())
})
}
}

View File

@ -17,10 +17,10 @@
package compose
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPreferredIndentationStr(t *testing.T) {
@ -83,10 +83,12 @@ func TestPreferredIndentationStr(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := preferredIndentationStr(tt.args.size, tt.args.useSpace)
if tt.wantErr && assert.NotNilf(t, err, fmt.Sprintf("preferredIndentationStr(%v, %v)", tt.args.size, tt.args.useSpace)) {
return
if tt.wantErr {
require.Errorf(t, err, "preferredIndentationStr(%v, %v)", tt.args.size, tt.args.useSpace)
} else {
require.NoError(t, err)
assert.Equalf(t, tt.want, got, "preferredIndentationStr(%v, %v)", tt.args.size, tt.args.useSpace)
}
assert.Equalf(t, tt.want, got, "preferredIndentationStr(%v, %v)", tt.args.size, tt.args.useSpace)
})
}
}

View File

@ -59,13 +59,13 @@ func watchCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
}
cmd.Flags().BoolVar(&buildOpts.quiet, "quiet", false, "hide build output")
cmd.Flags().BoolVar(&watchOpts.prune, "prune", false, "Prune dangling images on rebuild")
cmd.Flags().BoolVar(&watchOpts.prune, "prune", true, "Prune dangling images on rebuild")
cmd.Flags().BoolVar(&watchOpts.noUp, "no-up", false, "Do not build & start services before watching")
return cmd
}
func runWatch(ctx context.Context, dockerCli command.Cli, backend api.Service, watchOpts watchOptions, buildOpts buildOptions, services []string) error {
project, _, err := watchOpts.ToProject(ctx, dockerCli, nil)
project, _, err := watchOpts.ToProject(ctx, dockerCli, services)
if err != nil {
return err
}

View File

@ -27,42 +27,49 @@ var disableAnsi bool
func ansi(code string) string {
return fmt.Sprintf("\033%s", code)
}
func SaveCursor() {
if disableAnsi {
return
}
fmt.Print(ansi("7"))
}
func RestoreCursor() {
if disableAnsi {
return
}
fmt.Print(ansi("8"))
}
func HideCursor() {
if disableAnsi {
return
}
fmt.Print(ansi("[?25l"))
}
func ShowCursor() {
if disableAnsi {
return
}
fmt.Print(ansi("[?25h"))
}
func MoveCursor(y, x int) {
if disableAnsi {
return
}
fmt.Print(ansi(fmt.Sprintf("[%d;%dH", y, x)))
}
func MoveCursorX(pos int) {
if disableAnsi {
return
}
fmt.Print(ansi(fmt.Sprintf("[%dG", pos)))
}
func ClearLine() {
if disableAnsi {
return
@ -70,6 +77,7 @@ func ClearLine() {
// Does not move cursor from its current position
fmt.Print(ansi("[2K"))
}
func MoveCursorUp(lines int) {
if disableAnsi {
return
@ -77,6 +85,7 @@ func MoveCursorUp(lines int) {
// Does not add new lines
fmt.Print(ansi(fmt.Sprintf("[%dA", lines)))
}
func MoveCursorDown(lines int) {
if disableAnsi {
return
@ -84,10 +93,12 @@ func MoveCursorDown(lines int) {
// Does not add new lines
fmt.Print(ansi(fmt.Sprintf("[%dB", lines)))
}
func NewLine() {
// Like \n
fmt.Print("\012")
}
func lenAnsi(s string) int {
// len has into consideration ansi codes, if we want
// the len of the actual len(string) we need to strip

View File

@ -104,10 +104,12 @@ func makeColorFunc(code string) colorFunc {
}
}
var nextColor = rainbowColor
var rainbow []colorFunc
var currentIndex = 0
var mutex sync.Mutex
var (
nextColor = rainbowColor
rainbow []colorFunc
currentIndex = 0
mutex sync.Mutex
)
func rainbowColor() colorFunc {
mutex.Lock()

View File

@ -24,7 +24,7 @@ import (
"github.com/docker/cli/cli/command/formatter"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/go-units"
)
@ -212,9 +212,9 @@ func (c *ContainerContext) Publishers() api.PortPublishers {
}
func (c *ContainerContext) Ports() string {
var ports []types.Port
var ports []container.Port
for _, publisher := range c.c.Publishers {
ports = append(ports, types.Port{
ports = append(ports, container.Port{
IP: publisher.URL,
PrivatePort: uint16(publisher.TargetPort),
PublicPort: uint16(publisher.PublishedPort),

View File

@ -110,8 +110,10 @@ type LogKeyboard struct {
signalChannel chan<- os.Signal
}
var KeyboardManager *LogKeyboard
var eg multierror.Group
var (
KeyboardManager *LogKeyboard
eg multierror.Group
)
func NewKeyboardManager(ctx context.Context, isDockerDesktopActive, isWatchConfigured bool,
sc chan<- os.Signal,
@ -206,7 +208,7 @@ func (lk *LogKeyboard) navigationMenu() string {
if openDDInfo != "" || openDDUI != "" {
watchInfo = navColor(" ")
}
var isEnabled = " Enable"
isEnabled := " Enable"
if lk.Watch.Watching {
isEnabled = " Disable"
}
@ -236,7 +238,7 @@ func (lk *LogKeyboard) openDockerDesktop(ctx context.Context, project *types.Pro
link := fmt.Sprintf("docker-desktop://dashboard/apps/%s", project.Name)
err := open.Run(link)
if err != nil {
err = fmt.Errorf("Could not open Docker Desktop")
err = fmt.Errorf("could not open Docker Desktop")
lk.keyboardError("View", err)
}
return err
@ -253,20 +255,21 @@ func (lk *LogKeyboard) openDDComposeUI(ctx context.Context, project *types.Proje
link := fmt.Sprintf("docker-desktop://dashboard/docker-compose/%s", project.Name)
err := open.Run(link)
if err != nil {
err = fmt.Errorf("Could not open Docker Desktop Compose UI")
err = fmt.Errorf("could not open Docker Desktop Compose UI")
lk.keyboardError("View Config", err)
}
return err
}),
)
}
func (lk *LogKeyboard) openDDWatchDocs(ctx context.Context, project *types.Project) {
eg.Go(tracing.EventWrapFuncForErrGroup(ctx, "menu/gui/watch", tracing.SpanOptions{},
func(ctx context.Context) error {
link := fmt.Sprintf("docker-desktop://dashboard/docker-compose/%s/watch", project.Name)
err := open.Run(link)
if err != nil {
err = fmt.Errorf("Could not open Docker Desktop Compose UI")
err = fmt.Errorf("could not open Docker Desktop Compose UI")
lk.keyboardError("Watch Docs", err)
}
return err
@ -296,7 +299,7 @@ func (lk *LogKeyboard) StartWatch(ctx context.Context, doneCh chan bool, project
eg.Go(tracing.EventWrapFuncForErrGroup(ctx, "menu/watch", tracing.SpanOptions{},
func(ctx context.Context) error {
if options.Create.Build == nil {
err := fmt.Errorf("Cannot run watch mode with flag --no-build")
err := fmt.Errorf("cannot run watch mode with flag --no-build")
lk.keyboardError("Watch", err)
return err
}
@ -304,10 +307,15 @@ func (lk *LogKeyboard) StartWatch(ctx context.Context, doneCh chan bool, project
lk.Watch.newContext(ctx)
buildOpts := *options.Create.Build
buildOpts.Quiet = true
return lk.Watch.WatchFn(lk.Watch.Ctx, doneCh, project, options.Start.Services, api.WatchOptions{
err := lk.Watch.WatchFn(lk.Watch.Ctx, doneCh, project, options.Start.Services, api.WatchOptions{
Build: &buildOpts,
LogTo: options.Start.Attach,
})
if err != nil {
lk.Watch.switchWatching()
options.Start.Attach.Err(api.WatchLogger, err.Error())
}
return err
}))
}
}

View File

@ -62,7 +62,7 @@ func pluginMain() {
cmd.SetFlagErrorFunc(func(c *cobra.Command, err error) error {
return dockercli.StatusError{
StatusCode: compose.CommandSyntaxFailure.ExitCode,
StatusCode: 1,
Status: err.Error(),
}
})

View File

@ -13,6 +13,7 @@ Define and run multi-container applications with Docker
|:--------------------------------|:----------------------------------------------------------------------------------------|
| [`attach`](compose_attach.md) | Attach local standard input, output, and error streams to a service's running container |
| [`build`](compose_build.md) | Build or rebuild services |
| [`commit`](compose_commit.md) | Create a new image from a service container's changes |
| [`config`](compose_config.md) | Parse, resolve and render compose file in canonical format |
| [`cp`](compose_cp.md) | Copy files/folders between a service container and the local filesystem |
| [`create`](compose_create.md) | Creates containers for a service |
@ -27,6 +28,7 @@ Define and run multi-container applications with Docker
| [`pause`](compose_pause.md) | Pause services |
| [`port`](compose_port.md) | Print the public port for a port binding |
| [`ps`](compose_ps.md) | List containers |
| [`publish`](compose_publish.md) | Publish compose application |
| [`pull`](compose_pull.md) | Pull service images |
| [`push`](compose_push.md) | Push service images |
| [`restart`](compose_restart.md) | Restart service containers |
@ -66,7 +68,7 @@ Define and run multi-container applications with Docker
## Examples
### Use `-f` to specify the name and path of one or more Compose files
Use the `-f` flag to specify the location of a Compose configuration file.
Use the `-f` flag to specify the location of a Compose [configuration file](/reference/compose-file/).
#### Specifying multiple Compose files
You can supply multiple `-f` configuration files. When you supply multiple files, Compose combines them into a single
@ -76,10 +78,10 @@ to their predecessors.
For example, consider this command line:
```console
$ docker compose -f docker-compose.yml -f docker-compose.admin.yml run backup_db
$ docker compose -f compose.yaml -f compose.admin.yaml run backup_db
```
The `docker-compose.yml` file might specify a `webapp` service.
The `compose.yaml` file might specify a `webapp` service.
```yaml
services:
@ -90,7 +92,7 @@ services:
volumes:
- "/data"
```
If the `docker-compose.admin.yml` also specifies this same service, any matching fields override the previous file.
If the `compose.admin.yaml` also specifies this same service, any matching fields override the previous file.
New values, add to the `webapp` service configuration.
```yaml
@ -205,4 +207,4 @@ $ docker compose --dry-run up --build -d
From the example above, you can see that the first step is to pull the image defined by `db` service, then build the `backend` service.
Next, the containers are created. The `db` service is started, and the `backend` and `proxy` wait until the `db` service is healthy before starting.
Dry Run mode works with almost all commands. You cannot use Dry Run mode with a command that doesn't change the state of a Compose stack such as `ps`, `ls`, `logs` for example.
Dry Run mode works with almost all commands. You cannot use Dry Run mode with a command that doesn't change the state of a Compose stack such as `ps`, `ls`, `logs` for example.

View File

@ -8,8 +8,10 @@ Publish compose application
| Name | Type | Default | Description |
|:--------------------------|:---------|:--------|:-------------------------------------------------------------------------------|
| `--dry-run` | `bool` | | Execute command in dry run mode |
| `--oci-version` | `string` | | OCI Image/Artifact specification version (automatically determined by default) |
| `--oci-version` | `string` | | OCI image/artifact specification version (automatically determined by default) |
| `--resolve-image-digests` | `bool` | | Pin image tags to digests |
| `--with-env` | `bool` | | Include environment variables in the published OCI artifact |
| `-y`, `--yes` | `bool` | | Assume "yes" as answer to all prompts |
<!---MARKER_GEN_END-->

View File

@ -4,9 +4,9 @@
Services are built once and then tagged, by default as `project-service`.
If the Compose file specifies an
[image](https://github.com/compose-spec/compose-spec/blob/master/spec.md#image) name,
[image](https://github.com/compose-spec/compose-spec/blob/main/spec.md#image) name,
the image is tagged with that name, substituting any variables beforehand. See
[variable interpolation](https://github.com/compose-spec/compose-spec/blob/master/spec.md#interpolation).
[variable interpolation](https://github.com/compose-spec/compose-spec/blob/main/spec.md#interpolation).
If you change a service's `Dockerfile` or the contents of its build directory,
run `docker compose build` to rebuild it.
@ -20,6 +20,7 @@ run `docker compose build` to rebuild it.
| `--dry-run` | `bool` | | Execute command in dry run mode |
| `-m`, `--memory` | `bytes` | `0` | Set memory limit for the build container. Not supported by BuildKit. |
| `--no-cache` | `bool` | | Do not use cache when building the image |
| `--print` | `bool` | | Print equivalent bake file |
| `--pull` | `bool` | | Always attempt to pull a newer version of the image |
| `--push` | `bool` | | Push service images |
| `-q`, `--quiet` | `bool` | | Don't print anything to STDOUT |
@ -34,9 +35,9 @@ run `docker compose build` to rebuild it.
Services are built once and then tagged, by default as `project-service`.
If the Compose file specifies an
[image](https://github.com/compose-spec/compose-spec/blob/master/spec.md#image) name,
[image](https://github.com/compose-spec/compose-spec/blob/main/spec.md#image) name,
the image is tagged with that name, substituting any variables beforehand. See
[variable interpolation](https://github.com/compose-spec/compose-spec/blob/master/spec.md#interpolation).
[variable interpolation](https://github.com/compose-spec/compose-spec/blob/main/spec.md#interpolation).
If you change a service's `Dockerfile` or the contents of its build directory,
run `docker compose build` to rebuild it.

View File

@ -0,0 +1,19 @@
# docker compose commit
<!---MARKER_GEN_START-->
Create a new image from a service container's changes
### Options
| Name | Type | Default | Description |
|:------------------|:---------|:--------|:-----------------------------------------------------------|
| `-a`, `--author` | `string` | | Author (e.g., "John Hannibal Smith <hannibal@a-team.com>") |
| `-c`, `--change` | `list` | | Apply Dockerfile instruction to the created image |
| `--dry-run` | `bool` | | Execute command in dry run mode |
| `--index` | `int` | `0` | index of the container if service has multiple replicas. |
| `-m`, `--message` | `string` | | Commit message |
| `-p`, `--pause` | `bool` | `true` | Pause container during commit |
<!---MARKER_GEN_END-->

View File

@ -19,6 +19,7 @@ the canonical format.
| `--hash` | `string` | | Print the service config hash, one per line. |
| `--images` | `bool` | | Print the image names, one per line. |
| `--no-consistency` | `bool` | | Don't check model consistency - warning: may produce invalid Compose output |
| `--no-env-resolution` | `bool` | | Don't resolve service env files |
| `--no-interpolate` | `bool` | | Don't interpolate environment variables |
| `--no-normalize` | `bool` | | Don't normalize compose model |
| `--no-path-resolution` | `bool` | | Don't resolve file paths |

View File

@ -16,6 +16,7 @@ Creates containers for a service
| `--quiet-pull` | `bool` | | Pull without printing progress information |
| `--remove-orphans` | `bool` | | Remove containers for services not defined in the Compose file |
| `--scale` | `stringArray` | | Scale SERVICE to NUM instances. Overrides the `scale` setting in the Compose file if present. |
| `-y`, `--yes` | `bool` | | Assume "yes" as answer to all prompts and run non-interactively |
<!---MARKER_GEN_END-->

View File

@ -11,7 +11,7 @@ Lists running Compose projects
| `--dry-run` | `bool` | | Execute command in dry run mode |
| `--filter` | `filter` | | Filter output based on conditions provided |
| `--format` | `string` | `table` | Format the output. Values: [table \| json] |
| `-q`, `--quiet` | `bool` | | Only display IDs |
| `-q`, `--quiet` | `bool` | | Only display project names |
<!---MARKER_GEN_END-->

View File

@ -0,0 +1,18 @@
# docker compose publish
<!---MARKER_GEN_START-->
Publish compose application
### Options
| Name | Type | Default | Description |
|:--------------------------|:---------|:--------|:-------------------------------------------------------------------------------|
| `--dry-run` | `bool` | | Execute command in dry run mode |
| `--oci-version` | `string` | | OCI image/artifact specification version (automatically determined by default) |
| `--resolve-image-digests` | `bool` | | Pin image tags to digests |
| `--with-env` | `bool` | | Include environment variables in the published OCI artifact |
| `-y`, `--yes` | `bool` | | Assume "yes" as answer to all prompts |
<!---MARKER_GEN_END-->

View File

@ -9,8 +9,8 @@ after a container is built, but before the container's command is executed) are
after restarting.
If you are looking to configure a service's restart policy, refer to
[restart](https://github.com/compose-spec/compose-spec/blob/master/spec.md#restart)
or [restart_policy](https://github.com/compose-spec/compose-spec/blob/master/deploy.md#restart_policy).
[restart](https://github.com/compose-spec/compose-spec/blob/main/spec.md#restart)
or [restart_policy](https://github.com/compose-spec/compose-spec/blob/main/deploy.md#restart_policy).
### Options
@ -33,5 +33,5 @@ after a container is built, but before the container's command is executed) are
after restarting.
If you are looking to configure a service's restart policy, refer to
[restart](https://github.com/compose-spec/compose-spec/blob/master/spec.md#restart)
or [restart_policy](https://github.com/compose-spec/compose-spec/blob/master/deploy.md#restart_policy).
[restart](https://github.com/compose-spec/compose-spec/blob/main/spec.md#restart)
or [restart_policy](https://github.com/compose-spec/compose-spec/blob/main/deploy.md#restart_policy).

View File

@ -57,29 +57,33 @@ specified in the service configuration.
### Options
| Name | Type | Default | Description |
|:------------------------|:--------------|:--------|:---------------------------------------------------------------------------------|
| `--build` | `bool` | | Build image before starting container |
| `--cap-add` | `list` | | Add Linux capabilities |
| `--cap-drop` | `list` | | Drop Linux capabilities |
| `-d`, `--detach` | `bool` | | Run container in background and print container ID |
| `--dry-run` | `bool` | | Execute command in dry run mode |
| `--entrypoint` | `string` | | Override the entrypoint of the image |
| `-e`, `--env` | `stringArray` | | Set environment variables |
| `-i`, `--interactive` | `bool` | `true` | Keep STDIN open even if not attached |
| `-l`, `--label` | `stringArray` | | Add or override a label |
| `--name` | `string` | | Assign a name to the container |
| `-T`, `--no-TTY` | `bool` | `true` | Disable pseudo-TTY allocation (default: auto-detected) |
| `--no-deps` | `bool` | | Don't start linked services |
| `-p`, `--publish` | `stringArray` | | Publish a container's port(s) to the host |
| `--quiet-pull` | `bool` | | Pull without printing progress information |
| `--remove-orphans` | `bool` | | Remove containers for services not defined in the Compose file |
| `--rm` | `bool` | | Automatically remove the container when it exits |
| `-P`, `--service-ports` | `bool` | | Run command with all service's ports enabled and mapped to the host |
| `--use-aliases` | `bool` | | Use the service's network useAliases in the network(s) the container connects to |
| `-u`, `--user` | `string` | | Run as specified username or uid |
| `-v`, `--volume` | `stringArray` | | Bind mount a volume |
| `-w`, `--workdir` | `string` | | Working directory inside the container |
| Name | Type | Default | Description |
|:------------------------|:--------------|:---------|:---------------------------------------------------------------------------------|
| `--build` | `bool` | | Build image before starting container |
| `--cap-add` | `list` | | Add Linux capabilities |
| `--cap-drop` | `list` | | Drop Linux capabilities |
| `-d`, `--detach` | `bool` | | Run container in background and print container ID |
| `--dry-run` | `bool` | | Execute command in dry run mode |
| `--entrypoint` | `string` | | Override the entrypoint of the image |
| `-e`, `--env` | `stringArray` | | Set environment variables |
| `--env-from-file` | `stringArray` | | Set environment variables from file |
| `-i`, `--interactive` | `bool` | `true` | Keep STDIN open even if not attached |
| `-l`, `--label` | `stringArray` | | Add or override a label |
| `--name` | `string` | | Assign a name to the container |
| `-T`, `--no-TTY` | `bool` | `true` | Disable pseudo-TTY allocation (default: auto-detected) |
| `--no-deps` | `bool` | | Don't start linked services |
| `-p`, `--publish` | `stringArray` | | Publish a container's port(s) to the host |
| `--pull` | `string` | `policy` | Pull image before running ("always"\|"missing"\|"never") |
| `-q`, `--quiet` | `bool` | | Don't print anything to STDOUT |
| `--quiet-build` | `bool` | | Suppress progress output from the build process |
| `--quiet-pull` | `bool` | | Pull without printing progress information |
| `--remove-orphans` | `bool` | | Remove containers for services not defined in the Compose file |
| `--rm` | `bool` | | Automatically remove the container when it exits |
| `-P`, `--service-ports` | `bool` | | Run command with all service's ports enabled and mapped to the host |
| `--use-aliases` | `bool` | | Use the service's network useAliases in the network(s) the container connects to |
| `-u`, `--user` | `string` | | Run as specified username or uid |
| `-v`, `--volume` | `stringArray` | | Bind mount a volume |
| `-w`, `--workdir` | `string` | | Working directory inside the container |
<!---MARKER_GEN_END-->

View File

@ -5,13 +5,13 @@ Display a live stream of container(s) resource usage statistics
### Options
| Name | Type | Default | Description |
|:--------------|:---------|:--------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `-a`, `--all` | `bool` | | Show all containers (default shows just running) |
| `--dry-run` | `bool` | | Execute command in dry run mode |
| `--format` | `string` | | Format output using a custom template:<br>'table': Print output in table format with column headers (default)<br>'table TEMPLATE': Print output in table format using the given Go template<br>'json': Print in JSON format<br>'TEMPLATE': Print output using the given Go template.<br>Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates |
| `--no-stream` | `bool` | | Disable streaming stats and only pull the first result |
| `--no-trunc` | `bool` | | Do not truncate output |
| Name | Type | Default | Description |
|:--------------|:---------|:--------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `-a`, `--all` | `bool` | | Show all containers (default shows just running) |
| `--dry-run` | `bool` | | Execute command in dry run mode |
| `--format` | `string` | | Format output using a custom template:<br>'table': Print output in table format with column headers (default)<br>'table TEMPLATE': Print output in table format using the given Go template<br>'json': Print in JSON format<br>'TEMPLATE': Print output using the given Go template.<br>Refer to https://docs.docker.com/engine/cli/formatting/ for more information about formatting output with templates |
| `--no-stream` | `bool` | | Disable streaming stats and only pull the first result |
| `--no-trunc` | `bool` | | Do not truncate output |
<!---MARKER_GEN_END-->

View File

@ -51,8 +51,9 @@ If the process is interrupted using `SIGINT` (ctrl + C) or `SIGTERM`, the contai
| `-t`, `--timeout` | `int` | `0` | Use this timeout in seconds for container shutdown when attached or when containers are already running |
| `--timestamps` | `bool` | | Show timestamps |
| `--wait` | `bool` | | Wait for services to be running\|healthy. Implies detached mode. |
| `--wait-timeout` | `int` | `0` | Maximum duration to wait for the project to be running\|healthy |
| `--wait-timeout` | `int` | `0` | Maximum duration in seconds to wait for the project to be running\|healthy |
| `-w`, `--watch` | `bool` | | Watch source code and rebuild/refresh containers when files are updated. |
| `-y`, `--yes` | `bool` | | Assume "yes" as answer to all prompts and run non-interactively |
<!---MARKER_GEN_END-->

View File

@ -9,7 +9,7 @@ Watch build context for service and rebuild/refresh containers when files are up
|:------------|:-------|:--------|:----------------------------------------------|
| `--dry-run` | `bool` | | Execute command in dry run mode |
| `--no-up` | `bool` | | Do not build & start services before watching |
| `--prune` | `bool` | | Prune dangling images on rebuild |
| `--prune` | `bool` | `true` | Prune dangling images on rebuild |
| `--quiet` | `bool` | | hide build output |

View File

@ -7,6 +7,7 @@ plink: docker.yaml
cname:
- docker compose attach
- docker compose build
- docker compose commit
- docker compose config
- docker compose cp
- docker compose create
@ -21,6 +22,7 @@ cname:
- docker compose pause
- docker compose port
- docker compose ps
- docker compose publish
- docker compose pull
- docker compose push
- docker compose restart
@ -39,6 +41,7 @@ cname:
clink:
- docker_compose_attach.yaml
- docker_compose_build.yaml
- docker_compose_commit.yaml
- docker_compose_config.yaml
- docker_compose_cp.yaml
- docker_compose_create.yaml
@ -53,6 +56,7 @@ clink:
- docker_compose_pause.yaml
- docker_compose_port.yaml
- docker_compose_ps.yaml
- docker_compose_publish.yaml
- docker_compose_pull.yaml
- docker_compose_push.yaml
- docker_compose_restart.yaml
@ -227,7 +231,7 @@ options:
swarm: false
examples: |-
### Use `-f` to specify the name and path of one or more Compose files
Use the `-f` flag to specify the location of a Compose configuration file.
Use the `-f` flag to specify the location of a Compose [configuration file](/reference/compose-file/).
#### Specifying multiple Compose files
You can supply multiple `-f` configuration files. When you supply multiple files, Compose combines them into a single
@ -237,10 +241,10 @@ examples: |-
For example, consider this command line:
```console
$ docker compose -f docker-compose.yml -f docker-compose.admin.yml run backup_db
$ docker compose -f compose.yaml -f compose.admin.yaml run backup_db
```
The `docker-compose.yml` file might specify a `webapp` service.
The `compose.yaml` file might specify a `webapp` service.
```yaml
services:
@ -251,7 +255,7 @@ examples: |-
volumes:
- "/data"
```
If the `docker-compose.admin.yml` also specifies this same service, any matching fields override the previous file.
If the `compose.admin.yaml` also specifies this same service, any matching fields override the previous file.
New values, add to the `webapp` service configuration.
```yaml

View File

@ -8,7 +8,7 @@ options:
- option: oci-version
value_type: string
description: |
OCI Image/Artifact specification version (automatically determined by default)
OCI image/artifact specification version (automatically determined by default)
deprecated: false
hidden: false
experimental: false
@ -25,6 +25,27 @@ options:
experimentalcli: false
kubernetes: false
swarm: false
- option: with-env
value_type: bool
default_value: "false"
description: Include environment variables in the published OCI artifact
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: "yes"
shorthand: "y"
value_type: bool
default_value: "false"
description: Assume "yes" as answer to all prompts
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
inherited_options:
- option: dry-run
value_type: bool

View File

@ -4,9 +4,9 @@ long: |-
Services are built once and then tagged, by default as `project-service`.
If the Compose file specifies an
[image](https://github.com/compose-spec/compose-spec/blob/master/spec.md#image) name,
[image](https://github.com/compose-spec/compose-spec/blob/main/spec.md#image) name,
the image is tagged with that name, substituting any variables beforehand. See
[variable interpolation](https://github.com/compose-spec/compose-spec/blob/master/spec.md#interpolation).
[variable interpolation](https://github.com/compose-spec/compose-spec/blob/main/spec.md#interpolation).
If you change a service's `Dockerfile` or the contents of its build directory,
run `docker compose build` to rebuild it.
@ -96,6 +96,16 @@ options:
experimentalcli: false
kubernetes: false
swarm: false
- option: print
value_type: bool
default_value: "false"
description: Print equivalent bake file
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: progress
value_type: string
default_value: auto

View File

@ -0,0 +1,76 @@
command: docker compose commit
short: Create a new image from a service container's changes
long: Create a new image from a service container's changes
usage: docker compose commit [OPTIONS] SERVICE [REPOSITORY[:TAG]]
pname: docker compose
plink: docker_compose.yaml
options:
- option: author
shorthand: a
value_type: string
description: Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: change
shorthand: c
value_type: list
description: Apply Dockerfile instruction to the created image
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: index
value_type: int
default_value: "0"
description: index of the container if service has multiple replicas.
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: message
shorthand: m
value_type: string
description: Commit message
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: pause
shorthand: p
value_type: bool
default_value: "true"
description: Pause container during commit
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
inherited_options:
- option: dry-run
value_type: bool
default_value: "false"
description: Execute command in dry run mode
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false

View File

@ -59,6 +59,16 @@ options:
experimentalcli: false
kubernetes: false
swarm: false
- option: no-env-resolution
value_type: bool
default_value: "false"
description: Don't resolve service env files
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: no-interpolate
value_type: bool
default_value: "false"

View File

@ -88,6 +88,17 @@ options:
experimentalcli: false
kubernetes: false
swarm: false
- option: "yes"
shorthand: "y"
value_type: bool
default_value: "false"
description: Assume "yes" as answer to all prompts and run non-interactively
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
inherited_options:
- option: dry-run
value_type: bool

View File

@ -39,7 +39,7 @@ options:
shorthand: q
value_type: bool
default_value: "false"
description: Only display IDs
description: Only display project names
deprecated: false
hidden: false
experimental: false

View File

@ -0,0 +1,66 @@
command: docker compose publish
short: Publish compose application
long: Publish compose application
usage: docker compose publish [OPTIONS] REPOSITORY[:TAG]
pname: docker compose
plink: docker_compose.yaml
options:
- option: oci-version
value_type: string
description: |
OCI image/artifact specification version (automatically determined by default)
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: resolve-image-digests
value_type: bool
default_value: "false"
description: Pin image tags to digests
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: with-env
value_type: bool
default_value: "false"
description: Include environment variables in the published OCI artifact
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: "yes"
shorthand: "y"
value_type: bool
default_value: "false"
description: Assume "yes" as answer to all prompts
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
inherited_options:
- option: dry-run
value_type: bool
default_value: "false"
description: Execute command in dry run mode
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false

View File

@ -9,8 +9,8 @@ long: |-
after restarting.
If you are looking to configure a service's restart policy, refer to
[restart](https://github.com/compose-spec/compose-spec/blob/master/spec.md#restart)
or [restart_policy](https://github.com/compose-spec/compose-spec/blob/master/deploy.md#restart_policy).
[restart](https://github.com/compose-spec/compose-spec/blob/main/spec.md#restart)
or [restart_policy](https://github.com/compose-spec/compose-spec/blob/main/deploy.md#restart_policy).
usage: docker compose restart [OPTIONS] [SERVICE...]
pname: docker compose
plink: docker_compose.yaml

View File

@ -117,6 +117,16 @@ options:
experimentalcli: false
kubernetes: false
swarm: false
- option: env-from-file
value_type: stringArray
default_value: '[]'
description: Set environment variables from file
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: interactive
shorthand: i
value_type: bool
@ -180,6 +190,37 @@ options:
experimentalcli: false
kubernetes: false
swarm: false
- option: pull
value_type: string
default_value: policy
description: Pull image before running ("always"|"missing"|"never")
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: quiet
shorthand: q
value_type: bool
default_value: "false"
description: Don't print anything to STDOUT
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: quiet-build
value_type: bool
default_value: "false"
description: Suppress progress output from the build process
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: quiet-pull
value_type: bool
default_value: "false"

View File

@ -24,7 +24,7 @@ options:
'table TEMPLATE': Print output in table format using the given Go template
'json': Print in JSON format
'TEMPLATE': Print output using the given Go template.
Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates
Refer to https://docs.docker.com/engine/cli/formatting/ for more information about formatting output with templates
deprecated: false
hidden: false
experimental: false

View File

@ -289,7 +289,8 @@ options:
- option: wait-timeout
value_type: int
default_value: "0"
description: Maximum duration to wait for the project to be running|healthy
description: |
Maximum duration in seconds to wait for the project to be running|healthy
deprecated: false
hidden: false
experimental: false
@ -308,6 +309,17 @@ options:
experimentalcli: false
kubernetes: false
swarm: false
- option: "yes"
shorthand: "y"
value_type: bool
default_value: "false"
description: Assume "yes" as answer to all prompts and run non-interactively
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
inherited_options:
- option: dry-run
value_type: bool

View File

@ -19,7 +19,7 @@ options:
swarm: false
- option: prune
value_type: bool
default_value: "false"
default_value: "true"
description: Prune dangling images on rebuild
deprecated: false
hidden: false

193
go.mod
View File

@ -1,94 +1,97 @@
module github.com/docker/compose/v2
go 1.22.0
go 1.23.8
require (
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e
github.com/Microsoft/go-winio v0.6.2
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/buger/goterm v1.0.4
github.com/compose-spec/compose-go/v2 v2.4.4
github.com/containerd/containerd v1.7.23
github.com/containerd/platforms v0.2.1
github.com/davecgh/go-spew v1.1.1
github.com/compose-spec/compose-go/v2 v2.5.0
github.com/containerd/containerd/v2 v2.0.4
github.com/containerd/platforms v1.0.0-rc.1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/distribution/reference v0.6.0
github.com/docker/buildx v0.17.1
github.com/docker/cli v27.3.2-0.20241008150905-cb3048fbebb1+incompatible
github.com/docker/cli-docs-tool v0.8.0
github.com/docker/docker v27.3.1+incompatible
github.com/docker/buildx v0.22.0
github.com/docker/cli v28.0.4+incompatible
github.com/docker/cli-docs-tool v0.9.0
github.com/docker/docker v28.0.4+incompatible
github.com/docker/go-connections v0.5.0
github.com/docker/go-units v0.5.0
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203
github.com/fsnotify/fsevents v0.2.0
github.com/google/go-cmp v0.6.0
github.com/google/go-cmp v0.7.0
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-version v1.7.0
github.com/jonboulle/clockwork v0.4.0
github.com/jonboulle/clockwork v0.5.0
github.com/mattn/go-shellwords v1.0.12
github.com/mitchellh/go-ps v1.0.0
github.com/mitchellh/mapstructure v1.5.0
github.com/moby/buildkit v0.16.0
github.com/moby/buildkit v0.20.1
github.com/moby/patternmatcher v0.6.0
github.com/moby/term v0.5.0
github.com/moby/term v0.5.2
github.com/morikuni/aec v1.0.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.0
github.com/otiai10/copy v1.14.0
github.com/opencontainers/image-spec v1.1.1
github.com/otiai10/copy v1.14.1
github.com/r3labs/sse v0.0.0-20210224172625-26fe804710bc
github.com/sirupsen/logrus v1.9.3
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0
github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.6
github.com/stretchr/testify v1.10.0
github.com/theupdateframework/notary v0.7.0
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1
go.opentelemetry.io/otel v1.21.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0
go.opentelemetry.io/otel/metric v1.21.0
go.opentelemetry.io/otel/sdk v1.21.0
go.opentelemetry.io/otel/trace v1.21.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0
go.opentelemetry.io/otel v1.34.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0
go.opentelemetry.io/otel/metric v1.34.0
go.opentelemetry.io/otel/sdk v1.34.0
go.opentelemetry.io/otel/trace v1.34.0
go.uber.org/goleak v1.3.0
go.uber.org/mock v0.5.0
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948
golang.org/x/sync v0.8.0
golang.org/x/sys v0.26.0
google.golang.org/grpc v1.67.1
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f
golang.org/x/sync v0.13.0
golang.org/x/sys v0.32.0
google.golang.org/grpc v1.71.0
gopkg.in/yaml.v3 v3.0.1
gotest.tools/v3 v3.5.1
tags.cncf.io/container-device-interface v0.8.0
gotest.tools/v3 v3.5.2
tags.cncf.io/container-device-interface v1.0.1
)
require (
dario.cat/mergo v1.0.1 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect
github.com/aws/aws-sdk-go-v2/config v1.26.6 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect
github.com/aws/smithy-go v1.19.0 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
github.com/aws/smithy-go v1.20.3 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/containerd/containerd/api v1.7.19 // indirect
github.com/containerd/continuity v0.4.3 // indirect
github.com/containerd/errdefs v0.3.0 // indirect
github.com/containerd/containerd/api v1.8.0 // indirect
github.com/containerd/continuity v0.4.5 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/ttrpc v1.2.5 // indirect
github.com/containerd/typeurl/v2 v2.2.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/containerd/ttrpc v1.2.7 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.2 // indirect
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
@ -96,15 +99,16 @@ require (
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fvbommel/sortorder v1.1.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/gofuzz v1.2.0 // indirect
@ -112,27 +116,29 @@ require (
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/in-toto/in-toto-golang v0.5.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/magiconair/properties v1.8.9 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/moby/spdystream v0.4.0 // indirect
github.com/moby/sys/capability v0.4.0 // indirect
github.com/moby/sys/mountinfo v0.7.2 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/signal v0.7.1 // indirect
@ -143,52 +149,61 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/otiai10/mint v1.6.3 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.17.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/tonistiigi/fsutil v0.0.0-20240424095704-91a3fc46842c // indirect
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205 // indirect
github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a // indirect
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 // indirect
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.21.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/oauth2 v0.22.0 // indirect
golang.org/x/term v0.23.0 // indirect
golang.org/x/text v0.17.0 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
github.com/zclconf/go-cty v1.16.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/oauth2 v0.25.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.6.0 // indirect
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
google.golang.org/protobuf v1.34.2 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
google.golang.org/protobuf v1.36.4 // indirect
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/api v0.29.2 // indirect
k8s.io/apimachinery v0.29.2 // indirect
k8s.io/client-go v0.29.2 // indirect
k8s.io/klog/v2 v2.110.1 // indirect
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
k8s.io/api v0.31.2 // indirect
k8s.io/apimachinery v0.31.2 // indirect
k8s.io/client-go v0.31.2 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
replace github.com/compose-spec/compose-go/v2 => github.com/glours/compose-go/v2 v2.0.0-20250403082600-80aa75f06535

459
go.sum
View File

@ -1,25 +1,23 @@
cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM=
cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 h1:59MxjQVfjXsBpLy+dbd2/ELV5ofnUkUZBvWSC85sheA=
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0/go.mod h1:OahwfttHWG6eJ0clwcfBAHoDI6X/LV/15hx/wlMZSrU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
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/go.mod h1:gCLVsLfv1egrcZu+GoJATN5ts75F2s62ih/457eWzOw=
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
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/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e h1:rd4bOvKmDIx0WeTv9Qz+hghsgyjikFiPrseXHlKepO0=
github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e/go.mod h1:blbwPQh4DTlCZEfk1BLU4oMIhLda2U+A840Uag9DsZw=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZNZQ=
github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU=
github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg=
github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
@ -31,34 +29,36 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc=
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU=
github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
github.com/aws/aws-sdk-go-v2/config v1.26.6 h1:Z/7w9bUqlRI0FFQpetVuFYEsjzE3h7fpU6HuGmfPL/o=
github.com/aws/aws-sdk-go-v2/config v1.26.6/go.mod h1:uKU6cnDmYCvJ+pxO9S4cWDb2yWWIH5hra+32hVh1MI4=
github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5gbadPbWdV1WcAddK8=
github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw=
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 h1:n3GDfwqF2tzEkXlv5cuy4iy7LpKDtqDMcNLfZDu9rls=
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino=
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow=
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 h1:QPMJf+Jw8E1l7zqhZmMlFw6w1NmfkfiSK8mS4zOx3BA=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8=
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0=
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U=
github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY=
github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90=
github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg=
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI=
github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII=
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM=
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw=
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE=
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ=
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/beorn7/perks v0.0.0-20150223135152-b965b613227f/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@ -75,68 +75,69 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembj
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e85keuznYcH5rqI438v41pKcBl4ZxQ=
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20 h1:N+3sFI5GUjRKBi+i0TxYVST9h4Ie192jJWpHvthBBgg=
github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
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/compose-spec/compose-go/v2 v2.4.4 h1:cvHBl5Jf1iNBmRrZCICmHvaoskYc1etTPEMLKVwokAY=
github.com/compose-spec/compose-go/v2 v2.4.4/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
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/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ=
github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw=
github.com/containerd/containerd/api v1.7.19 h1:VWbJL+8Ap4Ju2mx9c9qS1uFSB1OVYr5JJrW2yT5vFoA=
github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig=
github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8=
github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4=
github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
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/v2 v2.0.4 h1:+r7yJMwhTfMm3CDyiBjMBQO8a9CTBxL2Bg/JtqtIwB8=
github.com/containerd/containerd/v2 v2.0.4/go.mod h1:5j9QUUaV/cy9ZeAx4S+8n9ffpf+iYnEj4jiExgcbuLY=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
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/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY=
github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/nydus-snapshotter v0.14.0 h1:6/eAi6d7MjaeLLuMO8Udfe5GVsDudmrDNO4SGETMBco=
github.com/containerd/nydus-snapshotter v0.14.0/go.mod h1:TT4jv2SnIDxEBu4H2YOvWQHPOap031ydTaHTuvc5VQk=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/containerd/stargz-snapshotter v0.15.1 h1:fpsP4kf/Z4n2EYnU0WT8ZCE3eiKDwikDhL6VwxIlgeA=
github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU=
github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk=
github.com/containerd/ttrpc v1.2.5 h1:IFckT1EFQoFBMG4c3sMdT8EP3/aKfumK1msY+Ze4oLU=
github.com/containerd/ttrpc v1.2.5/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
github.com/containerd/typeurl/v2 v2.2.0 h1:6NBDbQzr7I5LHgp34xAXYF5DOTQDn05X58lsPEmzLso=
github.com/containerd/typeurl/v2 v2.2.0/go.mod h1:8XOOxnyatxSWuG8OfsZXVnAF4iZfedjS/8UHSPJnX4g=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/containerd/nydus-snapshotter v0.15.0 h1:RqZRs1GPeM6T3wmuxJV9u+2Rg4YETVMwTmiDeX+iWC8=
github.com/containerd/nydus-snapshotter v0.15.0/go.mod h1:biq0ijpeZe0I5yZFSJyHzFSjjRZQ7P7y/OuHyd7hYOw=
github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E=
github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=
github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y=
github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8=
github.com/containerd/stargz-snapshotter v0.16.3 h1:zbQMm8dRuPHEOD4OqAYGajJJUwCeUzt4j7w9Iaw58u4=
github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8=
github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU=
github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ=
github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
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.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.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/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/docker/buildx v0.17.1 h1:9ob2jGp4+W9PxWw68GsoNFp+eYFc7eUoRL9VljLCSM4=
github.com/docker/buildx v0.17.1/go.mod h1:kJOhOhS47LRvrLFRulFiO5SE6VJf54yYMn7DzjgO5W0=
github.com/docker/cli v27.3.2-0.20241008150905-cb3048fbebb1+incompatible h1:fJ3SzYiebfWoas3qOJpmRsNrDL2w1XIpPFywSLFhhfk=
github.com/docker/cli v27.3.2-0.20241008150905-cb3048fbebb1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli-docs-tool v0.8.0 h1:YcDWl7rQJC3lJ7WVZRwSs3bc9nka97QLWfyJQli8yJU=
github.com/docker/cli-docs-tool v0.8.0/go.mod h1:8TQQ3E7mOXoYUs811LiPdUnAhXrcVsBIrW21a5pUbdk=
github.com/docker/buildx v0.22.0 h1:pGTcGZa+kxpYUlM/6ACsp1hXhkEDulz++RNXPdE8Afk=
github.com/docker/buildx v0.22.0/go.mod h1:ThbnUe4kNiStlq6cLXruElyEdSTdPL3k/QerNUmPvHE=
github.com/docker/cli v28.0.4+incompatible h1:pBJSJeNd9QeIWPjRcV91RVJihd/TXB77q1ef64XEu4A=
github.com/docker/cli v28.0.4+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/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.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/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok=
github.com/docker/docker v28.0.4+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/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
@ -144,8 +145,6 @@ github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
@ -158,8 +157,6 @@ github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJ
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM=
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
@ -168,39 +165,41 @@ github.com/fsnotify/fsevents v0.2.0/go.mod h1:B3eEk39i4hz8y1zaWS/wPrAP4O6wkIl7HQ
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw=
github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/glours/compose-go/v2 v2.0.0-20250403082600-80aa75f06535 h1:S/P6v3QxsMpkKn+2OSMPNkfSkadSjSHoMGAc/eBZgMU=
github.com/glours/compose-go/v2 v2.0.0-20250403082600-80aa75f06535/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-sql-driver/mysql v1.3.0 h1:pgwjLi/dvffoP9aabwkT3AKpXQM93QARkjFhDDqC1UE=
github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc=
github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0=
github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY=
github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -215,13 +214,13 @@ github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvR
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@ -229,11 +228,10 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@ -255,13 +253,15 @@ github.com/in-toto/in-toto-golang v0.5.0/go.mod h1:/Rq0IZHLV7Ku5gielPT4wPHJfH1Gd
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf h1:FtEj8sfIcaaBfAKrE1Cwb61YDtYq9JxChK1c7AKce7s=
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf/go.mod h1:yrqSXGoD/4EKfF26AOGzscPOgTTJcyAwM2rpixWT+t4=
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8 h1:CZkYfurY6KGhVtlalI4QwQ6T0Cu6iuY3e0x5RLu96WE=
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d h1:jRQLvyVGL+iVtDElaEIDdKwpPqUIZJfzkNLV34htpEc=
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@ -274,8 +274,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@ -287,9 +287,12 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v1.5.3 h1:C8fxWnhYyME3n0klPOhVM7PtYUB3eV1W3DeFmN3j53Y=
github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@ -297,16 +300,14 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
@ -319,16 +320,18 @@ 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 v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/buildkit v0.16.0 h1:wOVBj1o5YNVad/txPQNXUXdelm7Hs/i0PUFjzbK0VKE=
github.com/moby/buildkit v0.16.0/go.mod h1:Xqx/5GlrqE1yIRORk0NSCVDFpQAU1WjlT6KHYZdisIQ=
github.com/moby/buildkit v0.20.1 h1:sT0ZXhhNo5rVbMcYfgttma3TdUHfO5JjFA0UAL8p9fY=
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/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/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8=
github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk=
github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I=
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
@ -341,8 +344,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/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
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.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
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-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -361,52 +364,55 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
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/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
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/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
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/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.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
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/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.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.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@ -417,8 +423,8 @@ github.com/r3labs/sse v0.0.0-20210224172625-26fe804710bc h1:zAsgcP8MhzAbhMnB1QQ2
github.com/r3labs/sse v0.0.0-20210224172625-26fe804710bc/go.mod h1:S8xSOnV3CgpNrWd0GQ/OoQfMtlg2uPRSuTzcSGrzwK8=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE=
@ -439,13 +445,13 @@ github.com/spdx/tools-golang v0.5.3/go.mod h1:/ETOahiAo96Ob0/RAIBmFZw6XN0yTnyr/u
github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94 h1:JmfC365KywYwHB946TTiQWEb8kqPY+pybPLoGE9GgVk=
github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431 h1:XTHrT015sxHyJ5FnQ0AeemSspZWaDq7DoTRW0EVsDCE=
github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.0/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c h1:2EejZtjFjKJGk71ANb+wtFK5EjUzUkEM3R0xnp559xg=
github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -462,22 +468,26 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 h1:QB54BJwA6x8QU9nHY3xJSZR2kX9bgpZekRKGkLTmEXA=
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375/go.mod h1:xRroudyp5iVtxKqZCrA6n2TLFRBf8bmnjr1UD4x+z7g=
github.com/tonistiigi/fsutil v0.0.0-20240424095704-91a3fc46842c h1:+6wg/4ORAbnSoGDzg2Q1i3CeMcT/jjhye/ZfnBHy7/M=
github.com/tonistiigi/fsutil v0.0.0-20240424095704-91a3fc46842c/go.mod h1:vbbYqJlnswsbJqWUcJN8fKtBhnEgldDrcagTgnBVKKM=
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205 h1:eUk79E1w8yMtXeHSzjKorxuC8qJOnyXQnLaJehxpJaI=
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY=
github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a h1:EfGw4G0x/8qXWgtcZ6KVaPS+wpWOQMaypczzP8ojkMY=
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/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE=
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/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw=
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc=
github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts=
github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk=
github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs=
github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@ -485,39 +495,45 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zclconf/go-cty v1.16.0 h1:xPKEhst+BW5D0wxebMZkxgapvOE/dw7bFTlgSc9nD6w=
github.com/zclconf/go-cty v1.16.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 h1:gbhw/u49SS3gkPWiYweQNJGm/uJN5GkI/FrosxSHT7A=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1/go.mod h1:GnOaBaFQ2we3b9AGWJpsBa7v1S5RlQzlC3O7dRMxZhM=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo=
go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 h1:jd0+5t/YynESZqsSyPz+7PAFdEop0dlN0+PkyHYo8oI=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0/go.mod h1:U707O40ee1FpQGyhvqnzmCJm1Wh6OX6GGBVn0E6Uyyk=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 h1:bflGWrfYyuulcdxf14V6n9+CoQcu5SAAdHmDPAJnlps=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0/go.mod h1:qcTO4xHAxZLaLxPd60TdE88rxtItPHgHWqOhOGRr0as=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 h1:digkEZCJWobwBqMwC0cwCq8/wkkRy/OowZg5OArWZrM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0/go.mod h1:/OpE/y70qVkndM0TrxT4KBoN3RsFZP0QaofcfYrj76I=
go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4=
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
go.opentelemetry.io/otel/sdk/metric v1.21.0 h1:smhI5oD714d6jHE6Tie36fPx4WDFIg+Y6RfAY4ICcR0=
go.opentelemetry.io/otel/sdk/metric v1.21.0/go.mod h1:FJ8RAsoPGv/wYMgBdUJXOm+6pzFY3YdljnXtv1SBE8Q=
go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 h1:yMkBS9yViCc7U7yeLzJPM2XizlfdVvBRSmsQDWu6qc0=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0/go.mod h1:n8MR6/liuGB5EmTETUBeU5ZgqMOlqKRxUaqPQBOANZ8=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 h1:4BZHA+B1wXEQoGNHxW8mURaLhcdGwvRnmhGbm+odRbc=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0/go.mod h1:3qi2EEwMgB4xnKgPLqsDP3j9qxnHDZeHsnAxfjQqTko=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 h1:FZ6ei8GFW7kyPYdxJaV2rgI6M+4tvZzhYsQ2wgyVC08=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0/go.mod h1:MdEu/mC6j3D+tTEfvI15b5Ci2Fn7NneJ71YMoiS3tpI=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0 h1:ZsXq73BERAiNuuFXYqP4MR5hBrjXfMGSO+Cx7qoOZiM=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0/go.mod h1:hg1zaDMpyZJuUzjFxFsRYBoccE86tM9Uf4IqNMUxvrY=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy03iVTXP6ffeN2iXrxfGsZGCjVx0/4KlizjyBwU=
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/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
@ -530,15 +546,15 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA=
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
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.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -549,10 +565,10 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -560,8 +576,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -582,19 +598,20 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -602,23 +619,21 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
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-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-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8=
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24=
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y=
@ -633,6 +648,8 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1 h1:d4KQkxAaAiRY2h5Zqis161Pv91A37uZyJOx73duwUwM=
gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1/go.mod h1:WbjuEoo1oadwzQ4apSDU+JTvmllEHtsNHS6y7vFc7iw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
@ -645,25 +662,25 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A=
k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0=
k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8=
k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU=
k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg=
k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA=
k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk=
k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=
k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=
k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
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=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
tags.cncf.io/container-device-interface v1.0.1 h1:KqQDr4vIlxwfYh0Ed/uJGVgX+CHAkahrgabg6Q8GYxc=
tags.cncf.io/container-device-interface v1.0.1/go.mod h1:JojJIOeW3hNbcnOH2q0NrWNha/JuHoDZcmYxAZwb2i0=

View File

@ -25,7 +25,7 @@ import (
"path/filepath"
"time"
pusherrors "github.com/containerd/containerd/remotes/errors"
pusherrors "github.com/containerd/containerd/v2/core/remotes/errors"
"github.com/distribution/reference"
"github.com/docker/buildx/util/imagetools"
"github.com/docker/compose/v2/pkg/api"
@ -54,6 +54,8 @@ const (
// > an artifactType field, and tooling to work with artifacts should
// > fallback to the config.mediaType value.
ComposeEmptyConfigMediaType = "application/vnd.docker.compose.config.empty.v1+json"
// ComposeEnvFileMediaType is the media type for each Env File layer in the image manifest.
ComposeEnvFileMediaType = "application/vnd.docker.compose.envfile"
)
// clientAuthStatusCodes are client (4xx) errors that are authentication
@ -81,6 +83,18 @@ func DescriptorForComposeFile(path string, content []byte) v1.Descriptor {
}
}
func DescriptorForEnvFile(path string, content []byte) v1.Descriptor {
return v1.Descriptor{
MediaType: ComposeEnvFileMediaType,
Digest: digest.FromString(string(content)),
Size: int64(len(content)),
Annotations: map[string]string{
"com.docker.compose.version": api.ComposeVersion,
"com.docker.compose.envfile": filepath.Base(path),
},
}
}
func PushManifest(
ctx context.Context,
resolver *imagetools.Resolver,
@ -88,6 +102,10 @@ func PushManifest(
layers []Pushable,
ociVersion api.OCIVersion,
) error {
// Check if we need an extra empty layer for the manifest config
if ociVersion == api.OCIVersion1_1 || ociVersion == "" {
layers = append(layers, Pushable{Descriptor: v1.DescriptorEmptyJSON, Data: []byte("{}")})
}
// prepare to push the manifest by pushing the layers
layerDescriptors := make([]v1.Descriptor, len(layers))
for i := range layers {

View File

@ -16,8 +16,6 @@ package sync
import (
"context"
"github.com/compose-spec/compose-go/v2/types"
)
// PathMapping contains the Compose service and modified host system path.
@ -38,5 +36,5 @@ type PathMapping struct {
}
type Syncer interface {
Sync(ctx context.Context, service types.ServiceConfig, paths []PathMapping) error
Sync(ctx context.Context, service string, paths []*PathMapping) error
}

View File

@ -32,8 +32,7 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/compose-spec/compose-go/v2/types"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/archive"
)
@ -44,7 +43,7 @@ type archiveEntry struct {
}
type LowLevelClient interface {
ContainersForService(ctx context.Context, projectName string, serviceName string) ([]moby.Container, error)
ContainersForService(ctx context.Context, projectName string, serviceName string) ([]container.Summary, error)
Exec(ctx context.Context, containerID string, cmd []string, in io.Reader) error
Untar(ctx context.Context, id string, reader io.ReadCloser) error
@ -65,8 +64,8 @@ func NewTar(projectName string, client LowLevelClient) *Tar {
}
}
func (t *Tar) Sync(ctx context.Context, service types.ServiceConfig, paths []PathMapping) error {
containers, err := t.client.ContainersForService(ctx, t.projectName, service.Name)
func (t *Tar) Sync(ctx context.Context, service string, paths []*PathMapping) error {
containers, err := t.client.ContainersForService(ctx, t.projectName, service)
if err != nil {
return err
}
@ -77,7 +76,7 @@ func (t *Tar) Sync(ctx context.Context, service types.ServiceConfig, paths []Pat
if _, err := os.Stat(p.HostPath); err != nil && errors.Is(err, fs.ErrNotExist) {
pathsToDelete = append(pathsToDelete, p.ContainerPath)
} else {
pathsToCopy = append(pathsToCopy, p)
pathsToCopy = append(pathsToCopy, *p)
}
}

View File

@ -25,7 +25,7 @@ import (
"time"
"github.com/compose-spec/compose-go/v2/types"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)
@ -140,15 +140,15 @@ func ServiceOptions(service types.ServiceConfig) SpanOptions {
// For convenience, it's returned as a SpanOptions object to allow it to be
// passed directly to the wrapping helper methods in this package such as
// SpanWrapFunc.
func ContainerOptions(container moby.Container) SpanOptions {
func ContainerOptions(ctr container.Summary) SpanOptions {
attrs := []attribute.KeyValue{
attribute.String("container.id", container.ID),
attribute.String("container.image", container.Image),
unixTimeAttr("container.created_at", container.Created),
attribute.String("container.id", ctr.ID),
attribute.String("container.image", ctr.Image),
unixTimeAttr("container.created_at", ctr.Created),
}
if len(container.Names) != 0 {
attrs = append(attrs, attribute.String("container.name", strings.TrimPrefix(container.Names[0], "/")))
if len(ctr.Names) != 0 {
attrs = append(attrs, attribute.String("container.name", strings.TrimPrefix(ctr.Names[0], "/")))
}
return []trace.SpanStartEventOption{

View File

@ -16,7 +16,5 @@
package internal
var (
// Version is the version of the CLI injected in compilation time
Version = "dev"
)
// Version is the version of the CLI injected in compilation time
var Version = "dev"

View File

@ -23,6 +23,7 @@ import (
"time"
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/opts"
"github.com/docker/compose/v2/pkg/utils"
)
@ -92,6 +93,8 @@ type Service interface {
Scale(ctx context.Context, project *types.Project, options ScaleOptions) error
// Export a service container's filesystem as a tar archive
Export(ctx context.Context, projectName string, options ExportOptions) error
// Create a new image from a service container's changes
Commit(ctx context.Context, projectName string, options CommitOptions) error
// Generate generates a Compose Project from existing containers
Generate(ctx context.Context, options GenerateOptions) (*types.Project, error)
}
@ -152,6 +155,8 @@ type BuildOptions struct {
Memory int64
// Builder name passed in the command line
Builder string
// Print don't actually run builder but print equivalent build config
Print bool
}
// Apply mutates project according to build options
@ -204,6 +209,8 @@ type CreateOptions struct {
Timeout *time.Duration
// QuietPull makes the pulling process quiet
QuietPull bool
// AssumeYes assume "yes" as answer to all prompts and run non-interactively
AssumeYes bool
}
// StartOptions group options of the Start API
@ -415,6 +422,8 @@ const (
// PublishOptions group options of the Publish API
type PublishOptions struct {
ResolveImageDigests bool
WithEnvironment bool
AssumeYes bool
OCIVersion OCIVersion
}
@ -426,7 +435,6 @@ func (e Event) String() string {
attr = append(attr, fmt.Sprintf("%s=%s", k, v))
}
return fmt.Sprintf("%s container %s %s (%s)\n", t, e.Status, e.Container, strings.Join(attr, ", "))
}
// ListOptions group options of the ls API
@ -517,6 +525,8 @@ type ContainerProcSummary struct {
Name string
Processes [][]string
Titles []string
Service string
Replica string
}
// ImageSummary holds container image description
@ -526,6 +536,7 @@ type ImageSummary struct {
Repository string
Tag string
Size int64
LastTagTime time.Time
}
// ServiceStatus hold status about a service
@ -565,6 +576,19 @@ type ExportOptions struct {
Output string
}
// CommitOptions group options of the Commit API
type CommitOptions struct {
Service string
Reference string
Pause bool
Comment string
Author string
Changes opts.ListOpts
Index int
}
type GenerateOptions struct {
// ProjectName to set in the Compose file
ProjectName string

View File

@ -60,7 +60,7 @@ type DryRunKey struct{}
// DryRunClient implements APIClient by delegating to implementation functions. This allows lazy init and per-method overrides
type DryRunClient struct {
apiClient client.APIClient
containers []moby.Container
containers []containerType.Summary
execs sync.Map
resolver *imagetools.Resolver
}
@ -82,7 +82,7 @@ func NewDryRunClient(apiClient client.APIClient, cli command.Cli) (*DryRunClient
}
return &DryRunClient{
apiClient: apiClient,
containers: []moby.Container{},
containers: []containerType.Summary{},
execs: sync.Map{},
resolver: imagetools.New(configFile),
}, nil
@ -101,8 +101,9 @@ func (d *DryRunClient) ContainerAttach(ctx context.Context, container string, op
}
func (d *DryRunClient) ContainerCreate(ctx context.Context, config *containerType.Config, hostConfig *containerType.HostConfig,
networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string) (containerType.CreateResponse, error) {
d.containers = append(d.containers, moby.Container{
networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string,
) (containerType.CreateResponse, error) {
d.containers = append(d.containers, containerType.Summary{
ID: containerName,
Names: []string{containerName},
Labels: config.Labels,
@ -114,7 +115,7 @@ func (d *DryRunClient) ContainerCreate(ctx context.Context, config *containerTyp
return containerType.CreateResponse{ID: containerName}, nil
}
func (d *DryRunClient) ContainerInspect(ctx context.Context, container string) (moby.ContainerJSON, error) {
func (d *DryRunClient) ContainerInspect(ctx context.Context, container string) (containerType.InspectResponse, error) {
containerJSON, err := d.apiClient.ContainerInspect(ctx, container)
if err != nil {
id := "dryRunId"
@ -123,20 +124,20 @@ func (d *DryRunClient) ContainerInspect(ctx context.Context, container string) (
id = container
}
}
return moby.ContainerJSON{
ContainerJSONBase: &moby.ContainerJSONBase{
return containerType.InspectResponse{
ContainerJSONBase: &containerType.ContainerJSONBase{
ID: id,
Name: container,
State: &moby.ContainerState{
State: &containerType.State{
Status: "running", // needed for --wait option
Health: &moby.Health{
Status: moby.Healthy, // needed for healthcheck control
Health: &containerType.Health{
Status: containerType.Healthy, // needed for healthcheck control
},
},
},
Mounts: nil,
Config: &containerType.Config{},
NetworkSettings: &moby.NetworkSettings{},
NetworkSettings: &containerType.NetworkSettings{},
}, nil
}
return containerJSON, err
@ -146,7 +147,7 @@ func (d *DryRunClient) ContainerKill(ctx context.Context, container, signal stri
return nil
}
func (d *DryRunClient) ContainerList(ctx context.Context, options containerType.ListOptions) ([]moby.Container, error) {
func (d *DryRunClient) ContainerList(ctx context.Context, options containerType.ListOptions) ([]containerType.Summary, error) {
caller := getCallingFunction()
switch caller {
case "start":
@ -221,15 +222,24 @@ func (d *DryRunClient) ImageBuild(ctx context.Context, reader io.Reader, options
}, nil
}
func (d *DryRunClient) ImageInspectWithRaw(ctx context.Context, imageName string) (moby.ImageInspect, []byte, error) {
func (d *DryRunClient) ImageInspect(ctx context.Context, imageName string, options ...client.ImageInspectOption) (image.InspectResponse, error) {
caller := getCallingFunction()
switch caller {
case "pullServiceImage", "buildContainerVolumes":
return moby.ImageInspect{ID: "dryRunId"}, nil, nil
return image.InspectResponse{ID: "dryRunId"}, nil
default:
return d.apiClient.ImageInspectWithRaw(ctx, imageName)
return d.apiClient.ImageInspect(ctx, imageName, options...)
}
}
// Deprecated: Use [DryRunClient.ImageInspect] instead; raw response can be obtained by [client.ImageInspectWithRawResponse] option.
func (d *DryRunClient) ImageInspectWithRaw(ctx context.Context, imageName string) (image.InspectResponse, []byte, error) {
var buf bytes.Buffer
resp, err := d.ImageInspect(ctx, imageName, client.ImageInspectWithRawResponse(&buf))
if err != nil {
return image.InspectResponse{}, nil, err
}
return resp, buf.Bytes(), err
}
func (d *DryRunClient) ImagePull(ctx context.Context, ref string, options image.PullOptions) (io.ReadCloser, error) {
@ -299,7 +309,7 @@ func (d *DryRunClient) VolumeRemove(ctx context.Context, volumeID string, force
return nil
}
func (d *DryRunClient) ContainerExecCreate(ctx context.Context, container string, config containerType.ExecOptions) (moby.IDResponse, error) {
func (d *DryRunClient) ContainerExecCreate(ctx context.Context, container string, config containerType.ExecOptions) (containerType.ExecCreateResponse, error) {
b := make([]byte, 32)
_, _ = rand.Read(b)
id := fmt.Sprintf("%x", b)
@ -307,7 +317,7 @@ func (d *DryRunClient) ContainerExecCreate(ctx context.Context, container string
container: container,
command: config.Cmd,
})
return moby.IDResponse{
return containerType.ExecCreateResponse{
ID: id,
}, nil
}
@ -344,7 +354,7 @@ func (d *DryRunClient) ConfigUpdate(ctx context.Context, id string, version swar
return d.apiClient.ConfigUpdate(ctx, id, version, config)
}
func (d *DryRunClient) ContainerCommit(ctx context.Context, container string, options containerType.CommitOptions) (moby.IDResponse, error) {
func (d *DryRunClient) ContainerCommit(ctx context.Context, container string, options containerType.CommitOptions) (containerType.CommitResponse, error) {
return d.apiClient.ContainerCommit(ctx, container, options)
}
@ -368,7 +378,7 @@ func (d *DryRunClient) ContainerExport(ctx context.Context, container string) (i
return d.apiClient.ContainerExport(ctx, container)
}
func (d *DryRunClient) ContainerInspectWithRaw(ctx context.Context, container string, getSize bool) (moby.ContainerJSON, []byte, error) {
func (d *DryRunClient) ContainerInspectWithRaw(ctx context.Context, container string, getSize bool) (containerType.InspectResponse, []byte, error) {
return d.apiClient.ContainerInspectWithRaw(ctx, container, getSize)
}
@ -392,11 +402,11 @@ func (d *DryRunClient) ContainerStatsOneShot(ctx context.Context, container stri
return d.apiClient.ContainerStatsOneShot(ctx, container)
}
func (d *DryRunClient) ContainerTop(ctx context.Context, container string, arguments []string) (containerType.ContainerTopOKBody, error) {
func (d *DryRunClient) ContainerTop(ctx context.Context, container string, arguments []string) (containerType.TopResponse, error) {
return d.apiClient.ContainerTop(ctx, container, arguments)
}
func (d *DryRunClient) ContainerUpdate(ctx context.Context, container string, updateConfig containerType.UpdateConfig) (containerType.ContainerUpdateOKBody, error) {
func (d *DryRunClient) ContainerUpdate(ctx context.Context, container string, updateConfig containerType.UpdateConfig) (containerType.UpdateResponse, error) {
return d.apiClient.ContainerUpdate(ctx, container, updateConfig)
}
@ -424,8 +434,8 @@ func (d *DryRunClient) ImageCreate(ctx context.Context, parentReference string,
return d.apiClient.ImageCreate(ctx, parentReference, options)
}
func (d *DryRunClient) ImageHistory(ctx context.Context, imageName string) ([]image.HistoryResponseItem, error) {
return d.apiClient.ImageHistory(ctx, imageName)
func (d *DryRunClient) ImageHistory(ctx context.Context, imageName string, options ...client.ImageHistoryOption) ([]image.HistoryResponseItem, error) {
return d.apiClient.ImageHistory(ctx, imageName, options...)
}
func (d *DryRunClient) ImageImport(ctx context.Context, source image.ImportSource, ref string, options image.ImportOptions) (io.ReadCloser, error) {
@ -436,16 +446,16 @@ func (d *DryRunClient) ImageList(ctx context.Context, options image.ListOptions)
return d.apiClient.ImageList(ctx, options)
}
func (d *DryRunClient) ImageLoad(ctx context.Context, input io.Reader, quiet bool) (image.LoadResponse, error) {
return d.apiClient.ImageLoad(ctx, input, quiet)
func (d *DryRunClient) ImageLoad(ctx context.Context, input io.Reader, options ...client.ImageLoadOption) (image.LoadResponse, error) {
return d.apiClient.ImageLoad(ctx, input, options...)
}
func (d *DryRunClient) ImageSearch(ctx context.Context, term string, options registry.SearchOptions) ([]registry.SearchResult, error) {
return d.apiClient.ImageSearch(ctx, term, options)
}
func (d *DryRunClient) ImageSave(ctx context.Context, images []string) (io.ReadCloser, error) {
return d.apiClient.ImageSave(ctx, images)
func (d *DryRunClient) ImageSave(ctx context.Context, images []string, options ...client.ImageSaveOption) (io.ReadCloser, error) {
return d.apiClient.ImageSave(ctx, images, options...)
}
func (d *DryRunClient) ImageTag(ctx context.Context, imageName, ref string) error {

View File

@ -25,7 +25,6 @@ import (
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/streams"
moby "github.com/docker/docker/api/types"
containerType "github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/stdcopy"
"github.com/moby/term"
@ -52,8 +51,8 @@ func (s *composeService) attach(ctx context.Context, project *types.Project, lis
_, _ = fmt.Fprintf(s.stdout(), "Attaching to %s\n", strings.Join(names, ", "))
for _, container := range containers {
err := s.attachContainer(ctx, container, listener)
for _, ctr := range containers {
err := s.attachContainer(ctx, ctr, listener)
if err != nil {
return nil, err
}
@ -61,7 +60,7 @@ func (s *composeService) attach(ctx context.Context, project *types.Project, lis
return containers, err
}
func (s *composeService) attachContainer(ctx context.Context, container moby.Container, listener api.ContainerEventListener) error {
func (s *composeService) attachContainer(ctx context.Context, container containerType.Summary, listener api.ContainerEventListener) error {
serviceName := container.Labels[api.ServiceLabel]
containerName := getContainerNameWithoutProject(container)

View File

@ -21,7 +21,9 @@ import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"time"
"github.com/compose-spec/compose-go/v2/types"
"github.com/containerd/platforms"
@ -32,13 +34,13 @@ import (
"github.com/docker/buildx/util/buildflags"
xprogress "github.com/docker/buildx/util/progress"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/hints"
cliopts "github.com/docker/cli/opts"
"github.com/docker/compose/v2/internal/tracing"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/progress"
"github.com/docker/compose/v2/pkg/utils"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/builder/remotecontext/urlutil"
bclient "github.com/moby/buildkit/client"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/auth/authprovider"
@ -48,6 +50,8 @@ import (
"github.com/moby/buildkit/util/progress/progressui"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
// required to get default driver registered
_ "github.com/docker/buildx/driver/docker"
@ -59,31 +63,29 @@ func (s *composeService) Build(ctx context.Context, project *types.Project, opti
return err
}
return progress.RunWithTitle(ctx, func(ctx context.Context) error {
_, err := s.build(ctx, project, options, nil)
return err
return tracing.SpanWrapFunc("project/build", tracing.ProjectOptions(ctx, project),
func(ctx context.Context) error {
_, err := s.build(ctx, project, options, nil)
return err
})(ctx)
}, s.stdinfo(), "Building")
}
type serviceToBuild struct {
name string
service types.ServiceConfig
}
const bakeSuggest = "Compose can now delegate builds to bake for better performance.\n To do so, set COMPOSE_BAKE=true."
var suggest sync.Once
//nolint:gocyclo
func (s *composeService) build(ctx context.Context, project *types.Project, options api.BuildOptions, localImages map[string]string) (map[string]string, error) {
buildkitEnabled, err := s.dockerCli.BuildKitEnabled()
if err != nil {
return nil, err
}
func (s *composeService) build(ctx context.Context, project *types.Project, options api.BuildOptions, localImages map[string]api.ImageSummary) (map[string]string, error) {
imageIDs := map[string]string{}
serviceToBeBuild := map[string]serviceToBuild{}
serviceToBuild := types.Services{}
var policy types.DependencyOption = types.IgnoreDependencies
if options.Deps {
policy = types.IncludeDependencies
}
err = project.ForEachService(options.Services, func(serviceName string, service *types.ServiceConfig) error {
err := project.ForEachService(options.Services, func(serviceName string, service *types.ServiceConfig) error {
if service.Build == nil {
return nil
}
@ -92,20 +94,58 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
if localImagePresent && service.PullPolicy != types.PullPolicyBuild {
return nil
}
serviceToBeBuild[serviceName] = serviceToBuild{name: serviceName, service: *service}
serviceToBuild[serviceName] = *service
return nil
}, policy)
if err != nil || len(serviceToBeBuild) == 0 {
if err != nil || len(serviceToBuild) == 0 {
return imageIDs, err
}
bake, err := buildWithBake(s.dockerCli)
if err != nil {
return nil, err
}
if bake || options.Print {
trace.SpanFromContext(ctx).SetAttributes(attribute.String("builder", "bake"))
return s.doBuildBake(ctx, project, serviceToBuild, options)
}
// Not using bake, additional_context: service:xx is implemented by building images in dependency order
project, err = project.WithServicesTransform(func(serviceName string, service types.ServiceConfig) (types.ServiceConfig, error) {
if service.Build != nil {
for _, c := range service.Build.AdditionalContexts {
if t, found := strings.CutPrefix(c, types.ServicePrefix); found {
if service.DependsOn == nil {
service.DependsOn = map[string]types.ServiceDependency{}
}
service.DependsOn[t] = types.ServiceDependency{
Condition: "build", // non-canonical, but will force dependency graph ordering
}
}
}
}
return service, nil
})
if err != nil {
return imageIDs, err
}
// Initialize buildkit nodes
buildkitEnabled, err := s.dockerCli.BuildKitEnabled()
if err != nil {
return nil, err
}
var (
b *builder.Builder
nodes []builder.Node
w *xprogress.Printer
)
if buildkitEnabled {
if hints.Enabled() && progress.Mode != progress.ModeQuiet && progress.Mode != progress.ModeJSON {
suggest.Do(func() {
fmt.Fprintln(s.dockerCli.Out(), bakeSuggest) //nolint:errcheck
})
}
builderName := options.Builder
if builderName == "" {
builderName = os.Getenv("BUILDX_BUILDER")
@ -129,12 +169,14 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
if options.Quiet {
options.Progress = progress.ModeQuiet
}
if options.Progress == "" {
options.Progress = os.Getenv("BUILDKIT_PROGRESS")
}
w, err = xprogress.NewPrinter(progressCtx, os.Stdout, progressui.DisplayMode(options.Progress),
xprogress.WithDesc(
fmt.Sprintf("building with %q instance using %s driver", b.Name, b.Driver),
fmt.Sprintf("%s:%s", b.Driver, b.Name),
))
if err != nil {
return nil, err
}
@ -151,18 +193,23 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
}
return -1
}
cw := progress.ContextWriter(ctx)
err = InDependencyOrder(ctx, project, func(ctx context.Context, name string) error {
serviceToBuild, ok := serviceToBeBuild[name]
service, ok := serviceToBuild[name]
if !ok {
return nil
}
service := serviceToBuild.service
serviceName := fmt.Sprintf("Service %s", name)
if !buildkitEnabled {
trace.SpanFromContext(ctx).SetAttributes(attribute.String("builder", "classic"))
cw.Event(progress.BuildingEvent(serviceName))
id, err := s.doBuildClassic(ctx, project, service, options)
if err != nil {
return err
}
cw.Event(progress.BuiltEvent(serviceName))
builtDigests[getServiceIndex(name)] = id
if options.Push {
@ -180,6 +227,7 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
return err
}
trace.SpanFromContext(ctx).SetAttributes(attribute.String("builder", "buildkit"))
digest, err := s.doBuildBuildkit(ctx, name, buildOptions, w, nodes)
if err != nil {
return err
@ -204,8 +252,10 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
for i, imageDigest := range builtDigests {
if imageDigest != "" {
imageRef := api.GetImageNameOrDefault(project.Services[names[i]], project.Name)
service := project.Services[names[i]]
imageRef := api.GetImageNameOrDefault(service, project.Name)
imageIDs[imageRef] = imageDigest
cw.Event(progress.BuiltEvent(names[i]))
}
}
return imageIDs, err
@ -213,7 +263,7 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
func (s *composeService) ensureImagesExists(ctx context.Context, project *types.Project, buildOpts *api.BuildOptions, quietPull bool) error {
for name, service := range project.Services {
if service.Image == "" && service.Build == nil {
if service.Provider == nil && service.Image == "" && service.Build == nil {
return fmt.Errorf("invalid service %q. Must specify either image or build", name)
}
}
@ -241,7 +291,11 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
}
for name, digest := range builtImages {
images[name] = digest
images[name] = api.ImageSummary{
Repository: name,
ID: digest,
LastTagTime: time.Now(),
}
}
return nil
},
@ -254,19 +308,16 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
// set digest as com.docker.compose.image label so we can detect outdated containers
for name, service := range project.Services {
image := api.GetImageNameOrDefault(service, project.Name)
digest, ok := images[image]
img, ok := images[image]
if ok {
if service.Labels == nil {
service.Labels = types.Labels{}
}
service.CustomLabels.Add(api.ImageDigestLabel, digest)
service.CustomLabels.Add(api.ImageDigestLabel, img.ID)
}
project.Services[name] = service
}
return nil
}
func (s *composeService) getLocalImagesDigests(ctx context.Context, project *types.Project) (map[string]string, error) {
func (s *composeService) getLocalImagesDigests(ctx context.Context, project *types.Project) (map[string]api.ImageSummary, error) {
var imageNames []string
for _, s := range project.Services {
imgName := api.GetImageNameOrDefault(s, project.Name)
@ -274,18 +325,14 @@ func (s *composeService) getLocalImagesDigests(ctx context.Context, project *typ
imageNames = append(imageNames, imgName)
}
}
imgs, err := s.getImages(ctx, imageNames)
imgs, err := s.getImageSummaries(ctx, imageNames)
if err != nil {
return nil, err
}
images := map[string]string{}
for name, info := range imgs {
images[name] = info.ID
}
for i, service := range project.Services {
imgName := api.GetImageNameOrDefault(service, project.Name)
digest, ok := images[imgName]
img, ok := imgs[imgName]
if !ok {
continue
}
@ -294,7 +341,7 @@ func (s *composeService) getLocalImagesDigests(ctx context.Context, project *typ
if err != nil {
return nil, err
}
inspect, _, err := s.apiClient().ImageInspectWithRaw(ctx, digest)
inspect, err := s.apiClient().ImageInspect(ctx, img.ID)
if err != nil {
return nil, err
}
@ -307,15 +354,15 @@ func (s *composeService) getLocalImagesDigests(ctx context.Context, project *typ
// there is a local image, but it's for the wrong platform, so
// pretend it doesn't exist so that we can pull/build an image
// for the correct platform instead
delete(images, imgName)
delete(imgs, imgName)
}
}
project.Services[i].CustomLabels.Add(api.ImageDigestLabel, digest)
project.Services[i].CustomLabels.Add(api.ImageDigestLabel, img.ID)
}
return images, nil
return imgs, nil
}
// resolveAndMergeBuildArgs returns the final set of build arguments to use for the service image build.
@ -327,12 +374,7 @@ func (s *composeService) getLocalImagesDigests(ctx context.Context, project *typ
//
// Finally, standard proxy variables based on the Docker client configuration are added, but will not overwrite
// any values if already present.
func resolveAndMergeBuildArgs(
dockerCli command.Cli,
project *types.Project,
service types.ServiceConfig,
opts api.BuildOptions,
) types.MappingWithEquals {
func resolveAndMergeBuildArgs(dockerCli command.Cli, project *types.Project, service types.ServiceConfig, opts api.BuildOptions) types.MappingWithEquals {
result := make(types.MappingWithEquals).
OverrideBy(service.Build.Args).
OverrideBy(opts.Args).
@ -365,7 +407,9 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
}
sessionConfig := []session.Attachable{
authprovider.NewDockerAuthProvider(s.configFile(), nil),
authprovider.NewDockerAuthProvider(authprovider.DockerAuthProviderConfig{
ConfigFile: s.configFile(),
}),
}
if len(options.SSHs) > 0 || len(service.Build.SSH) > 0 {
sshAgentProvider, err := sshAgentProvider(append(service.Build.SSH, options.SSHs...))
@ -393,7 +437,7 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
return build.Options{}, err
}
if service.Build.Privileged {
allow = append(allow, entitlements.EntitlementSecurityInsecure)
allow = append(allow, entitlements.EntitlementSecurityInsecure.String())
}
imageLabels := getImageBuildLabels(project, service)
@ -425,10 +469,10 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
ContextPath: service.Build.Context,
DockerfileInline: service.Build.DockerfileInline,
DockerfilePath: dockerFilePath(service.Build.Context, service.Build.Dockerfile),
NamedContexts: toBuildContexts(service.Build.AdditionalContexts),
NamedContexts: toBuildContexts(service, project),
},
CacheFrom: pb.CreateCaches(cacheFrom),
CacheTo: pb.CreateCaches(cacheTo),
CacheFrom: pb.CreateCaches(cacheFrom.ToPB()),
CacheTo: pb.CreateCaches(cacheTo.ToPB()),
NoCache: service.Build.NoCache,
Pull: service.Build.Pull,
BuildArgs: flatten(resolveAndMergeBuildArgs(s.dockerCli, project, service, options)),
@ -472,16 +516,6 @@ func flatten(in types.MappingWithEquals) types.Mapping {
return out
}
func dockerFilePath(ctxName string, dockerfile string) string {
if dockerfile == "" {
return ""
}
if urlutil.IsGitURL(ctxName) || filepath.IsAbs(dockerfile) {
return dockerfile
}
return filepath.Join(ctxName, dockerfile)
}
func sshAgentProvider(sshKeys types.SSHConfig) (session.Attachable, error) {
sshConfig := make([]sshprovider.AgentConfig, 0, len(sshKeys))
for _, sshKey := range sshKeys {
@ -540,9 +574,16 @@ func getImageBuildLabels(project *types.Project, service types.ServiceConfig) ty
return ret
}
func toBuildContexts(additionalContexts types.Mapping) map[string]build.NamedContext {
func toBuildContexts(service types.ServiceConfig, project *types.Project) map[string]build.NamedContext {
namedContexts := map[string]build.NamedContext{}
for name, contextPath := range additionalContexts {
for name, contextPath := range service.Build.AdditionalContexts {
if strings.HasPrefix(contextPath, types.ServicePrefix) {
// image we depend on has been built previously, as we run in dependency order.
// so we convert the service reference into an image reference
target := contextPath[len(types.ServicePrefix):]
image := api.GetImageNameOrDefault(project.Services[target], project.Name)
contextPath = "docker-image://" + image
}
namedContexts[name] = build.NamedContext{Path: contextPath}
}
return namedContexts

407
pkg/compose/build_bake.go Normal file
View File

@ -0,0 +1,407 @@
/*
Copyright 2020 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package compose
import (
"bufio"
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"slices"
"strconv"
"strings"
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli-plugins/manager"
"github.com/docker/cli/cli-plugins/socket"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/progress"
"github.com/docker/docker/api/types/versions"
"github.com/docker/docker/builder/remotecontext/urlutil"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/util/gitutil"
"github.com/moby/buildkit/util/progress/progressui"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
"golang.org/x/sync/errgroup"
)
func buildWithBake(dockerCli command.Cli) (bool, error) {
b, ok := os.LookupEnv("COMPOSE_BAKE")
if !ok {
if dockerCli.ConfigFile().Plugins["compose"]["build"] == "bake" {
b, ok = "true", true
}
}
if !ok {
return false, nil
}
bake, err := strconv.ParseBool(b)
if err != nil {
return false, err
}
if !bake {
return false, nil
}
enabled, err := dockerCli.BuildKitEnabled()
if err != nil {
return false, err
}
if !enabled {
logrus.Warnf("Docker Compose is configured to build using Bake, but buildkit isn't enabled")
}
_, err = manager.GetPlugin("buildx", dockerCli, &cobra.Command{})
if err != nil {
if manager.IsNotFound(err) {
logrus.Warnf("Docker Compose is configured to build using Bake, but buildx isn't installed")
return false, nil
}
return false, err
}
return true, err
}
// We _could_ use bake.* types from github.com/docker/buildx but long term plan is to remove buildx as a dependency
type bakeConfig struct {
Groups map[string]bakeGroup `json:"group"`
Targets map[string]bakeTarget `json:"target"`
}
type bakeGroup struct {
Targets []string `json:"targets"`
}
type bakeTarget struct {
Context string `json:"context,omitempty"`
Contexts map[string]string `json:"contexts,omitempty"`
Dockerfile string `json:"dockerfile,omitempty"`
DockerfileInline string `json:"dockerfile-inline,omitempty"`
Args map[string]string `json:"args,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
Tags []string `json:"tags,omitempty"`
CacheFrom []string `json:"cache-from,omitempty"`
CacheTo []string `json:"cache-to,omitempty"`
Target string `json:"target,omitempty"`
Secrets []string `json:"secret,omitempty"`
SSH []string `json:"ssh,omitempty"`
Platforms []string `json:"platforms,omitempty"`
Pull bool `json:"pull,omitempty"`
NoCache bool `json:"no-cache,omitempty"`
NetworkMode string `json:"network,omitempty"`
NoCacheFilter []string `json:"no-cache-filter,omitempty"`
ShmSize types.UnitBytes `json:"shm-size,omitempty"`
Ulimits []string `json:"ulimits,omitempty"`
Call string `json:"call,omitempty"`
Entitlements []string `json:"entitlements,omitempty"`
Outputs []string `json:"output,omitempty"`
}
type bakeMetadata map[string]buildStatus
type buildStatus struct {
Digest string `json:"containerimage.digest"`
}
func (s *composeService) doBuildBake(ctx context.Context, project *types.Project, serviceToBeBuild types.Services, options api.BuildOptions) (map[string]string, error) { //nolint:gocyclo
eg := errgroup.Group{}
ch := make(chan *client.SolveStatus)
display, err := progressui.NewDisplay(os.Stdout, progressui.DisplayMode(options.Progress))
if err != nil {
return nil, err
}
eg.Go(func() error {
_, err := display.UpdateFrom(ctx, ch)
return err
})
cfg := bakeConfig{
Groups: map[string]bakeGroup{},
Targets: map[string]bakeTarget{},
}
var group bakeGroup
var privileged bool
var read []string
for serviceName, service := range serviceToBeBuild {
if service.Build == nil {
continue
}
build := *service.Build
args := types.Mapping{}
for k, v := range resolveAndMergeBuildArgs(s.dockerCli, project, service, options) {
if v == nil {
continue
}
args[k] = *v
}
image := api.GetImageNameOrDefault(service, project.Name)
entitlements := build.Entitlements
if slices.Contains(build.Entitlements, "security.insecure") {
privileged = true
}
if build.Privileged {
entitlements = append(entitlements, "security.insecure")
privileged = true
}
var output string
push := options.Push && service.Image != ""
if len(service.Build.Platforms) > 1 {
output = fmt.Sprintf("type=image,push=%t", push)
} else {
output = fmt.Sprintf("type=docker,load=true,push=%t", push)
}
read = append(read, build.Context)
for _, path := range build.AdditionalContexts {
_, err := gitutil.ParseGitRef(path)
if !strings.Contains(path, "://") && err != nil {
read = append(read, path)
}
}
cfg.Targets[serviceName] = bakeTarget{
Context: build.Context,
Contexts: additionalContexts(build.AdditionalContexts),
Dockerfile: dockerFilePath(build.Context, build.Dockerfile),
DockerfileInline: strings.ReplaceAll(build.DockerfileInline, "${", "$${"),
Args: args,
Labels: build.Labels,
Tags: append(build.Tags, image),
CacheFrom: build.CacheFrom,
// CacheTo: TODO
Platforms: build.Platforms,
Target: build.Target,
Secrets: toBakeSecrets(project, build.Secrets),
SSH: toBakeSSH(append(build.SSH, options.SSHs...)),
Pull: options.Pull,
NoCache: options.NoCache,
ShmSize: build.ShmSize,
Ulimits: toBakeUlimits(build.Ulimits),
Entitlements: entitlements,
Outputs: []string{output},
}
group.Targets = append(group.Targets, serviceName)
}
cfg.Groups["default"] = group
b, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
return nil, err
}
if options.Print {
_, err = fmt.Fprintln(s.stdinfo(), string(b))
return nil, err
}
logrus.Debugf("bake build config:\n%s", string(b))
metadata, err := os.CreateTemp(os.TempDir(), "compose")
if err != nil {
return nil, err
}
buildx, err := manager.GetPlugin("buildx", s.dockerCli, &cobra.Command{})
if err != nil {
return nil, err
}
args := []string{"bake", "--file", "-", "--progress", "rawjson", "--metadata-file", metadata.Name()}
mustAllow := buildx.Version != "" && versions.GreaterThanOrEqualTo(buildx.Version[1:], "0.17.0")
if mustAllow {
// FIXME we should prompt user about this, but this is a breaking change in UX
for _, path := range read {
args = append(args, "--allow", "fs.read="+path)
}
if privileged {
args = append(args, "--allow", "security.insecure")
}
}
if options.Builder != "" {
args = append(args, "--builder", options.Builder)
}
logrus.Debugf("Executing bake with args: %v", args)
cmd := exec.CommandContext(ctx, buildx.Path, args...)
// Remove DOCKER_CLI_PLUGIN... variable so buildx can detect it run standalone
cmd.Env = filter(os.Environ(), manager.ReexecEnvvar)
// Use docker/cli mechanism to propagate termination signal to child process
server, err := socket.NewPluginServer(nil)
if err != nil {
defer server.Close() //nolint:errcheck
cmd.Cancel = server.Close
cmd.Env = replace(cmd.Env, socket.EnvKey, server.Addr().String())
}
cmd.Env = append(cmd.Env, fmt.Sprintf("DOCKER_CONTEXT=%s", s.dockerCli.CurrentContext()))
// propagate opentelemetry context to child process, see https://github.com/open-telemetry/oteps/blob/main/text/0258-env-context-baggage-carriers.md
carrier := propagation.MapCarrier{}
otel.GetTextMapPropagator().Inject(ctx, &carrier)
cmd.Env = append(cmd.Env, types.Mapping(carrier).Values()...)
cmd.Stdout = s.stdout()
cmd.Stdin = bytes.NewBuffer(b)
pipe, err := cmd.StderrPipe()
if err != nil {
return nil, err
}
var errMessage string
scanner := bufio.NewScanner(pipe)
scanner.Split(bufio.ScanLines)
err = cmd.Start()
if err != nil {
return nil, err
}
eg.Go(cmd.Wait)
for scanner.Scan() {
line := scanner.Text()
decoder := json.NewDecoder(strings.NewReader(line))
var status client.SolveStatus
err := decoder.Decode(&status)
if err != nil {
if strings.HasPrefix(line, "ERROR: ") {
errMessage = line[7:]
}
continue
}
ch <- &status
}
close(ch) // stop build progress UI
err = eg.Wait()
if err != nil {
if errMessage != "" {
return nil, errors.New(errMessage)
}
return nil, fmt.Errorf("failed to execute bake: %w", err)
}
b, err = os.ReadFile(metadata.Name())
if err != nil {
return nil, err
}
var md bakeMetadata
err = json.Unmarshal(b, &md)
if err != nil {
return nil, err
}
cw := progress.ContextWriter(ctx)
results := map[string]string{}
for name, m := range md {
results[name] = m.Digest
cw.Event(progress.BuiltEvent(name))
}
return results, nil
}
func additionalContexts(contexts types.Mapping) map[string]string {
ac := map[string]string{}
for k, v := range contexts {
if target, found := strings.CutPrefix(v, types.ServicePrefix); found {
v = "target:" + target
}
ac[k] = v
}
return ac
}
func toBakeUlimits(ulimits map[string]*types.UlimitsConfig) []string {
s := []string{}
for u, l := range ulimits {
if l.Single > 0 {
s = append(s, fmt.Sprintf("%s=%d", u, l.Single))
} else {
s = append(s, fmt.Sprintf("%s=%d:%d", u, l.Soft, l.Hard))
}
}
return s
}
func toBakeSSH(ssh types.SSHConfig) []string {
var s []string
for _, key := range ssh {
s = append(s, fmt.Sprintf("%s=%s", key.ID, key.Path))
}
return s
}
func toBakeSecrets(project *types.Project, secrets []types.ServiceSecretConfig) []string {
var s []string
for _, ref := range secrets {
def := project.Secrets[ref.Source]
target := ref.Target
if target == "" {
target = ref.Source
}
switch {
case def.Environment != "":
s = append(s, fmt.Sprintf("id=%s,type=env,env=%s", target, def.Environment))
case def.File != "":
s = append(s, fmt.Sprintf("id=%s,type=file,src=%s", target, def.File))
}
}
return s
}
func filter(environ []string, variable string) []string {
prefix := variable + "="
filtered := make([]string, 0, len(environ))
for _, val := range environ {
if !strings.HasPrefix(val, prefix) {
filtered = append(filtered, val)
}
}
return filtered
}
func replace(environ []string, variable, value string) []string {
filtered := filter(environ, variable)
return append(filtered, fmt.Sprintf("%s=%s", variable, value))
}
func dockerFilePath(ctxName string, dockerfile string) string {
if dockerfile == "" {
return ""
}
if urlutil.IsGitURL(ctxName) || filepath.IsAbs(dockerfile) {
return dockerfile
}
return filepath.Join(ctxName, dockerfile)
}

View File

@ -45,10 +45,10 @@ func (s *composeService) doBuildBuildkit(ctx context.Context, service string, op
response, err = build.Build(ctx, nodes,
map[string]build.Options{service: opts},
dockerutil.NewClient(s.dockerCli),
confutil.ConfigDir(s.dockerCli),
confutil.NewConfig(s.dockerCli),
buildx.WithPrefix(p, service, true))
if err != nil {
return "", WrapCategorisedComposeError(err, BuildFailure)
return "", err
}
}
@ -70,11 +70,6 @@ func (s composeService) dryRunBuildResponse(ctx context.Context, name string, op
w := progress.ContextWriter(ctx)
buildResponse := map[string]*client.SolveResponse{}
dryRunUUID := fmt.Sprintf("dryRun-%x", sha1.Sum([]byte(name)))
w.Event(progress.Event{
ID: " ",
Status: progress.Done,
Text: fmt.Sprintf("build service %s", name),
})
w.Event(progress.Event{
ID: "==>",
Status: progress.Done,

87
pkg/compose/commit.go Normal file
View File

@ -0,0 +1,87 @@
/*
Copyright 2020 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package compose
import (
"context"
"fmt"
"strings"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/progress"
"github.com/docker/docker/api/types/container"
)
func (s *composeService) Commit(ctx context.Context, projectName string, options api.CommitOptions) error {
return progress.RunWithTitle(ctx, func(ctx context.Context) error {
return s.commit(ctx, projectName, options)
}, s.stdinfo(), "Committing")
}
func (s *composeService) commit(ctx context.Context, projectName string, options api.CommitOptions) error {
projectName = strings.ToLower(projectName)
ctr, err := s.getSpecifiedContainer(ctx, projectName, oneOffInclude, false, options.Service, options.Index)
if err != nil {
return err
}
clnt := s.dockerCli.Client()
w := progress.ContextWriter(ctx)
name := getCanonicalContainerName(ctr)
msg := fmt.Sprintf("Commit %s", name)
w.Event(progress.Event{
ID: name,
Text: msg,
Status: progress.Working,
StatusText: "Committing",
})
if s.dryRun {
w.Event(progress.Event{
ID: name,
Text: msg,
Status: progress.Done,
StatusText: "Committed",
})
return nil
}
response, err := clnt.ContainerCommit(ctx, ctr.ID, container.CommitOptions{
Reference: options.Reference,
Comment: options.Comment,
Author: options.Author,
Changes: options.Changes.GetAll(),
Pause: options.Pause,
})
if err != nil {
return err
}
w.Event(progress.Event{
ID: name,
Text: msg,
Status: progress.Done,
StatusText: fmt.Sprintf("Committed as %s", response.ID),
})
return nil
}

View File

@ -25,22 +25,22 @@ import (
"strings"
"sync"
"github.com/docker/compose/v2/internal/desktop"
"github.com/docker/compose/v2/internal/experimental"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/volume"
"github.com/jonboulle/clockwork"
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/flags"
"github.com/docker/cli/cli/streams"
"github.com/docker/compose/v2/pkg/api"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/api/types/volume"
"github.com/docker/docker/client"
"github.com/jonboulle/clockwork"
"github.com/docker/compose/v2/internal/desktop"
"github.com/docker/compose/v2/internal/experimental"
"github.com/docker/compose/v2/pkg/api"
)
var stdioToStdout bool
@ -139,7 +139,7 @@ func (s *composeService) stdinfo() *streams.Out {
return s.dockerCli.Err()
}
func getCanonicalContainerName(c moby.Container) string {
func getCanonicalContainerName(c container.Summary) string {
if len(c.Names) == 0 {
// corner case, sometime happens on removal. return short ID as a safeguard value
return c.ID[:12]
@ -154,7 +154,7 @@ func getCanonicalContainerName(c moby.Container) string {
return strings.TrimPrefix(c.Names[0], "/")
}
func getContainerNameWithoutProject(c moby.Container) string {
func getContainerNameWithoutProject(c container.Summary) string {
project := c.Labels[api.ProjectLabel]
defaultName := getDefaultContainerName(project, c.Labels[api.ServiceLabel], c.Labels[api.ContainerNumberLabel])
name := getCanonicalContainerName(c)
@ -187,7 +187,6 @@ func (s *composeService) projectFromName(containers Containers, projectName stri
Image: c.Image,
Labels: c.Labels,
}
}
service.Scale = increment(service.Scale)
set[serviceLabel] = service
@ -321,7 +320,6 @@ func (s *composeService) RuntimeVersion(ctx context.Context) (string, error) {
runtimeVersion.val = version.APIVersion
})
return runtimeVersion.val, runtimeVersion.err
}
func (s *composeService) isDesktopIntegrationActive() bool {

View File

@ -25,13 +25,12 @@ import (
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/utils"
moby "github.com/docker/docker/api/types"
containerType "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
)
// Containers is a set of moby Container
type Containers []moby.Container
type Containers []container.Summary
type oneOff int
@ -44,7 +43,7 @@ const (
func (s *composeService) getContainers(ctx context.Context, project string, oneOff oneOff, all bool, selectedServices ...string) (Containers, error) {
var containers Containers
f := getDefaultFilters(project, oneOff, selectedServices...)
containers, err := s.apiClient().ContainerList(ctx, containerType.ListOptions{
containers, err := s.apiClient().ContainerList(ctx, container.ListOptions{
Filters: filters.NewArgs(f...),
All: all,
})
@ -73,25 +72,25 @@ func getDefaultFilters(projectName string, oneOff oneOff, selectedServices ...st
return f
}
func (s *composeService) getSpecifiedContainer(ctx context.Context, projectName string, oneOff oneOff, all bool, serviceName string, containerIndex int) (moby.Container, error) {
func (s *composeService) getSpecifiedContainer(ctx context.Context, projectName string, oneOff oneOff, all bool, serviceName string, containerIndex int) (container.Summary, error) {
defaultFilters := getDefaultFilters(projectName, oneOff, serviceName)
if containerIndex > 0 {
defaultFilters = append(defaultFilters, containerNumberFilter(containerIndex))
}
containers, err := s.apiClient().ContainerList(ctx, containerType.ListOptions{
containers, err := s.apiClient().ContainerList(ctx, container.ListOptions{
Filters: filters.NewArgs(
defaultFilters...,
),
All: all,
})
if err != nil {
return moby.Container{}, err
return container.Summary{}, err
}
if len(containers) < 1 {
if containerIndex > 0 {
return moby.Container{}, fmt.Errorf("service %q is not running container #%d", serviceName, containerIndex)
return container.Summary{}, fmt.Errorf("service %q is not running container #%d", serviceName, containerIndex)
}
return moby.Container{}, fmt.Errorf("service %q is not running", serviceName)
return container.Summary{}, fmt.Errorf("service %q is not running", serviceName)
}
// Sort by container number first, then put one-off containers at the end
@ -107,22 +106,30 @@ func (s *composeService) getSpecifiedContainer(ctx context.Context, projectName
return numberLabelX < numberLabelY
})
container := containers[0]
return container, nil
return containers[0], nil
}
// containerPredicate define a predicate we want container to satisfy for filtering operations
type containerPredicate func(c moby.Container) bool
type containerPredicate func(c container.Summary) bool
func matches(c container.Summary, predicates ...containerPredicate) bool {
for _, predicate := range predicates {
if !predicate(c) {
return false
}
}
return true
}
func isService(services ...string) containerPredicate {
return func(c moby.Container) bool {
return func(c container.Summary) bool {
service := c.Labels[api.ServiceLabel]
return utils.StringContains(services, service)
}
}
func isRunning() containerPredicate {
return func(c moby.Container) bool {
return func(c container.Summary) bool {
return c.State == "running"
}
}
@ -130,7 +137,7 @@ func isRunning() containerPredicate {
// isOrphaned is a predicate to select containers without a matching service definition in compose project
func isOrphaned(project *types.Project) containerPredicate {
services := append(project.ServiceNames(), project.DisabledServiceNames()...)
return func(c moby.Container) bool {
return func(c container.Summary) bool {
// One-off container
v, ok := c.Labels[api.OneoffLabel]
if ok && v == "True" {
@ -142,16 +149,16 @@ func isOrphaned(project *types.Project) containerPredicate {
}
}
func isNotOneOff(c moby.Container) bool {
func isNotOneOff(c container.Summary) bool {
v, ok := c.Labels[api.OneoffLabel]
return !ok || v == "False"
}
// filter return Containers with elements to match predicate
func (containers Containers) filter(predicate containerPredicate) Containers {
func (containers Containers) filter(predicates ...containerPredicate) Containers {
var filtered Containers
for _, c := range containers {
if predicate(c) {
if matches(c, predicates...) {
filtered = append(filtered, c)
}
}
@ -166,7 +173,7 @@ func (containers Containers) names() []string {
return names
}
func (containers Containers) forEach(fn func(moby.Container)) {
func (containers Containers) forEach(fn func(container.Summary)) {
for _, c := range containers {
fn(c)
}

View File

@ -20,25 +20,25 @@ import (
"context"
"errors"
"fmt"
"slices"
"sort"
"strconv"
"strings"
"sync"
"time"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"github.com/compose-spec/compose-go/v2/types"
"github.com/containerd/platforms"
"github.com/docker/compose/v2/internal/tracing"
moby "github.com/docker/docker/api/types"
containerType "github.com/docker/docker/api/types/container"
mmount "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/versions"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"golang.org/x/sync/errgroup"
"github.com/docker/compose/v2/internal/tracing"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/progress"
"github.com/docker/compose/v2/pkg/utils"
@ -47,7 +47,7 @@ import (
const (
doubledContainerNameWarning = "WARNING: The %q service is using the custom container name %q. " +
"Docker requires each container to have a unique name. " +
"Remove the custom name to scale the service.\n"
"Remove the custom name to scale the service"
)
// convergence manages service's container lifecycle.
@ -56,24 +56,26 @@ const (
// Cross services dependencies are managed by creating services in expected order and updating `service:xx` reference
// when a service has converged, so dependent ones can be managed with resolved containers references.
type convergence struct {
service *composeService
observedState map[string]Containers
stateMutex sync.Mutex
service *composeService
services map[string]Containers
networks map[string]string
volumes map[string]string
stateMutex sync.Mutex
}
func (c *convergence) getObservedState(serviceName string) Containers {
c.stateMutex.Lock()
defer c.stateMutex.Unlock()
return c.observedState[serviceName]
return c.services[serviceName]
}
func (c *convergence) setObservedState(serviceName string, containers Containers) {
c.stateMutex.Lock()
defer c.stateMutex.Unlock()
c.observedState[serviceName] = containers
c.services[serviceName] = containers
}
func newConvergence(services []string, state Containers, s *composeService) *convergence {
func newConvergence(services []string, state Containers, networks map[string]string, volumes map[string]string, s *composeService) *convergence {
observedState := map[string]Containers{}
for _, s := range services {
observedState[s] = Containers{}
@ -83,8 +85,10 @@ func newConvergence(services []string, state Containers, s *composeService) *con
observedState[service] = append(observedState[service], c)
}
return &convergence{
service: s,
observedState: observedState,
service: s,
services: observedState,
networks: networks,
volumes: volumes,
}
}
@ -106,6 +110,9 @@ func (c *convergence) apply(ctx context.Context, project *types.Project, options
}
func (c *convergence) ensureService(ctx context.Context, project *types.Project, service types.ServiceConfig, recreate string, inherit bool, timeout *time.Duration) error { //nolint:gocyclo
if service.Provider != nil {
return c.service.runPlugin(ctx, project, service, "up")
}
expected, err := getScale(service)
if err != nil {
return err
@ -123,11 +130,11 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
sort.Slice(containers, func(i, j int) bool {
// select obsolete containers first, so they get removed as we scale down
if obsolete, _ := mustRecreate(service, containers[i], recreate); obsolete {
if obsolete, _ := c.mustRecreate(service, containers[i], recreate); obsolete {
// i is obsolete, so must be first in the list
return true
}
if obsolete, _ := mustRecreate(service, containers[j], recreate); obsolete {
if obsolete, _ := c.mustRecreate(service, containers[j], recreate); obsolete {
// j is obsolete, so must be first in the list
return false
}
@ -136,16 +143,18 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
ni, erri := strconv.Atoi(containers[i].Labels[api.ContainerNumberLabel])
nj, errj := strconv.Atoi(containers[j].Labels[api.ContainerNumberLabel])
if erri == nil && errj == nil {
return ni < nj
return ni > nj
}
// If we don't get a container number (?) just sort by creation date
return containers[i].Created < containers[j].Created
})
slices.Reverse(containers)
for i, container := range containers {
if i >= expected {
// Scale Down
// As we sorted containers, obsolete ones and/or highest number will be removed
container := container
traceOpts := append(tracing.ServiceOptions(service), tracing.ContainerOptions(container)...)
eg.Go(tracing.SpanWrapFuncForErrGroup(ctx, "service/scale/down", traceOpts, func(ctx context.Context) error {
@ -154,7 +163,7 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
continue
}
mustRecreate, err := mustRecreate(service, container, recreate)
mustRecreate, err := c.mustRecreate(service, container, recreate)
if err != nil {
return err
}
@ -197,7 +206,6 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
// Scale UP
number := next + i
name := getContainerName(project.Name, service, number)
i := i
eventOpts := tracing.SpanOptions{trace.WithAttributes(attribute.String("container.name", name))}
eg.Go(tracing.EventWrapFuncForErrGroup(ctx, "service/scale/up", eventOpts, func(ctx context.Context) error {
opts := createOptions{
@ -219,20 +227,28 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
}
func (c *convergence) stopDependentContainers(ctx context.Context, project *types.Project, service types.ServiceConfig) error {
w := progress.ContextWriter(ctx)
// Stop dependent containers, so they will be restarted after service is re-created
dependents := project.GetDependentsForService(service)
dependents := project.GetDependentsForService(service, func(dependency types.ServiceDependency) bool {
return dependency.Restart
})
if len(dependents) == 0 {
return nil
}
err := c.service.stop(ctx, project.Name, api.StopOptions{
Services: dependents,
Project: project,
})
if err != nil {
return err
}
for _, name := range dependents {
dependents := c.getObservedState(name)
err := c.service.stopContainers(ctx, w, &service, dependents, nil)
if err != nil {
return err
}
for i, dependent := range dependents {
dependentStates := c.getObservedState(name)
for i, dependent := range dependentStates {
dependent.State = ContainerExited
dependents[i] = dependent
dependentStates[i] = dependent
}
c.setObservedState(name, dependents)
c.setObservedState(name, dependentStates)
}
return nil
}
@ -312,7 +328,7 @@ func (c *convergence) resolveSharedNamespaces(service *types.ServiceConfig) erro
return nil
}
func mustRecreate(expected types.ServiceConfig, actual moby.Container, policy string) (bool, error) {
func (c *convergence) mustRecreate(expected types.ServiceConfig, actual containerType.Summary, policy string) (bool, error) {
if policy == api.RecreateNever {
return false, nil
}
@ -325,7 +341,74 @@ func mustRecreate(expected types.ServiceConfig, actual moby.Container, policy st
}
configChanged := actual.Labels[api.ConfigHashLabel] != configHash
imageUpdated := actual.Labels[api.ImageDigestLabel] != expected.CustomLabels[api.ImageDigestLabel]
return configChanged || imageUpdated, nil
if configChanged || imageUpdated {
return true, nil
}
if c.networks != nil && actual.State == "running" {
if checkExpectedNetworks(expected, actual, c.networks) {
return true, nil
}
}
if c.volumes != nil {
if checkExpectedVolumes(expected, actual, c.volumes) {
return true, nil
}
}
return false, nil
}
func checkExpectedNetworks(expected types.ServiceConfig, actual containerType.Summary, networks map[string]string) bool {
// check the networks container is connected to are the expected ones
for net := range expected.Networks {
id := networks[net]
if id == "swarm" {
// corner-case : swarm overlay network isn't visible until a container is attached
continue
}
found := false
for _, settings := range actual.NetworkSettings.Networks {
if settings.NetworkID == id {
found = true
break
}
}
if !found {
// config is up-to-date but container is not connected to network
return true
}
}
return false
}
func checkExpectedVolumes(expected types.ServiceConfig, actual containerType.Summary, volumes map[string]string) bool {
// check container's volume mounts and search for the expected ones
for _, vol := range expected.Volumes {
if vol.Type != string(mmount.TypeVolume) {
continue
}
if vol.Source == "" {
continue
}
id := volumes[vol.Source]
found := false
for _, mount := range actual.Mounts {
if mount.Type != mmount.TypeVolume {
continue
}
if mount.Name == id {
found = true
break
}
}
if !found {
// config is up-to-date but container doesn't have volume mounted
return true
}
}
return false
}
func getContainerName(projectName string, service types.ServiceConfig, number int) string {
@ -340,22 +423,22 @@ func getDefaultContainerName(projectName, serviceName, index string) string {
return strings.Join([]string{projectName, serviceName, index}, api.Separator)
}
func getContainerProgressName(container moby.Container) string {
return "Container " + getCanonicalContainerName(container)
func getContainerProgressName(ctr containerType.Summary) string {
return "Container " + getCanonicalContainerName(ctr)
}
func containerEvents(containers Containers, eventFunc func(string) progress.Event) []progress.Event {
events := []progress.Event{}
for _, container := range containers {
events = append(events, eventFunc(getContainerProgressName(container)))
for _, ctr := range containers {
events = append(events, eventFunc(getContainerProgressName(ctr)))
}
return events
}
func containerReasonEvents(containers Containers, eventFunc func(string, string) progress.Event, reason string) []progress.Event {
events := []progress.Event{}
for _, container := range containers {
events = append(events, eventFunc(getContainerProgressName(container), reason))
for _, ctr := range containers {
events = append(events, eventFunc(getContainerProgressName(ctr), reason))
}
return events
}
@ -379,7 +462,7 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
continue
}
waitingFor := containers.filter(isService(dep))
waitingFor := containers.filter(isService(dep), isNotOneOff)
w.Events(containerEvents(waitingFor, progress.Waiting))
if len(waitingFor) == 0 {
if config.Required {
@ -389,7 +472,6 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
continue
}
dep, config := dep, config
eg.Go(func() error {
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
@ -486,7 +568,7 @@ func shouldWaitForDependency(serviceName string, dependencyConfig types.ServiceD
return true, nil
}
func nextContainerNumber(containers []moby.Container) int {
func nextContainerNumber(containers []containerType.Summary) int {
maxNumber := 0
for _, c := range containers {
s, ok := c.Labels[api.ContainerNumberLabel]
@ -503,15 +585,15 @@ func nextContainerNumber(containers []moby.Container) int {
}
}
return maxNumber + 1
}
func (s *composeService) createContainer(ctx context.Context, project *types.Project, service types.ServiceConfig,
name string, number int, opts createOptions) (container moby.Container, err error) {
name string, number int, opts createOptions,
) (ctr containerType.Summary, err error) {
w := progress.ContextWriter(ctx)
eventName := "Container " + name
w.Event(progress.CreatingEvent(eventName))
container, err = s.createMobyContainer(ctx, project, service, name, number, nil, opts, w)
ctr, err = s.createMobyContainer(ctx, project, service, name, number, nil, opts, w)
if err != nil {
return
}
@ -520,8 +602,9 @@ func (s *composeService) createContainer(ctx context.Context, project *types.Pro
}
func (s *composeService) recreateContainer(ctx context.Context, project *types.Project, service types.ServiceConfig,
replaced moby.Container, inherit bool, timeout *time.Duration) (moby.Container, error) {
var created moby.Container
replaced containerType.Summary, inherit bool, timeout *time.Duration,
) (containerType.Summary, error) {
var created containerType.Summary
w := progress.ContextWriter(ctx)
w.Event(progress.NewEvent(getContainerProgressName(replaced), progress.Working, "Recreate"))
@ -530,7 +613,7 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
return created, err
}
var inherited *moby.Container
var inherited *containerType.Summary
if inherit {
inherited = &replaced
}
@ -567,14 +650,14 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
return created, err
}
func (s *composeService) startContainer(ctx context.Context, container moby.Container) error {
func (s *composeService) startContainer(ctx context.Context, ctr containerType.Summary) error {
w := progress.ContextWriter(ctx)
w.Event(progress.NewEvent(getContainerProgressName(container), progress.Working, "Restart"))
err := s.apiClient().ContainerStart(ctx, container.ID, containerType.StartOptions{})
w.Event(progress.NewEvent(getContainerProgressName(ctr), progress.Working, "Restart"))
err := s.apiClient().ContainerStart(ctx, ctr.ID, containerType.StartOptions{})
if err != nil {
return err
}
w.Event(progress.NewEvent(getContainerProgressName(container), progress.Done, "Restarted"))
w.Event(progress.NewEvent(getContainerProgressName(ctr), progress.Done, "Restarted"))
return nil
}
@ -583,13 +666,12 @@ func (s *composeService) createMobyContainer(ctx context.Context,
service types.ServiceConfig,
name string,
number int,
inherit *moby.Container,
inherit *containerType.Summary,
opts createOptions,
w progress.Writer,
) (moby.Container, error) {
var created moby.Container
) (containerType.Summary, error) {
var created containerType.Summary
cfgs, err := s.getCreateConfigs(ctx, project, service, number, inherit, opts)
if err != nil {
return created, err
}
@ -622,11 +704,11 @@ func (s *composeService) createMobyContainer(ctx context.Context,
if err != nil {
return created, err
}
created = moby.Container{
created = containerType.Summary{
ID: inspectedContainer.ID,
Labels: inspectedContainer.Config.Labels,
Names: []string{inspectedContainer.Name},
NetworkSettings: &moby.SummaryNetworkSettings{
NetworkSettings: &containerType.NetworkSettingsSummary{
Networks: inspectedContainer.NetworkSettings.Networks,
},
}
@ -743,11 +825,11 @@ func (s *composeService) isServiceHealthy(ctx context.Context, containers Contai
return false, fmt.Errorf("container %s has no healthcheck configured", name)
}
switch container.State.Health.Status {
case moby.Healthy:
case containerType.Healthy:
// Continue by checking the next container.
case moby.Unhealthy:
case containerType.Unhealthy:
return false, fmt.Errorf("container %s is unhealthy", name)
case moby.Starting:
case containerType.Starting:
return false, nil
default:
return false, fmt.Errorf("container %s had unexpected health status %q", name, container.State.Health.Status)
@ -772,7 +854,8 @@ func (s *composeService) isServiceCompleted(ctx context.Context, containers Cont
func (s *composeService) startService(ctx context.Context,
project *types.Project, service types.ServiceConfig,
containers Containers, listener api.ContainerEventListener,
timeout time.Duration) error {
timeout time.Duration,
) error {
if service.Deploy != nil && service.Deploy.Replicas != nil && *service.Deploy.Replicas == 0 {
return nil
}
@ -790,19 +873,19 @@ func (s *composeService) startService(ctx context.Context,
}
w := progress.ContextWriter(ctx)
for _, container := range containers.filter(isService(service.Name)) {
if container.State == ContainerRunning {
for _, ctr := range containers.filter(isService(service.Name)) {
if ctr.State == ContainerRunning {
continue
}
eventName := getContainerProgressName(container)
eventName := getContainerProgressName(ctr)
w.Event(progress.StartingEvent(eventName))
err = s.apiClient().ContainerStart(ctx, container.ID, containerType.StartOptions{})
err = s.apiClient().ContainerStart(ctx, ctr.ID, containerType.StartOptions{})
if err != nil {
return err
}
for _, hook := range service.PostStart {
err = s.runHook(ctx, container, service, hook, listener)
err = s.runHook(ctx, ctr, service, hook, listener)
if err != nil {
return err
}

View File

@ -25,8 +25,9 @@ import (
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/config/configfile"
moby "github.com/docker/docker/api/types"
containerType "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/network"
"github.com/docker/go-connections/nat"
"go.uber.org/mock/gomock"
@ -70,7 +71,7 @@ func TestServiceLinks(t *testing.T) {
Scale: intPtr(1),
}
containerListOptions := containerType.ListOptions{
containerListOptions := container.ListOptions{
Filters: filters.NewArgs(
projectFilter(testProject),
serviceFilter("db"),
@ -94,7 +95,7 @@ func TestServiceLinks(t *testing.T) {
s.Links = []string{"db"}
c := testContainer("db", dbContainerName, false)
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]container.Summary{c}, nil)
links, err := tested.getLinks(context.Background(), testProject, s, 1)
assert.NilError(t, err)
@ -119,7 +120,7 @@ func TestServiceLinks(t *testing.T) {
c := testContainer("db", dbContainerName, false)
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]container.Summary{c}, nil)
links, err := tested.getLinks(context.Background(), testProject, s, 1)
assert.NilError(t, err)
@ -142,7 +143,7 @@ func TestServiceLinks(t *testing.T) {
s.Links = []string{"db:dbname"}
c := testContainer("db", dbContainerName, false)
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]container.Summary{c}, nil)
links, err := tested.getLinks(context.Background(), testProject, s, 1)
assert.NilError(t, err)
@ -167,7 +168,7 @@ func TestServiceLinks(t *testing.T) {
s.ExternalLinks = []string{"db1:db2"}
c := testContainer("db", dbContainerName, false)
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]container.Summary{c}, nil)
links, err := tested.getLinks(context.Background(), testProject, s, 1)
assert.NilError(t, err)
@ -196,7 +197,7 @@ func TestServiceLinks(t *testing.T) {
s.Labels = s.Labels.Add(api.OneoffLabel, "True")
c := testContainer("web", webContainerName, true)
containerListOptionsOneOff := containerType.ListOptions{
containerListOptionsOneOff := container.ListOptions{
Filters: filters.NewArgs(
projectFilter(testProject),
serviceFilter("web"),
@ -205,7 +206,7 @@ func TestServiceLinks(t *testing.T) {
),
All: true,
}
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptionsOneOff).Return([]moby.Container{c}, nil)
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptionsOneOff).Return([]container.Summary{c}, nil)
links, err := tested.getLinks(context.Background(), testProject, s, 1)
assert.NilError(t, err)
@ -268,7 +269,7 @@ func TestCreateMobyContainer(t *testing.T) {
cli.EXPECT().Client().Return(apiClient).AnyTimes()
cli.EXPECT().ConfigFile().Return(&configfile.ConfigFile{}).AnyTimes()
apiClient.EXPECT().DaemonHost().Return("").AnyTimes()
apiClient.EXPECT().ImageInspectWithRaw(gomock.Any(), gomock.Any()).Return(moby.ImageInspect{}, nil, nil).AnyTimes()
apiClient.EXPECT().ImageInspect(gomock.Any(), gomock.Any()).Return(image.InspectResponse{}, nil).AnyTimes()
// force `RuntimeVersion` to fetch again
runtimeVersion = runtimeVersionCache{}
apiClient.EXPECT().ServerVersion(gomock.Any()).Return(moby.Version{
@ -303,11 +304,11 @@ func TestCreateMobyContainer(t *testing.T) {
var falseBool bool
apiClient.EXPECT().ContainerCreate(gomock.Any(), gomock.Any(), gomock.Eq(
&containerType.HostConfig{
&container.HostConfig{
PortBindings: nat.PortMap{},
ExtraHosts: []string{},
Tmpfs: map[string]string{},
Resources: containerType.Resources{
Resources: container.Resources{
OomKillDisable: &falseBool,
},
NetworkMode: "b-moby-name",
@ -320,18 +321,18 @@ func TestCreateMobyContainer(t *testing.T) {
},
},
}), gomock.Any(), gomock.Any()).Times(1).Return(
containerType.CreateResponse{
container.CreateResponse{
ID: "an-id",
}, nil)
apiClient.EXPECT().ContainerInspect(gomock.Any(), gomock.Eq("an-id")).Times(1).Return(
moby.ContainerJSON{
ContainerJSONBase: &moby.ContainerJSONBase{
container.InspectResponse{
ContainerJSONBase: &container.ContainerJSONBase{
ID: "an-id",
Name: "a-name",
},
Config: &containerType.Config{},
NetworkSettings: &moby.NetworkSettings{},
Config: &container.Config{},
NetworkSettings: &container.NetworkSettings{},
}, nil)
apiClient.EXPECT().NetworkConnect(gomock.Any(), "a-moby-name", "an-id", gomock.Eq(
@ -357,7 +358,7 @@ func TestCreateMobyContainer(t *testing.T) {
cli.EXPECT().Client().Return(apiClient).AnyTimes()
cli.EXPECT().ConfigFile().Return(&configfile.ConfigFile{}).AnyTimes()
apiClient.EXPECT().DaemonHost().Return("").AnyTimes()
apiClient.EXPECT().ImageInspectWithRaw(gomock.Any(), gomock.Any()).Return(moby.ImageInspect{}, nil, nil).AnyTimes()
apiClient.EXPECT().ImageInspect(gomock.Any(), gomock.Any()).Return(image.InspectResponse{}, nil).AnyTimes()
// force `RuntimeVersion` to fetch fresh version
runtimeVersion = runtimeVersionCache{}
apiClient.EXPECT().ServerVersion(gomock.Any()).Return(moby.Version{
@ -392,11 +393,11 @@ func TestCreateMobyContainer(t *testing.T) {
var falseBool bool
apiClient.EXPECT().ContainerCreate(gomock.Any(), gomock.Any(), gomock.Eq(
&containerType.HostConfig{
&container.HostConfig{
PortBindings: nat.PortMap{},
ExtraHosts: []string{},
Tmpfs: map[string]string{},
Resources: containerType.Resources{
Resources: container.Resources{
OomKillDisable: &falseBool,
},
NetworkMode: "b-moby-name",
@ -413,18 +414,18 @@ func TestCreateMobyContainer(t *testing.T) {
},
},
}), gomock.Any(), gomock.Any()).Times(1).Return(
containerType.CreateResponse{
container.CreateResponse{
ID: "an-id",
}, nil)
apiClient.EXPECT().ContainerInspect(gomock.Any(), gomock.Eq("an-id")).Times(1).Return(
moby.ContainerJSON{
ContainerJSONBase: &moby.ContainerJSONBase{
container.InspectResponse{
ContainerJSONBase: &container.ContainerJSONBase{
ID: "an-id",
Name: "a-name",
},
Config: &containerType.Config{},
NetworkSettings: &moby.NetworkSettings{},
Config: &container.Config{},
NetworkSettings: &container.NetworkSettings{},
}, nil)
_, err := tested.createMobyContainer(context.Background(), &project, service, "test", 0, nil, createOptions{
@ -432,5 +433,4 @@ func TestCreateMobyContainer(t *testing.T) {
}, progress.ContextWriter(context.TODO()))
assert.NilError(t, err)
})
}

View File

@ -139,7 +139,6 @@ func (s *composeService) listContainersTargetedForCopy(ctx context.Context, proj
}
if direction == fromService {
return containers[:1], err
}
return containers, err
}

View File

@ -34,11 +34,12 @@ import (
pathutil "github.com/docker/compose/v2/internal/paths"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/progress"
"github.com/docker/compose/v2/pkg/prompt"
"github.com/docker/compose/v2/pkg/utils"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/blkiodev"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/strslice"
@ -80,12 +81,6 @@ func (s *composeService) create(ctx context.Context, project *types.Project, opt
return err
}
var observedState Containers
observedState, err = s.getContainers(ctx, project.Name, oneOffInclude, true)
if err != nil {
return err
}
err = s.ensureImagesExists(ctx, project, options.Build, options.QuietPull)
if err != nil {
return err
@ -93,14 +88,21 @@ func (s *composeService) create(ctx context.Context, project *types.Project, opt
prepareNetworks(project)
if err := s.ensureNetworks(ctx, project.Networks); err != nil {
networks, err := s.ensureNetworks(ctx, project)
if err != nil {
return err
}
if err := s.ensureProjectVolumes(ctx, project); err != nil {
volumes, err := s.ensureProjectVolumes(ctx, project, options.AssumeYes)
if err != nil {
return err
}
var observedState Containers
observedState, err = s.getContainers(ctx, project.Name, oneOffInclude, true)
if err != nil {
return err
}
orphans := observedState.filter(isOrphaned(project))
if len(orphans) > 0 && !options.IgnoreOrphans {
if options.RemoveOrphans {
@ -115,38 +117,43 @@ func (s *composeService) create(ctx context.Context, project *types.Project, opt
"--remove-orphans flag to clean it up.", orphans.names())
}
}
return newConvergence(options.Services, observedState, s).apply(ctx, project, options)
return newConvergence(options.Services, observedState, networks, volumes, s).apply(ctx, project, options)
}
func prepareNetworks(project *types.Project) {
for k, nw := range project.Networks {
nw.Labels = nw.Labels.Add(api.NetworkLabel, k)
nw.Labels = nw.Labels.Add(api.ProjectLabel, project.Name)
nw.Labels = nw.Labels.Add(api.VersionLabel, api.ComposeVersion)
nw.CustomLabels = nw.CustomLabels.
Add(api.NetworkLabel, k).
Add(api.ProjectLabel, project.Name).
Add(api.VersionLabel, api.ComposeVersion)
project.Networks[k] = nw
}
}
func (s *composeService) ensureNetworks(ctx context.Context, networks types.Networks) error {
for i, nw := range networks {
err := s.ensureNetwork(ctx, &nw)
func (s *composeService) ensureNetworks(ctx context.Context, project *types.Project) (map[string]string, error) {
networks := map[string]string{}
for name, nw := range project.Networks {
id, err := s.ensureNetwork(ctx, project, name, &nw)
if err != nil {
return err
return nil, err
}
networks[i] = nw
networks[name] = id
project.Networks[name] = nw
}
return nil
return networks, nil
}
func (s *composeService) ensureProjectVolumes(ctx context.Context, project *types.Project) error {
func (s *composeService) ensureProjectVolumes(ctx context.Context, project *types.Project, assumeYes bool) (map[string]string, error) {
ids := map[string]string{}
for k, volume := range project.Volumes {
volume.Labels = volume.Labels.Add(api.VolumeLabel, k)
volume.Labels = volume.Labels.Add(api.ProjectLabel, project.Name)
volume.Labels = volume.Labels.Add(api.VersionLabel, api.ComposeVersion)
err := s.ensureVolume(ctx, volume, project.Name)
volume.CustomLabels = volume.CustomLabels.Add(api.VolumeLabel, k)
volume.CustomLabels = volume.CustomLabels.Add(api.ProjectLabel, project.Name)
volume.CustomLabels = volume.CustomLabels.Add(api.VersionLabel, api.ComposeVersion)
id, err := s.ensureVolume(ctx, k, volume, project, assumeYes)
if err != nil {
return err
return nil, err
}
ids[k] = id
}
err := func() error {
@ -198,18 +205,17 @@ func (s *composeService) ensureProjectVolumes(ctx context.Context, project *type
}
return nil
}()
if err != nil {
progress.ContextWriter(ctx).TailMsgf("Failed to prepare Synchronized file shares: %v", err)
}
return nil
return ids, nil
}
func (s *composeService) getCreateConfigs(ctx context.Context,
p *types.Project,
service types.ServiceConfig,
number int,
inherit *moby.Container,
inherit *container.Summary,
opts createOptions,
) (createConfigs, error) {
labels, err := s.prepareLabels(opts.Labels, service, number)
@ -252,7 +258,7 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
if err != nil {
return createConfigs{}, err
}
var containerConfig = container.Config{
containerConfig := container.Config{
Hostname: service.Hostname,
Domainname: service.DomainName,
User: service.User,
@ -268,7 +274,7 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
WorkingDir: service.WorkingDir,
Entrypoint: entrypoint,
NetworkDisabled: service.NetworkMode == "disabled",
MacAddress: macAddress,
MacAddress: macAddress, // Field is deprecated since API v1.44, but kept for compatibility with older API versions.
Labels: labels,
StopSignal: service.StopSignal,
Env: ToMobyEnv(env),
@ -410,7 +416,7 @@ func (s *composeService) prepareContainerMACAddress(ctx context.Context, service
return "", fmt.Errorf("a MAC address is specified for multiple networks (%s), but this feature requires Docker Engine 1.44 or later (currently: %s)", strings.Join(withMacAddress, ", "), version)
}
if mainNw != nil {
if mainNw != nil && mainNw.MacAddress != "" {
macAddress = mainNw.MacAddress
}
}
@ -418,11 +424,11 @@ func (s *composeService) prepareContainerMACAddress(ctx context.Context, service
return macAddress, nil
}
func getAliases(project *types.Project, service types.ServiceConfig, serviceIndex int, networkKey string, useNetworkAliases bool) []string {
func getAliases(project *types.Project, service types.ServiceConfig, serviceIndex int, cfg *types.ServiceNetworkConfig, useNetworkAliases bool) []string {
aliases := []string{getContainerName(project.Name, service, serviceIndex)}
if useNetworkAliases {
aliases = append(aliases, service.Name)
if cfg := service.Networks[networkKey]; cfg != nil {
if cfg != nil {
aliases = append(aliases, cfg.Aliases...)
}
}
@ -437,6 +443,7 @@ func createEndpointSettings(p *types.Project, service types.ServiceConfig, servi
ipv6Address string
macAddress string
driverOpts types.Options
gwPriority int
)
if config != nil {
ipv4Address = config.Ipv4Address
@ -448,15 +455,17 @@ func createEndpointSettings(p *types.Project, service types.ServiceConfig, servi
}
macAddress = config.MacAddress
driverOpts = config.DriverOpts
gwPriority = config.GatewayPriority
}
return &network.EndpointSettings{
Aliases: getAliases(p, service, serviceIndex, networkKey, useNetworkAliases),
Aliases: getAliases(p, service, serviceIndex, config, useNetworkAliases),
Links: links,
IPAddress: ipv4Address,
IPv6Gateway: ipv6Address,
IPAMConfig: ipam,
MacAddress: macAddress,
DriverOpts: driverOpts,
GwPriority: gwPriority,
}
}
@ -477,10 +486,10 @@ func parseSecurityOpts(p *types.Project, securityOpts []string) ([]string, bool,
if strings.Contains(opt, ":") {
con = strings.SplitN(opt, ":", 2)
} else {
return securityOpts, false, fmt.Errorf("Invalid security-opt: %q", opt)
return securityOpts, false, fmt.Errorf("invalid security-opt: %q", opt)
}
}
if con[0] == "seccomp" && con[1] != "unconfined" {
if con[0] == "seccomp" && con[1] != "unconfined" && con[1] != "builtin" {
f, err := os.ReadFile(p.RelativePath(con[1]))
if err != nil {
return securityOpts, false, fmt.Errorf("opening seccomp profile (%s) failed: %w", con[1], err)
@ -542,23 +551,28 @@ func defaultNetworkSettings(
primaryNetworkKey = "default"
}
primaryNetworkMobyNetworkName := project.Networks[primaryNetworkKey].Name
endpointsConfig := map[string]*network.EndpointSettings{
primaryNetworkMobyNetworkName: createEndpointSettings(project, service, serviceIndex, primaryNetworkKey, links, useNetworkAliases),
}
primaryNetworkEndpoint := createEndpointSettings(project, service, serviceIndex, primaryNetworkKey, links, useNetworkAliases)
endpointsConfig := map[string]*network.EndpointSettings{}
// Starting from API version 1.44, the Engine will take several EndpointsConfigs
// so we can pass all the extra networks we want the container to be connected to
// in the network configuration instead of connecting the container to each extra
// network individually after creation.
if versions.GreaterThanOrEqualTo(version, "1.44") && len(service.Networks) > 1 {
serviceNetworks := service.NetworksByPriority()
for _, networkKey := range serviceNetworks[1:] {
mobyNetworkName := project.Networks[networkKey].Name
epSettings := createEndpointSettings(project, service, serviceIndex, networkKey, links, useNetworkAliases)
endpointsConfig[mobyNetworkName] = epSettings
if versions.GreaterThanOrEqualTo(version, "1.44") {
if len(service.Networks) > 1 {
serviceNetworks := service.NetworksByPriority()
for _, networkKey := range serviceNetworks[1:] {
mobyNetworkName := project.Networks[networkKey].Name
epSettings := createEndpointSettings(project, service, serviceIndex, networkKey, links, useNetworkAliases)
endpointsConfig[mobyNetworkName] = epSettings
}
}
if primaryNetworkEndpoint.MacAddress == "" {
primaryNetworkEndpoint.MacAddress = service.MacAddress
}
}
endpointsConfig[primaryNetworkMobyNetworkName] = primaryNetworkEndpoint
networkConfig := &network.NetworkingConfig{
EndpointsConfig: endpointsConfig,
}
@ -818,13 +832,13 @@ func (s *composeService) buildContainerVolumes(
ctx context.Context,
p types.Project,
service types.ServiceConfig,
inherit *moby.Container,
inherit *container.Summary,
) ([]string, []mount.Mount, error) {
var mounts []mount.Mount
var binds []string
image := api.GetImageNameOrDefault(service, p.Name)
imgInspect, _, err := s.apiClient().ImageInspectWithRaw(ctx, image)
img := api.GetImageNameOrDefault(service, p.Name)
imgInspect, err := s.apiClient().ImageInspect(ctx, img)
if err != nil {
return nil, nil, err
}
@ -879,8 +893,8 @@ func requireMountAPI(bind *types.ServiceVolumeBind) bool {
}
}
func buildContainerMountOptions(p types.Project, s types.ServiceConfig, img moby.ImageInspect, inherit *moby.Container) ([]mount.Mount, error) {
var mounts = map[string]mount.Mount{}
func buildContainerMountOptions(p types.Project, s types.ServiceConfig, img image.InspectResponse, inherit *container.Summary) ([]mount.Mount, error) {
mounts := map[string]mount.Mount{}
if inherit != nil {
for _, m := range inherit.Mounts {
if m.Type == "tmpfs" {
@ -966,7 +980,7 @@ func fillBindMounts(p types.Project, s types.ServiceConfig, m map[string]mount.M
}
func buildContainerConfigMounts(p types.Project, s types.ServiceConfig) ([]mount.Mount, error) {
var mounts = map[string]mount.Mount{}
mounts := map[string]mount.Mount{}
configsBaseDir := "/"
for _, config := range s.Configs {
@ -983,10 +997,10 @@ func buildContainerConfigMounts(p types.Project, s types.ServiceConfig) ([]mount
}
if definedConfig.Driver != "" {
return nil, errors.New("Docker Compose does not support configs.*.driver")
return nil, errors.New("Docker Compose does not support configs.*.driver") //nolint:staticcheck
}
if definedConfig.TemplateDriver != "" {
return nil, errors.New("Docker Compose does not support configs.*.template_driver")
return nil, errors.New("Docker Compose does not support configs.*.template_driver") //nolint:staticcheck
}
if definedConfig.Environment != "" || definedConfig.Content != "" {
@ -1016,7 +1030,7 @@ func buildContainerConfigMounts(p types.Project, s types.ServiceConfig) ([]mount
}
func buildContainerSecretMounts(p types.Project, s types.ServiceConfig) ([]mount.Mount, error) {
var mounts = map[string]mount.Mount{}
mounts := map[string]mount.Mount{}
secretsDir := "/run/secrets/"
for _, secret := range s.Secrets {
@ -1033,10 +1047,10 @@ func buildContainerSecretMounts(p types.Project, s types.ServiceConfig) ([]mount
}
if definedSecret.Driver != "" {
return nil, errors.New("Docker Compose does not support secrets.*.driver")
return nil, errors.New("Docker Compose does not support secrets.*.driver") //nolint:staticcheck
}
if definedSecret.TemplateDriver != "" {
return nil, errors.New("Docker Compose does not support secrets.*.template_driver")
return nil, errors.New("Docker Compose does not support secrets.*.template_driver") //nolint:staticcheck
}
if definedSecret.Environment != "" {
@ -1200,24 +1214,21 @@ func buildTmpfsOptions(tmpfs *types.ServiceVolumeTmpfs) *mount.TmpfsOptions {
}
}
func (s *composeService) ensureNetwork(ctx context.Context, n *types.NetworkConfig) error {
func (s *composeService) ensureNetwork(ctx context.Context, project *types.Project, name string, n *types.NetworkConfig) (string, error) {
if n.External {
return s.resolveExternalNetwork(ctx, n)
}
err := s.resolveOrCreateNetwork(ctx, n)
id, err := s.resolveOrCreateNetwork(ctx, project, name, n)
if errdefs.IsConflict(err) {
// Maybe another execution of `docker compose up|run` created same network
// let's retry once
return s.resolveOrCreateNetwork(ctx, n)
return s.resolveOrCreateNetwork(ctx, project, name, n)
}
return err
return id, err
}
func (s *composeService) resolveOrCreateNetwork(ctx context.Context, n *types.NetworkConfig) error { //nolint:gocyclo
expectedNetworkLabel := n.Labels[api.NetworkLabel]
expectedProjectLabel := n.Labels[api.ProjectLabel]
func (s *composeService) resolveOrCreateNetwork(ctx context.Context, project *types.Project, name string, n *types.NetworkConfig) (string, error) { //nolint:gocyclo
// First, try to find a unique network matching by name or ID
inspect, err := s.apiClient().NetworkInspect(ctx, n.Name, network.InspectOptions{})
if err == nil {
@ -1228,20 +1239,33 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, n *types.Ne
if !ok {
logrus.Warnf("a network with name %s exists but was not created by compose.\n"+
"Set `external: true` to use an existing network", n.Name)
} else if p != expectedProjectLabel {
} else if p != project.Name {
logrus.Warnf("a network with name %s exists but was not created for project %q.\n"+
"Set `external: true` to use an existing network", n.Name, expectedProjectLabel)
"Set `external: true` to use an existing network", n.Name, project.Name)
}
if inspect.Labels[api.NetworkLabel] != expectedNetworkLabel {
return fmt.Errorf(
if inspect.Labels[api.NetworkLabel] != name {
return "", fmt.Errorf(
"network %s was found but has incorrect label %s set to %q (expected: %q)",
n.Name,
api.NetworkLabel,
inspect.Labels[api.NetworkLabel],
expectedNetworkLabel,
name,
)
}
return nil
hash := inspect.Labels[api.ConfigHashLabel]
expected, err := NetworkHash(n)
if err != nil {
return "", err
}
if hash == "" || hash == expected {
return inspect.ID, nil
}
err = s.removeDivergedNetwork(ctx, project, name, n)
if err != nil {
return "", err
}
}
}
// ignore other errors. Typically, an ambiguous request by name results in some generic `invalidParameter` error
@ -1251,7 +1275,7 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, n *types.Ne
Filters: filters.NewArgs(filters.Arg("name", n.Name)),
})
if err != nil {
return err
return "", err
}
// NetworkList Matches all or part of a network name, so we have to filter for a strict match
@ -1260,9 +1284,9 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, n *types.Ne
})
for _, net := range networks {
if net.Labels[api.ProjectLabel] == expectedProjectLabel &&
net.Labels[api.NetworkLabel] == expectedNetworkLabel {
return nil
if net.Labels[api.ProjectLabel] == project.Name &&
net.Labels[api.NetworkLabel] == name {
return net.ID, nil
}
}
@ -1272,7 +1296,7 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, n *types.Ne
if len(networks) > 0 {
logrus.Warnf("a network with name %s exists but was not created by compose.\n"+
"Set `external: true` to use an existing network", n.Name)
return nil
return networks[0].ID, nil
}
var ipam *network.IPAM
@ -1291,14 +1315,20 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, n *types.Ne
Config: config,
}
}
hash, err := NetworkHash(n)
if err != nil {
return "", err
}
n.CustomLabels = n.CustomLabels.Add(api.ConfigHashLabel, hash)
createOpts := network.CreateOptions{
Labels: n.Labels,
Labels: mergeLabels(n.Labels, n.CustomLabels),
Driver: n.Driver,
Options: n.DriverOpts,
Internal: n.Internal,
Attachable: n.Attachable,
IPAM: ipam,
EnableIPv6: n.EnableIPv6,
EnableIPv4: n.EnableIPv4,
}
if n.Ipam.Driver != "" || len(n.Ipam.Config) > 0 {
@ -1318,20 +1348,47 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, n *types.Ne
}
createOpts.IPAM.Config = append(createOpts.IPAM.Config, config)
}
networkEventName := fmt.Sprintf("Network %s", n.Name)
w := progress.ContextWriter(ctx)
w.Event(progress.CreatingEvent(networkEventName))
_, err = s.apiClient().NetworkCreate(ctx, n.Name, createOpts)
resp, err := s.apiClient().NetworkCreate(ctx, n.Name, createOpts)
if err != nil {
w.Event(progress.ErrorEvent(networkEventName))
return fmt.Errorf("failed to create network %s: %w", n.Name, err)
return "", fmt.Errorf("failed to create network %s: %w", n.Name, err)
}
w.Event(progress.CreatedEvent(networkEventName))
return nil
return resp.ID, nil
}
func (s *composeService) resolveExternalNetwork(ctx context.Context, n *types.NetworkConfig) error {
func (s *composeService) removeDivergedNetwork(ctx context.Context, project *types.Project, name string, n *types.NetworkConfig) error {
// Remove services attached to this network to force recreation
var services []string
for _, service := range project.Services.Filter(func(config types.ServiceConfig) bool {
_, ok := config.Networks[name]
return ok
}) {
services = append(services, service.Name)
}
// Stop containers so we can remove network
// They will be restarted (actually: recreated) with the updated network
err := s.stop(ctx, project.Name, api.StopOptions{
Services: services,
Project: project,
})
if err != nil {
return err
}
err = s.apiClient().NetworkRemove(ctx, n.Name)
eventName := fmt.Sprintf("Network %s", n.Name)
progress.ContextWriter(ctx).Event(progress.RemovedEvent(eventName))
return err
}
func (s *composeService) resolveExternalNetwork(ctx context.Context, n *types.NetworkConfig) (string, error) {
// NetworkInspect will match on ID prefix, so NetworkList with a name
// filter is used to look for an exact match to prevent e.g. a network
// named `db` from getting erroneously matched to a network with an ID
@ -1339,16 +1396,15 @@ func (s *composeService) resolveExternalNetwork(ctx context.Context, n *types.Ne
networks, err := s.apiClient().NetworkList(ctx, network.ListOptions{
Filters: filters.NewArgs(filters.Arg("name", n.Name)),
})
if err != nil {
return err
return "", err
}
if len(networks) == 0 {
// in this instance, n.Name is really an ID
sn, err := s.apiClient().NetworkInspect(ctx, n.Name, network.InspectOptions{})
if err != nil && !errdefs.IsNotFound(err) {
return err
return "", err
}
networks = append(networks, sn)
}
@ -1363,40 +1419,40 @@ func (s *composeService) resolveExternalNetwork(ctx context.Context, n *types.Ne
switch len(networks) {
case 1:
return nil
return networks[0].ID, nil
case 0:
enabled, err := s.isSWarmEnabled(ctx)
if err != nil {
return err
return "", err
}
if enabled {
// Swarm nodes do not register overlay networks that were
// created on a different node unless they're in use.
// So we can't preemptively check network exists, but
// networkAttach will later fail anyway if network actually doesn't exist
return nil
return "swarm", nil
}
return fmt.Errorf("network %s declared as external, but could not be found", n.Name)
return "", fmt.Errorf("network %s declared as external, but could not be found", n.Name)
default:
return fmt.Errorf("multiple networks with name %q were found. Use network ID as `name` to avoid ambiguity", n.Name)
return "", fmt.Errorf("multiple networks with name %q were found. Use network ID as `name` to avoid ambiguity", n.Name)
}
}
func (s *composeService) ensureVolume(ctx context.Context, volume types.VolumeConfig, project string) error {
func (s *composeService) ensureVolume(ctx context.Context, name string, volume types.VolumeConfig, project *types.Project, assumeYes bool) (string, error) {
inspected, err := s.apiClient().VolumeInspect(ctx, volume.Name)
if err != nil {
if !errdefs.IsNotFound(err) {
return err
return "", err
}
if volume.External {
return fmt.Errorf("external volume %q not found", volume.Name)
return "", fmt.Errorf("external volume %q not found", volume.Name)
}
err := s.createVolume(ctx, volume)
return err
err = s.createVolume(ctx, volume)
return volume.Name, err
}
if volume.External {
return nil
return volume.Name, nil
}
// Volume exists with name, but let's double-check this is the expected one
@ -1404,18 +1460,86 @@ func (s *composeService) ensureVolume(ctx context.Context, volume types.VolumeCo
if !ok {
logrus.Warnf("volume %q already exists but was not created by Docker Compose. Use `external: true` to use an existing volume", volume.Name)
}
if ok && p != project {
logrus.Warnf("volume %q already exists but was created for project %q (expected %q). Use `external: true` to use an existing volume", volume.Name, p, project)
if ok && p != project.Name {
logrus.Warnf("volume %q already exists but was created for project %q (expected %q). Use `external: true` to use an existing volume", volume.Name, p, project.Name)
}
return nil
expected, err := VolumeHash(volume)
if err != nil {
return "", err
}
actual, ok := inspected.Labels[api.ConfigHashLabel]
if ok && actual != expected {
confirm := assumeYes
if !assumeYes {
msg := fmt.Sprintf("Volume %q exists but doesn't match configuration in compose file. Recreate (data will be lost)?", volume.Name)
confirm, err = prompt.NewPrompt(s.stdin(), s.stdout()).Confirm(msg, false)
if err != nil {
return "", err
}
}
if confirm {
err = s.removeDivergedVolume(ctx, name, volume, project)
if err != nil {
return "", err
}
return volume.Name, s.createVolume(ctx, volume)
}
}
return inspected.Name, nil
}
func (s *composeService) removeDivergedVolume(ctx context.Context, name string, volume types.VolumeConfig, project *types.Project) error {
// Remove services mounting divergent volume
var services []string
for _, service := range project.Services.Filter(func(config types.ServiceConfig) bool {
for _, cfg := range config.Volumes {
if cfg.Source == name {
return true
}
}
return false
}) {
services = append(services, service.Name)
}
err := s.stop(ctx, project.Name, api.StopOptions{
Services: services,
Project: project,
})
if err != nil {
return err
}
containers, err := s.getContainers(ctx, project.Name, oneOffExclude, true, services...)
if err != nil {
return err
}
// FIXME (ndeloof) we have to remove container so we can recreate volume
// but doing so we can't inherit anonymous volumes from previous instance
err = s.remove(ctx, containers, api.RemoveOptions{
Services: services,
Project: project,
})
if err != nil {
return err
}
return s.apiClient().VolumeRemove(ctx, volume.Name, true)
}
func (s *composeService) createVolume(ctx context.Context, volume types.VolumeConfig) error {
eventName := fmt.Sprintf("Volume %q", volume.Name)
w := progress.ContextWriter(ctx)
w.Event(progress.CreatingEvent(eventName))
_, err := s.apiClient().VolumeCreate(ctx, volumetypes.CreateOptions{
Labels: volume.Labels,
hash, err := VolumeHash(volume)
if err != nil {
return err
}
volume.CustomLabels.Add(api.ConfigHashLabel, hash)
_, err = s.apiClient().VolumeCreate(ctx, volumetypes.CreateOptions{
Labels: mergeLabels(volume.Labels, volume.CustomLabels),
Name: volume.Name,
Driver: volume.Driver,
DriverOpts: volume.DriverOpts,

View File

@ -22,13 +22,14 @@ import (
"sort"
"testing"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image"
"gotest.tools/v3/assert/cmp"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/docker/api/types/network"
composetypes "github.com/compose-spec/compose-go/v2/types"
moby "github.com/docker/docker/api/types"
mountTypes "github.com/docker/docker/api/types/mount"
"gotest.tools/v3/assert"
@ -92,7 +93,7 @@ func TestPrepareNetworkLabels(t *testing.T) {
Networks: composetypes.Networks(map[string]composetypes.NetworkConfig{"skynet": {}}),
}
prepareNetworks(&project)
assert.DeepEqual(t, project.Networks["skynet"].Labels, composetypes.Labels(map[string]string{
assert.DeepEqual(t, project.Networks["skynet"].CustomLabels, composetypes.Labels(map[string]string{
"com.docker.compose.network": "skynet",
"com.docker.compose.project": "myProject",
"com.docker.compose.version": api.ComposeVersion,
@ -140,8 +141,8 @@ func TestBuildContainerMountOptions(t *testing.T) {
}),
}
inherit := &moby.Container{
Mounts: []moby.MountPoint{
inherit := &container.Summary{
Mounts: []container.MountPoint{
{
Type: composetypes.VolumeTypeVolume,
Destination: "/var/myvolume1",
@ -153,7 +154,7 @@ func TestBuildContainerMountOptions(t *testing.T) {
},
}
mounts, err := buildContainerMountOptions(project, project.Services["myService"], moby.ImageInspect{}, inherit)
mounts, err := buildContainerMountOptions(project, project.Services["myService"], image.InspectResponse{}, inherit)
sort.Slice(mounts, func(i, j int) bool {
return mounts[i].Target < mounts[j].Target
})
@ -165,7 +166,7 @@ func TestBuildContainerMountOptions(t *testing.T) {
assert.Equal(t, mounts[2].VolumeOptions.Subpath, "etc")
assert.Equal(t, mounts[3].Target, "\\\\.\\pipe\\docker_engine")
mounts, err = buildContainerMountOptions(project, project.Services["myService"], moby.ImageInspect{}, inherit)
mounts, err = buildContainerMountOptions(project, project.Services["myService"], image.InspectResponse{}, inherit)
sort.Slice(mounts, func(i, j int) bool {
return mounts[i].Target < mounts[j].Target
})
@ -278,33 +279,26 @@ func TestDefaultNetworkSettings(t *testing.T) {
}
func TestCreateEndpointSettings(t *testing.T) {
eps := createEndpointSettings(
&composetypes.Project{
Name: "projName",
},
composetypes.ServiceConfig{
Name: "serviceName",
ContainerName: "containerName",
Networks: map[string]*composetypes.ServiceNetworkConfig{
"netName": {
Priority: 100,
Aliases: []string{"alias1", "alias2"},
Ipv4Address: "10.16.17.18",
Ipv6Address: "fdb4:7a7f:373a:3f0c::42",
LinkLocalIPs: []string{"169.254.10.20"},
MacAddress: "10:00:00:00:01",
DriverOpts: composetypes.Options{
"driverOpt1": "optval1",
"driverOpt2": "optval2",
},
eps := createEndpointSettings(&composetypes.Project{
Name: "projName",
}, composetypes.ServiceConfig{
Name: "serviceName",
ContainerName: "containerName",
Networks: map[string]*composetypes.ServiceNetworkConfig{
"netName": {
Priority: 100,
Aliases: []string{"alias1", "alias2"},
Ipv4Address: "10.16.17.18",
Ipv6Address: "fdb4:7a7f:373a:3f0c::42",
LinkLocalIPs: []string{"169.254.10.20"},
MacAddress: "10:00:00:00:01",
DriverOpts: composetypes.Options{
"driverOpt1": "optval1",
"driverOpt2": "optval2",
},
},
},
0, // serviceIndex
"netName", // networkKey
[]string{"link1", "link2"}, // links
true, // useNetworkAliases
)
}, 0, "netName", []string{"link1", "link2"}, true)
assert.Check(t, cmp.DeepEqual(eps, &network.EndpointSettings{
IPAMConfig: &network.EndpointIPAMConfig{
IPv4Address: "10.16.17.18",

View File

@ -172,7 +172,6 @@ func (t *graphTraversal) run(ctx context.Context, graph *Graph, eg *errgroup.Gro
continue
}
node := node
if !t.consume(node.Key) {
// another worker already visited this node
continue
@ -438,7 +437,6 @@ func (g *Graph) HasCycles() (bool, error) {
if !utils.StringContains(discovered, vertex.Key) && !utils.StringContains(finished, vertex.Key) {
var err error
discovered, finished, err = g.visit(vertex.Key, path, discovered, finished)
if err != nil {
return true, err
}

View File

@ -27,7 +27,6 @@ import (
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/progress"
"github.com/docker/compose/v2/pkg/utils"
moby "github.com/docker/docker/api/types"
containerType "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
imageapi "github.com/docker/docker/api/types/image"
@ -84,8 +83,11 @@ func (s *composeService) down(ctx context.Context, projectName string, options a
}
err = InReverseDependencyOrder(ctx, project, func(c context.Context, service string) error {
serviceContainers := containers.filter(isService(service))
serv := project.Services[service]
if serv.Provider != nil {
return s.runPlugin(ctx, project, serv, "down")
}
serviceContainers := containers.filter(isService(service))
err := s.removeContainers(ctx, serviceContainers, &serv, options.Timeout, options.Volumes)
return err
}, WithRootNodesAndDown(options.Services))
@ -297,13 +299,13 @@ func (s *composeService) removeVolume(ctx context.Context, id string, w progress
return err
}
func (s *composeService) stopContainer(ctx context.Context, w progress.Writer, service *types.ServiceConfig, container moby.Container, timeout *time.Duration) error {
eventName := getContainerProgressName(container)
func (s *composeService) stopContainer(ctx context.Context, w progress.Writer, service *types.ServiceConfig, ctr containerType.Summary, timeout *time.Duration) error {
eventName := getContainerProgressName(ctr)
w.Event(progress.StoppingEvent(eventName))
if service != nil {
for _, hook := range service.PreStop {
err := s.runHook(ctx, container, *service, hook, nil)
err := s.runHook(ctx, ctr, *service, hook, nil)
if err != nil {
return err
}
@ -311,7 +313,7 @@ func (s *composeService) stopContainer(ctx context.Context, w progress.Writer, s
}
timeoutInSecond := utils.DurationSecondToInt(timeout)
err := s.apiClient().ContainerStop(ctx, container.ID, containerType.StopOptions{Timeout: timeoutInSecond})
err := s.apiClient().ContainerStop(ctx, ctr.ID, containerType.StopOptions{Timeout: timeoutInSecond})
if err != nil {
w.Event(progress.ErrorMessageEvent(eventName, "Error while Stopping"))
return err
@ -320,32 +322,30 @@ func (s *composeService) stopContainer(ctx context.Context, w progress.Writer, s
return nil
}
func (s *composeService) stopContainers(ctx context.Context, w progress.Writer, serv *types.ServiceConfig, containers []moby.Container, timeout *time.Duration) error {
func (s *composeService) stopContainers(ctx context.Context, w progress.Writer, serv *types.ServiceConfig, containers []containerType.Summary, timeout *time.Duration) error {
eg, ctx := errgroup.WithContext(ctx)
for _, container := range containers {
container := container
for _, ctr := range containers {
eg.Go(func() error {
return s.stopContainer(ctx, w, serv, container, timeout)
return s.stopContainer(ctx, w, serv, ctr, timeout)
})
}
return eg.Wait()
}
func (s *composeService) removeContainers(ctx context.Context, containers []moby.Container, service *types.ServiceConfig, timeout *time.Duration, volumes bool) error {
func (s *composeService) removeContainers(ctx context.Context, containers []containerType.Summary, service *types.ServiceConfig, timeout *time.Duration, volumes bool) error {
eg, _ := errgroup.WithContext(ctx)
for _, container := range containers {
container := container
for _, ctr := range containers {
eg.Go(func() error {
return s.stopAndRemoveContainer(ctx, container, service, timeout, volumes)
return s.stopAndRemoveContainer(ctx, ctr, service, timeout, volumes)
})
}
return eg.Wait()
}
func (s *composeService) stopAndRemoveContainer(ctx context.Context, container moby.Container, service *types.ServiceConfig, timeout *time.Duration, volumes bool) error {
func (s *composeService) stopAndRemoveContainer(ctx context.Context, ctr containerType.Summary, service *types.ServiceConfig, timeout *time.Duration, volumes bool) error {
w := progress.ContextWriter(ctx)
eventName := getContainerProgressName(container)
err := s.stopContainer(ctx, w, service, container, timeout)
eventName := getContainerProgressName(ctr)
err := s.stopContainer(ctx, w, service, ctr, timeout)
if errdefs.IsNotFound(err) {
w.Event(progress.RemovedEvent(eventName))
return nil
@ -354,7 +354,7 @@ func (s *composeService) stopAndRemoveContainer(ctx context.Context, container m
return err
}
w.Event(progress.RemovingEvent(eventName))
err = s.apiClient().ContainerRemove(ctx, container.ID, containerType.RemoveOptions{
err = s.apiClient().ContainerRemove(ctx, ctr.ID, containerType.RemoveOptions{
Force: true,
RemoveVolumes: volumes,
})

View File

@ -25,8 +25,7 @@ import (
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/streams"
moby "github.com/docker/docker/api/types"
containerType "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/network"
@ -49,7 +48,7 @@ func TestDown(t *testing.T) {
}
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
[]moby.Container{
[]container.Summary{
testContainer("service1", "123", false),
testContainer("service2", "456", false),
testContainer("service2", "789", false),
@ -70,14 +69,14 @@ func TestDown(t *testing.T) {
{ID: "def456", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}},
}, nil)
stopOptions := containerType.StopOptions{}
stopOptions := container.StopOptions{}
api.EXPECT().ContainerStop(gomock.Any(), "123", stopOptions).Return(nil)
api.EXPECT().ContainerStop(gomock.Any(), "456", stopOptions).Return(nil)
api.EXPECT().ContainerStop(gomock.Any(), "789", stopOptions).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "123", containerType.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "456", containerType.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "789", containerType.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "123", container.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "456", container.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "789", container.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{
Filters: filters.NewArgs(
@ -106,7 +105,7 @@ func TestDownWithGivenServices(t *testing.T) {
}
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
[]moby.Container{
[]container.Summary{
testContainer("service1", "123", false),
testContainer("service2", "456", false),
testContainer("service2", "789", false),
@ -127,10 +126,10 @@ func TestDownWithGivenServices(t *testing.T) {
{ID: "def456", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}},
}, nil)
stopOptions := containerType.StopOptions{}
stopOptions := container.StopOptions{}
api.EXPECT().ContainerStop(gomock.Any(), "123", stopOptions).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "123", containerType.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "123", container.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{
Filters: filters.NewArgs(
@ -158,7 +157,7 @@ func TestDownWithSpecifiedServiceButTheServicesAreNotRunning(t *testing.T) {
}
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
[]moby.Container{
[]container.Summary{
testContainer("service1", "123", false),
testContainer("service2", "456", false),
testContainer("service2", "789", false),
@ -195,7 +194,7 @@ func TestDownRemoveOrphans(t *testing.T) {
}
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(true)).Return(
[]moby.Container{
[]container.Summary{
testContainer("service1", "123", false),
testContainer("service2", "789", false),
testContainer("service_orphan", "321", true),
@ -211,16 +210,17 @@ func TestDownRemoveOrphans(t *testing.T) {
{
Name: "myProject_default",
Labels: map[string]string{compose.NetworkLabel: "default"},
}}, nil)
},
}, nil)
stopOptions := containerType.StopOptions{}
stopOptions := container.StopOptions{}
api.EXPECT().ContainerStop(gomock.Any(), "123", stopOptions).Return(nil)
api.EXPECT().ContainerStop(gomock.Any(), "789", stopOptions).Return(nil)
api.EXPECT().ContainerStop(gomock.Any(), "321", stopOptions).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "123", containerType.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "789", containerType.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "321", containerType.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "123", container.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "789", container.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "321", container.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{
Filters: filters.NewArgs(
@ -245,7 +245,7 @@ func TestDownRemoveVolumes(t *testing.T) {
}
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
[]moby.Container{testContainer("service1", "123", false)}, nil)
[]container.Summary{testContainer("service1", "123", false)}, nil)
api.EXPECT().VolumeList(
gomock.Any(),
volume.ListOptions{
@ -257,8 +257,8 @@ func TestDownRemoveVolumes(t *testing.T) {
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
Return(nil, nil)
api.EXPECT().ContainerStop(gomock.Any(), "123", containerType.StopOptions{}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "123", containerType.RemoveOptions{Force: true, RemoveVolumes: true}).Return(nil)
api.EXPECT().ContainerStop(gomock.Any(), "123", container.StopOptions{}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "123", container.RemoveOptions{Force: true, RemoveVolumes: true}).Return(nil)
api.EXPECT().VolumeRemove(gomock.Any(), "myProject_volume", true).Return(nil)
@ -290,7 +290,7 @@ func TestDownRemoveImages(t *testing.T) {
}
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).
Return([]moby.Container{
Return([]container.Summary{
testContainer("service1", "123", false),
}, nil).
AnyTimes()
@ -319,7 +319,7 @@ func TestDownRemoveImages(t *testing.T) {
"missing-named-image": false,
}
for img, exists := range imagesToBeInspected {
var resp moby.ImageInspect
var resp image.InspectResponse
var err error
if exists {
resp.RepoTags = []string{img}
@ -327,13 +327,13 @@ func TestDownRemoveImages(t *testing.T) {
err = errdefs.NotFound(fmt.Errorf("test specified that image %q should not exist", img))
}
api.EXPECT().ImageInspectWithRaw(gomock.Any(), img).
Return(resp, nil, err).
api.EXPECT().ImageInspect(gomock.Any(), img).
Return(resp, err).
AnyTimes()
}
api.EXPECT().ImageInspectWithRaw(gomock.Any(), "registry.example.com/remote-image-tagged:v1.0").
Return(moby.ImageInspect{RepoTags: []string{"registry.example.com/remote-image-tagged:v1.0"}}, nil, nil).
api.EXPECT().ImageInspect(gomock.Any(), "registry.example.com/remote-image-tagged:v1.0").
Return(image.InspectResponse{RepoTags: []string{"registry.example.com/remote-image-tagged:v1.0"}}, nil).
AnyTimes()
localImagesToBeRemoved := []string{
@ -378,10 +378,10 @@ func TestDownRemoveImages_NoLabel(t *testing.T) {
dockerCli: cli,
}
container := testContainer("service1", "123", false)
ctr := testContainer("service1", "123", false)
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
[]moby.Container{container}, nil)
[]container.Summary{ctr}, nil)
api.EXPECT().VolumeList(
gomock.Any(),
@ -403,11 +403,11 @@ func TestDownRemoveImages_NoLabel(t *testing.T) {
),
}).Return(nil, nil)
api.EXPECT().ImageInspectWithRaw(gomock.Any(), "testproject-service1").
Return(moby.ImageInspect{}, nil, nil)
api.EXPECT().ImageInspect(gomock.Any(), "testproject-service1").
Return(image.InspectResponse{}, nil)
api.EXPECT().ContainerStop(gomock.Any(), "123", containerType.StopOptions{}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "123", containerType.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().ContainerStop(gomock.Any(), "123", container.StopOptions{}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "123", container.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().ImageRemove(gomock.Any(), "testproject-service1:latest", image.RemoveOptions{}).Return(nil, nil)

View File

@ -21,10 +21,8 @@ import (
"strings"
)
var (
// isCaseInsensitiveEnvVars is true on platforms where environment variable names are treated case-insensitively.
isCaseInsensitiveEnvVars = (runtime.GOOS == "windows")
)
// isCaseInsensitiveEnvVars is true on platforms where environment variable names are treated case-insensitively.
var isCaseInsensitiveEnvVars = (runtime.GOOS == "windows")
// envResolver returns resolver for environment variables suitable for the current platform.
// Expected to be used with `MappingWithEquals.Resolve`.

View File

@ -1,71 +0,0 @@
/*
Copyright 2020 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package compose
import (
"errors"
"io/fs"
"github.com/compose-spec/compose-go/v2/errdefs"
)
// Error error to categorize failures and extract metrics info
type Error struct {
Err error
Category *FailureCategory
}
// WrapComposeError wraps the error if not nil, otherwise returns nil
func WrapComposeError(err error) error {
if err == nil {
return nil
}
return Error{
Err: err,
}
}
// WrapCategorisedComposeError wraps the error if not nil, otherwise returns nil
func WrapCategorisedComposeError(err error, failure FailureCategory) error {
if err == nil {
return nil
}
return Error{
Err: err,
Category: &failure,
}
}
// Unwrap get underlying error
func (e Error) Unwrap() error { return e.Err }
func (e Error) Error() string { return e.Err.Error() }
// GetMetricsFailureCategory get metrics status and error code corresponding to this error
func (e Error) GetMetricsFailureCategory() FailureCategory {
if e.Category != nil {
return *e.Category
}
var pathError *fs.PathError
if errors.As(e.Err, &pathError) {
return FileNotFoundFailure
}
if errdefs.IsNotFoundError(e.Err) {
return FileNotFoundFailure
}
return ComposeParseFailure
}

View File

@ -24,7 +24,7 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command/container"
"github.com/docker/compose/v2/pkg/api"
moby "github.com/docker/docker/api/types"
containerType "github.com/docker/docker/api/types/container"
)
func (s *composeService) Exec(ctx context.Context, projectName string, options api.RunOptions) (int, error) {
@ -57,6 +57,6 @@ func (s *composeService) Exec(ctx context.Context, projectName string, options a
return 0, err
}
func (s *composeService) getExecTarget(ctx context.Context, projectName string, opts api.RunOptions) (moby.Container, error) {
func (s *composeService) getExecTarget(ctx context.Context, projectName string, opts api.RunOptions) (containerType.Summary, error) {
return s.getSpecifiedContainer(ctx, projectName, oneOffInclude, false, opts.Service, opts.Index)
}

View File

@ -24,8 +24,7 @@ import (
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/utils"
moby "github.com/docker/docker/api/types"
containerType "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network"
@ -40,7 +39,7 @@ func (s *composeService) Generate(ctx context.Context, options api.GenerateOptio
filtersListNames.Add("name", containerName)
filtersListIDs.Add("id", containerName)
}
containers, err := s.apiClient().ContainerList(ctx, containerType.ListOptions{
containers, err := s.apiClient().ContainerList(ctx, container.ListOptions{
Filters: filtersListNames,
All: true,
})
@ -48,16 +47,16 @@ func (s *composeService) Generate(ctx context.Context, options api.GenerateOptio
return nil, err
}
containersByIds, err := s.apiClient().ContainerList(ctx, containerType.ListOptions{
containersByIds, err := s.apiClient().ContainerList(ctx, container.ListOptions{
Filters: filtersListIDs,
All: true,
})
if err != nil {
return nil, err
}
for _, container := range containersByIds {
if !utils.Contains(containers, container) {
containers = append(containers, container)
for _, ctr := range containersByIds {
if !utils.Contains(containers, ctr) {
containers = append(containers, ctr)
}
}
@ -68,7 +67,7 @@ func (s *composeService) Generate(ctx context.Context, options api.GenerateOptio
return s.createProjectFromContainers(containers, options.ProjectName)
}
func (s *composeService) createProjectFromContainers(containers []moby.Container, projectName string) (*types.Project, error) {
func (s *composeService) createProjectFromContainers(containers []container.Summary, projectName string) (*types.Project, error) {
project := &types.Project{}
services := types.Services{}
networks := types.Networks{}
@ -92,7 +91,6 @@ func (s *composeService) createProjectFromContainers(containers []moby.Container
Image: c.Image,
Labels: c.Labels,
}
}
service.Scale = increment(service.Scale)
@ -113,7 +111,7 @@ func (s *composeService) createProjectFromContainers(containers []moby.Container
return project, nil
}
func (s *composeService) extractComposeConfiguration(service *types.ServiceConfig, inspect moby.ContainerJSON, volumes types.Volumes, secrets types.Secrets, networks types.Networks) {
func (s *composeService) extractComposeConfiguration(service *types.ServiceConfig, inspect container.InspectResponse, volumes types.Volumes, secrets types.Secrets, networks types.Networks) {
service.Environment = types.NewMappingWithEquals(inspect.Config.Env)
if inspect.Config.Healthcheck != nil {
healthConfig := inspect.Config.Healthcheck
@ -145,7 +143,7 @@ func (s *composeService) extractComposeConfiguration(service *types.ServiceConfi
}
}
func (s *composeService) toComposeHealthCheck(healthConfig *containerType.HealthConfig) *types.HealthCheckConfig {
func (s *composeService) toComposeHealthCheck(healthConfig *container.HealthConfig) *types.HealthCheckConfig {
var healthCheck types.HealthCheckConfig
healthCheck.Test = healthConfig.Test
if healthConfig.Timeout != 0 {
@ -171,8 +169,9 @@ func (s *composeService) toComposeHealthCheck(healthConfig *containerType.Health
return &healthCheck
}
func (s *composeService) toComposeVolumes(volumes []moby.MountPoint) (map[string]types.VolumeConfig,
[]types.ServiceVolumeConfig, map[string]types.SecretConfig, []types.ServiceSecretConfig) {
func (s *composeService) toComposeVolumes(volumes []container.MountPoint) (map[string]types.VolumeConfig,
[]types.ServiceVolumeConfig, map[string]types.SecretConfig, []types.ServiceSecretConfig,
) {
volumeConfigs := make(map[string]types.VolumeConfig)
secretConfigs := make(map[string]types.SecretConfig)
var serviceVolumeConfigs []types.ServiceVolumeConfig
@ -227,7 +226,6 @@ func (s *composeService) toComposeNetwork(networks map[string]*network.EndpointS
networkConfigs[name] = types.NetworkConfig{
Internal: inspect.Internal,
}
}
serviceNetworkConfigs[name] = &types.ServiceNetworkConfig{
Aliases: net.Aliases,

View File

@ -41,3 +41,24 @@ func ServiceHash(o types.ServiceConfig) (string, error) {
}
return digest.SHA256.FromBytes(bytes).Encoded(), nil
}
// NetworkHash computes the configuration hash for a network.
func NetworkHash(o *types.NetworkConfig) (string, error) {
bytes, err := json.Marshal(o)
if err != nil {
return "", err
}
return digest.SHA256.FromBytes(bytes).Encoded(), nil
}
// VolumeHash computes the configuration hash for a volume.
func VolumeHash(o types.VolumeConfig) (string, error) {
if o.Driver == "" { // (TODO: jhrotko) This probably should be fixed in compose-go
o.Driver = "local"
}
bytes, err := json.Marshal(o)
if err != nil {
return "", err
}
return digest.SHA256.FromBytes(bytes).Encoded(), nil
}

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