From a96c305b253d66c10343d4a78d2df53c403ca31a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 17 Apr 2025 18:05:33 +0200 Subject: [PATCH 01/28] build: write --print output to stdout stdinfo should only be used for status/progress messages: it defaults to stderr and makes piping the output trickier. bakex always writes `docker buildx bake --print` always uses stdout. Signed-off-by: Simon Ser --- pkg/compose/build_bake.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/compose/build_bake.go b/pkg/compose/build_bake.go index 0340004271f..ac474bc77d4 100644 --- a/pkg/compose/build_bake.go +++ b/pkg/compose/build_bake.go @@ -220,7 +220,7 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project } if options.Print { - _, err = fmt.Fprintln(s.stdinfo(), string(b)) + _, err = fmt.Fprintln(s.stdout(), string(b)) return nil, err } logrus.Debugf("bake build config:\n%s", string(b)) From bf6b4472634749d9344faaa67c6dc2c67aea3eed Mon Sep 17 00:00:00 2001 From: skanehira Date: Thu, 17 Apr 2025 11:13:23 +0900 Subject: [PATCH 02/28] fix: concurrent map writes when pulling Signed-off-by: skanehira --- pkg/compose/pull.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/compose/pull.go b/pkg/compose/pull.go index fbe82afcb97..0e70c189213 100644 --- a/pkg/compose/pull.go +++ b/pkg/compose/pull.go @@ -24,6 +24,7 @@ import ( "fmt" "io" "strings" + "sync" "time" "github.com/compose-spec/compose-go/v2/types" @@ -322,9 +323,12 @@ func (s *composeService) pullRequiredImages(ctx context.Context, project *types. eg, ctx := errgroup.WithContext(ctx) eg.SetLimit(s.maxConcurrency) pulledImages := map[string]api.ImageSummary{} + var mutex sync.Mutex for name, service := range needPull { eg.Go(func() error { id, err := s.pullServiceImage(ctx, service, s.configFile(), w, quietPull, project.Environment["DOCKER_DEFAULT_PLATFORM"]) + mutex.Lock() + defer mutex.Unlock() pulledImages[name] = api.ImageSummary{ ID: id, Repository: service.Image, From 322c531a8c99c14708a72c9190fddc7361132699 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 10:50:58 +0000 Subject: [PATCH 03/28] build(deps): bump github.com/docker/cli Bumps [github.com/docker/cli](https://github.com/docker/cli) from 28.1.0+incompatible to 28.1.1+incompatible. - [Commits](https://github.com/docker/cli/compare/v28.1.0...v28.1.1) --- updated-dependencies: - dependency-name: github.com/docker/cli dependency-version: 28.1.1+incompatible dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a5953abbf1d..e317ae5e669 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/davecgh/go-spew v1.1.1 github.com/distribution/reference v0.6.0 github.com/docker/buildx v0.23.0 - github.com/docker/cli v28.1.0+incompatible + github.com/docker/cli v28.1.1+incompatible github.com/docker/cli-docs-tool v0.9.0 github.com/docker/docker v28.1.0+incompatible github.com/docker/go-connections v0.5.0 diff --git a/go.sum b/go.sum index 545b1156eb6..4e0e9e1593f 100644 --- a/go.sum +++ b/go.sum @@ -129,8 +129,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/buildx v0.23.0 h1:qoYhuWyZ6PVCrWbkxClLzBWDBCUkyFK6Chjzg6nU+V8= github.com/docker/buildx v0.23.0/go.mod h1:y/6Zf/y3Bf0zTWqgg8PuNFATcqnuhFmQuNf4VyrnPtg= -github.com/docker/cli v28.1.0+incompatible h1:WiJhUBbuIH/BsJtth+C1hPwra4P0nsKJiWy9ie5My5s= -github.com/docker/cli v28.1.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v28.1.1+incompatible h1:eyUemzeI45DY7eDPuwUcmDyDj1pM98oD5MdSpiItp8k= +github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli-docs-tool v0.9.0 h1:CVwQbE+ZziwlPqrJ7LRyUF6GvCA+6gj7MTCsayaK9t0= github.com/docker/cli-docs-tool v0.9.0/go.mod h1:ClrwlNW+UioiRyH9GiAOe1o3J/TsY3Tr1ipoypjAUtc= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= From f3bbfdae58c93d987c88307598afaa68f5cd81d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 12:41:42 +0000 Subject: [PATCH 04/28] build(deps): bump github.com/docker/docker Bumps [github.com/docker/docker](https://github.com/docker/docker) from 28.1.0+incompatible to 28.1.1+incompatible. - [Release notes](https://github.com/docker/docker/releases) - [Commits](https://github.com/docker/docker/compare/v28.1.0...v28.1.1) --- updated-dependencies: - dependency-name: github.com/docker/docker dependency-version: 28.1.1+incompatible dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e317ae5e669..9b335dc8266 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/docker/buildx v0.23.0 github.com/docker/cli v28.1.1+incompatible github.com/docker/cli-docs-tool v0.9.0 - github.com/docker/docker v28.1.0+incompatible + github.com/docker/docker v28.1.1+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 diff --git a/go.sum b/go.sum index 4e0e9e1593f..bb48048327f 100644 --- a/go.sum +++ b/go.sum @@ -136,8 +136,8 @@ github.com/docker/cli-docs-tool v0.9.0/go.mod h1:ClrwlNW+UioiRyH9GiAOe1o3J/TsY3T 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 v28.1.0+incompatible h1:4iqpcWQCt3Txcz7iWIb1U3SZ/n9ffo4U+ryY5/3eOp0= -github.com/docker/docker v28.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I= +github.com/docker/docker v28.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= From 8fd0c297f5fab23ea96e69fe4c82b6b29a284d68 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 12:51:32 +0000 Subject: [PATCH 05/28] build(deps): bump google.golang.org/grpc from 1.71.1 to 1.72.0 Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.71.1 to 1.72.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.71.1...v1.72.0) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-version: 1.72.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 9b335dc8266..b416f8aa942 100644 --- a/go.mod +++ b/go.mod @@ -57,7 +57,7 @@ require ( golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 golang.org/x/sync v0.13.0 golang.org/x/sys v0.32.0 - google.golang.org/grpc v1.71.1 + google.golang.org/grpc v1.72.0 gopkg.in/yaml.v3 v3.0.1 gotest.tools/v3 v3.5.2 tags.cncf.io/container-device-interface v1.0.1 @@ -186,13 +186,13 @@ require ( go.opentelemetry.io/proto/otlp v1.3.1 // indirect golang.org/x/crypto v0.37.0 // indirect golang.org/x/net v0.39.0 // indirect - golang.org/x/oauth2 v0.25.0 // indirect + golang.org/x/oauth2 v0.26.0 // indirect golang.org/x/term v0.31.0 // indirect golang.org/x/text v0.24.0 // indirect golang.org/x/time v0.11.0 // 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 + google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect + google.golang.org/protobuf v1.36.5 // 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 diff --git a/go.sum b/go.sum index bb48048327f..f0cf1a25bb9 100644 --- a/go.sum +++ b/go.sum @@ -568,8 +568,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= -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/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= +golang.org/x/oauth2 v0.26.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= @@ -626,15 +626,15 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T 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/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/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ= google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI= -google.golang.org/grpc v1.71.1/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= +google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM= +google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/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= From f46689a75edc872800ab9d7a985dcc3dfe00c5d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 13:05:17 +0000 Subject: [PATCH 06/28] build(deps): bump github.com/containerd/containerd/v2 Bumps [github.com/containerd/containerd/v2](https://github.com/containerd/containerd) from 2.0.4 to 2.0.5. - [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.4...v2.0.5) --- updated-dependencies: - dependency-name: github.com/containerd/containerd/v2 dependency-version: 2.0.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index b416f8aa942..8e67d2b2e70 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/buger/goterm v1.0.4 github.com/compose-spec/compose-go/v2 v2.6.0 - github.com/containerd/containerd/v2 v2.0.4 + github.com/containerd/containerd/v2 v2.0.5 github.com/containerd/platforms v1.0.0-rc.1 github.com/davecgh/go-spew v1.1.1 github.com/distribution/reference v0.6.0 @@ -186,7 +186,7 @@ require ( go.opentelemetry.io/proto/otlp v1.3.1 // indirect golang.org/x/crypto v0.37.0 // indirect golang.org/x/net v0.39.0 // indirect - golang.org/x/oauth2 v0.26.0 // indirect + golang.org/x/oauth2 v0.28.0 // indirect golang.org/x/term v0.31.0 // indirect golang.org/x/text v0.24.0 // indirect golang.org/x/time v0.11.0 // indirect diff --git a/go.sum b/go.sum index f0cf1a25bb9..0814618a1d9 100644 --- a/go.sum +++ b/go.sum @@ -90,8 +90,8 @@ github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0= github.com/containerd/containerd/api v1.8.0/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/containerd/v2 v2.0.5 h1:2vg/TjUXnaohAxiHnthQg8K06L9I4gdYEMcOLiMc8BQ= +github.com/containerd/containerd/v2 v2.0.5/go.mod h1:Qqo0UN43i2fX1FLkrSTCg6zcHNfjN7gEnx3NPRZI+N0= 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= @@ -568,8 +568,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= -golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= -golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= +golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= 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= From f5491328bbed562b2e1e7a0ed4573d486adb33f8 Mon Sep 17 00:00:00 2001 From: Guillaume Lours <705411+glours@users.noreply.github.com> Date: Tue, 22 Apr 2025 14:27:49 +0200 Subject: [PATCH 07/28] remove support of Synchronize File Shares integration with Docker Desktop Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com> --- go.mod | 2 - go.sum | 5 - internal/desktop/client.go | 209 ----------------- internal/desktop/file_shares.go | 384 -------------------------------- pkg/compose/create.go | 56 ----- pkg/compose/desktop.go | 18 -- pkg/compose/down.go | 8 - 7 files changed, 682 deletions(-) delete mode 100644 internal/desktop/file_shares.go diff --git a/go.mod b/go.mod index 8e67d2b2e70..b2450cfc34b 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,6 @@ require ( github.com/opencontainers/go-digest v1.0.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.9.1 @@ -193,7 +192,6 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect google.golang.org/protobuf v1.36.5 // 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 diff --git a/go.sum b/go.sum index 0814618a1d9..80ef4899f57 100644 --- a/go.sum +++ b/go.sum @@ -420,8 +420,6 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/r3labs/sse v0.0.0-20210224172625-26fe804710bc h1:zAsgcP8MhzAbhMnB1QQ2O7ZhWYVGYSR2iVcjzQuPV+o= -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.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= @@ -561,7 +559,6 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 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= @@ -637,8 +634,6 @@ google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwl google.golang.org/protobuf v1.36.5/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= -gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UDAkHu8BrjI= gopkg.in/cenkalti/backoff.v2 v2.2.1 h1:eJ9UAg01/HIHG987TwxvnzK2MgxXq97YY6rYDpY9aII= gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/desktop/client.go b/internal/desktop/client.go index 47b7aafc295..ab7fa908174 100644 --- a/internal/desktop/client.go +++ b/internal/desktop/client.go @@ -17,10 +17,8 @@ package desktop import ( - "bytes" "context" "encoding/json" - "errors" "fmt" "io" "net" @@ -29,7 +27,6 @@ import ( "github.com/docker/compose/v2/internal" "github.com/docker/compose/v2/internal/memnet" - "github.com/r3labs/sse" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) @@ -130,212 +127,6 @@ func (c *Client) FeatureFlags(ctx context.Context) (FeatureFlagResponse, error) return ret, nil } -type GetFileSharesConfigResponse struct { - Active bool `json:"active"` - Compose struct { - ManageBindMounts bool `json:"manageBindMounts"` - } -} - -func (c *Client) GetFileSharesConfig(ctx context.Context) (*GetFileSharesConfigResponse, error) { - req, err := c.newRequest(ctx, http.MethodGet, "/mutagen/file-shares/config", http.NoBody) - if err != nil { - return nil, err - } - - resp, err := c.client.Do(req) - if err != nil { - return nil, err - } - defer func() { - _ = resp.Body.Close() - }() - - if resp.StatusCode != http.StatusOK { - return nil, newHTTPStatusCodeError(resp) - } - - var ret GetFileSharesConfigResponse - if err := json.NewDecoder(resp.Body).Decode(&ret); err != nil { - return nil, err - } - return &ret, nil -} - -type CreateFileShareRequest struct { - HostPath string `json:"hostPath"` - Labels map[string]string `json:"labels,omitempty"` -} - -type CreateFileShareResponse struct { - FileShareID string `json:"fileShareID"` -} - -func (c *Client) CreateFileShare(ctx context.Context, r CreateFileShareRequest) (*CreateFileShareResponse, error) { - rawBody, err := json.Marshal(r) - if err != nil { - return nil, fmt.Errorf("failed to marshal request: %w", err) - } - - req, err := c.newRequest(ctx, http.MethodPost, "/mutagen/file-shares", bytes.NewReader(rawBody)) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", "application/json") - - resp, err := c.client.Do(req) - if err != nil { - return nil, err - } - defer func() { - _ = resp.Body.Close() - }() - - if resp.StatusCode != http.StatusOK { - errBody, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(errBody)) - } - - var ret CreateFileShareResponse - if err := json.NewDecoder(resp.Body).Decode(&ret); err != nil { - return nil, err - } - return &ret, nil -} - -type FileShareReceiverState struct { - TotalReceivedSize uint64 `json:"totalReceivedSize"` -} - -type FileShareEndpoint struct { - Path string `json:"path"` - TotalFileSize uint64 `json:"totalFileSize,omitempty"` - StagingProgress *FileShareReceiverState `json:"stagingProgress"` -} - -type FileShareSession struct { - SessionID string `json:"identifier"` - Alpha FileShareEndpoint `json:"alpha"` - Beta FileShareEndpoint `json:"beta"` - Labels map[string]string `json:"labels"` - Status string `json:"status"` -} - -func (c *Client) ListFileShares(ctx context.Context) ([]FileShareSession, error) { - req, err := c.newRequest(ctx, http.MethodGet, "/mutagen/file-shares", http.NoBody) - if err != nil { - return nil, err - } - - resp, err := c.client.Do(req) - if err != nil { - return nil, err - } - defer func() { - _ = resp.Body.Close() - }() - - if resp.StatusCode != http.StatusOK { - return nil, newHTTPStatusCodeError(resp) - } - - var ret []FileShareSession - if err := json.NewDecoder(resp.Body).Decode(&ret); err != nil { - return nil, err - } - return ret, nil -} - -func (c *Client) DeleteFileShare(ctx context.Context, id string) error { - req, err := c.newRequest(ctx, http.MethodDelete, "/mutagen/file-shares/"+id, http.NoBody) - if err != nil { - return err - } - - resp, err := c.client.Do(req) - if err != nil { - return err - } - defer func() { - _ = resp.Body.Close() - }() - - if resp.StatusCode < 200 || resp.StatusCode >= 300 { - return newHTTPStatusCodeError(resp) - } - return nil -} - -type EventMessage[T any] struct { - Value T - Error error -} - -func newHTTPStatusCodeError(resp *http.Response) error { - r := io.LimitReader(resp.Body, 2048) - body, err := io.ReadAll(r) - if err != nil { - return fmt.Errorf("http status code %d", resp.StatusCode) - } - return fmt.Errorf("http status code %d: %s", resp.StatusCode, string(body)) -} - -func (c *Client) StreamFileShares(ctx context.Context) (<-chan EventMessage[[]FileShareSession], error) { - req, err := c.newRequest(ctx, http.MethodGet, "/mutagen/file-shares/stream", http.NoBody) - if err != nil { - return nil, err - } - - resp, err := c.client.Do(req) - if err != nil { - return nil, err - } - - if resp.StatusCode < 200 || resp.StatusCode >= 300 { - _ = resp.Body.Close() - return nil, newHTTPStatusCodeError(resp) - } - - events := make(chan EventMessage[[]FileShareSession]) - go func(ctx context.Context) { - defer func() { - _ = resp.Body.Close() - close(events) - }() - if err := readEvents(ctx, resp.Body, events); err != nil { - select { - case <-ctx.Done(): - case events <- EventMessage[[]FileShareSession]{Error: err}: - } - } - }(ctx) - return events, nil -} - -func readEvents[T any](ctx context.Context, r io.Reader, events chan<- EventMessage[T]) error { - eventReader := sse.NewEventStreamReader(r) - for { - msg, err := eventReader.ReadEvent() - if errors.Is(err, io.EOF) { - return nil - } else if err != nil { - return fmt.Errorf("reading events: %w", err) - } - msg = bytes.TrimPrefix(msg, []byte("data: ")) - - var event T - if err := json.Unmarshal(msg, &event); err != nil { - return err - } - select { - case <-ctx.Done(): - return context.Cause(ctx) - case events <- EventMessage[T]{Value: event}: - // event was sent to channel, read next - } - } -} - func (c *Client) newRequest(ctx context.Context, method, path string, body io.Reader) (*http.Request, error) { req, err := http.NewRequestWithContext(ctx, method, backendURL(path), body) if err != nil { diff --git a/internal/desktop/file_shares.go b/internal/desktop/file_shares.go deleted file mode 100644 index 8c5c6e60331..00000000000 --- a/internal/desktop/file_shares.go +++ /dev/null @@ -1,384 +0,0 @@ -/* - 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 desktop - -import ( - "context" - "errors" - "fmt" - "strings" - "sync" - - "github.com/docker/compose/v2/internal/paths" - "github.com/docker/compose/v2/pkg/api" - "github.com/docker/compose/v2/pkg/progress" - "github.com/docker/go-units" - "github.com/sirupsen/logrus" -) - -// fileShareProgressID is the identifier used for the root grouping of file -// share events in the progress writer. -const fileShareProgressID = "Synchronized File Shares" - -// RemoveFileSharesForProject removes any Synchronized File Shares that were -// created by Compose for this project in the past if possible. -// -// Errors are not propagated; they are only sent to the progress writer. -func RemoveFileSharesForProject(ctx context.Context, c *Client, projectName string) { - w := progress.ContextWriter(ctx) - - existing, err := c.ListFileShares(ctx) - if err != nil { - w.TailMsgf("Synchronized File Shares not removed due to error: %v", err) - return - } - // filter the list first, so we can early return and not show the event if - // there's no sessions to clean up - var toRemove []FileShareSession - for _, share := range existing { - if share.Labels["com.docker.compose.project"] == projectName { - toRemove = append(toRemove, share) - } - } - if len(toRemove) == 0 { - return - } - - w.Event(progress.NewEvent(fileShareProgressID, progress.Working, "Removing")) - rootResult := progress.Done - defer func() { - w.Event(progress.NewEvent(fileShareProgressID, rootResult, "")) - }() - for _, share := range toRemove { - shareID := share.Labels["com.docker.desktop.mutagen.file-share.id"] - if shareID == "" { - w.Event(progress.Event{ - ID: share.Alpha.Path, - ParentID: fileShareProgressID, - Status: progress.Warning, - StatusText: "Invalid", - }) - continue - } - - w.Event(progress.Event{ - ID: share.Alpha.Path, - ParentID: fileShareProgressID, - Status: progress.Working, - }) - - var status progress.EventStatus - var statusText string - if err := c.DeleteFileShare(ctx, shareID); err != nil { - // TODO(milas): Docker Desktop is doing weird things with error responses, - // once fixed, we can return proper error types from the client - if strings.Contains(err.Error(), "file share in use") { - status = progress.Warning - statusText = "Resource is still in use" - if rootResult != progress.Error { - // error takes precedence over warning - rootResult = progress.Warning - } - } else { - logrus.Debugf("Error deleting file share %q: %v", shareID, err) - status = progress.Error - rootResult = progress.Error - } - } else { - logrus.Debugf("Deleted file share: %s", shareID) - status = progress.Done - } - - w.Event(progress.Event{ - ID: share.Alpha.Path, - ParentID: fileShareProgressID, - Status: status, - StatusText: statusText, - }) - } -} - -// FileShareManager maps between Compose bind mounts and Desktop File Shares -// state. -type FileShareManager struct { - mu sync.Mutex - cli *Client - projectName string - hostPaths []string - // state holds session status keyed by file share ID. - state map[string]*FileShareSession -} - -func NewFileShareManager(cli *Client, projectName string, hostPaths []string) *FileShareManager { - return &FileShareManager{ - cli: cli, - projectName: projectName, - hostPaths: hostPaths, - state: make(map[string]*FileShareSession), - } -} - -// EnsureExists looks for existing File Shares or creates new ones for the -// host paths. -// -// This function blocks until each share reaches steady state, at which point -// flow can continue. -func (m *FileShareManager) EnsureExists(ctx context.Context) (err error) { - w := progress.ContextWriter(ctx) - w.Event(progress.NewEvent(fileShareProgressID, progress.Working, "")) - defer func() { - if err != nil { - w.Event(progress.NewEvent(fileShareProgressID, progress.Error, "")) - } else { - w.Event(progress.NewEvent(fileShareProgressID, progress.Done, "")) - } - }() - - wait := &waiter{ - shareIDs: make(map[string]struct{}), - done: make(chan struct{}), - } - - handler := m.eventHandler(w, wait) - - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - // stream session events to update internal state for project - monitorErr := make(chan error, 1) - go func() { - defer close(monitorErr) - if err := m.watch(ctx, handler); err != nil && ctx.Err() == nil { - monitorErr <- err - } - }() - - if err := m.initialize(ctx, wait, handler); err != nil { - return err - } - - waitCh := wait.start() - if waitCh != nil { - select { - case <-ctx.Done(): - return context.Cause(ctx) - case err := <-monitorErr: - if err != nil { - return fmt.Errorf("watching file share sessions: %w", err) - } else if ctx.Err() == nil { - // this indicates a bug - it should not stop w/o an error if the context is still active - return errors.New("file share session watch stopped unexpectedly") - } - case <-wait.start(): - // everything is done - } - } - - return nil -} - -// initialize finds existing shares or creates new ones for the host paths. -// -// Once a share is found/created, its progress is monitored via the watch. -func (m *FileShareManager) initialize(ctx context.Context, wait *waiter, handler func(FileShareSession)) error { - // the watch is already running in the background, so the lock is taken - // throughout to prevent interleaving writes - m.mu.Lock() - defer m.mu.Unlock() - - existing, err := m.cli.ListFileShares(ctx) - if err != nil { - return err - } - - for _, path := range m.hostPaths { - var fileShareID string - var fss *FileShareSession - - if fss = findExistingShare(path, existing); fss != nil { - fileShareID = fss.Beta.Path - logrus.Debugf("Found existing suitable file share %s for path %q [%s]", fileShareID, path, fss.Alpha.Path) - wait.addShare(fileShareID) - handler(*fss) - continue - } else { - req := CreateFileShareRequest{ - HostPath: path, - Labels: map[string]string{ - "com.docker.compose.project": m.projectName, - }, - } - createResp, err := m.cli.CreateFileShare(ctx, req) - if err != nil { - return fmt.Errorf("creating file share: %w", err) - } - fileShareID = createResp.FileShareID - fss = m.state[fileShareID] - logrus.Debugf("Created file share %s for path %q", fileShareID, path) - } - wait.addShare(fileShareID) - if fss != nil { - handler(*fss) - } - } - - return nil -} - -func (m *FileShareManager) watch(ctx context.Context, handler func(FileShareSession)) error { - events, err := m.cli.StreamFileShares(ctx) - if err != nil { - return fmt.Errorf("streaming file shares: %w", err) - } - - for { - select { - case <-ctx.Done(): - return nil - case event := <-events: - if event.Error != nil { - return fmt.Errorf("reading file share events: %w", event.Error) - } - // closure for lock - func() { - m.mu.Lock() - defer m.mu.Unlock() - for _, fss := range event.Value { - handler(fss) - } - }() - } - } -} - -// eventHandler updates internal state, keeps track of in-progress syncs, and -// prints relevant events to progress. -func (m *FileShareManager) eventHandler(w progress.Writer, wait *waiter) func(fss FileShareSession) { - return func(fss FileShareSession) { - fileShareID := fss.Beta.Path - - shouldPrint := wait.isWatching(fileShareID) - forProject := fss.Labels[api.ProjectLabel] == m.projectName - - if shouldPrint || forProject { - m.state[fileShareID] = &fss - } - - var percent int - var current, total int64 - if fss.Beta.StagingProgress != nil { - current = int64(fss.Beta.StagingProgress.TotalReceivedSize) - } else { - current = int64(fss.Beta.TotalFileSize) - } - total = int64(fss.Alpha.TotalFileSize) - if total != 0 { - percent = int(current * 100 / total) - } - - var status progress.EventStatus - var text string - - switch { - case strings.HasPrefix(fss.Status, "halted"): - wait.shareDone(fileShareID) - status = progress.Error - case fss.Status == "watching": - wait.shareDone(fileShareID) - status = progress.Done - percent = 100 - case fss.Status == "staging-beta": - status = progress.Working - // TODO(milas): the printer doesn't style statuses for children nicely - text = fmt.Sprintf(" Syncing (%7s / %-7s)", - units.HumanSize(float64(current)), - units.HumanSize(float64(total)), - ) - default: - // catch-all for various other transitional statuses - status = progress.Working - } - - evt := progress.Event{ - ID: fss.Alpha.Path, - Status: status, - Text: text, - ParentID: fileShareProgressID, - Current: current, - Total: total, - Percent: percent, - } - - if shouldPrint { - w.Event(evt) - } - } -} - -func findExistingShare(path string, existing []FileShareSession) *FileShareSession { - for _, share := range existing { - if paths.IsChild(share.Alpha.Path, path) { - return &share - } - } - return nil -} - -type waiter struct { - mu sync.Mutex - shareIDs map[string]struct{} - done chan struct{} -} - -func (w *waiter) addShare(fileShareID string) { - w.mu.Lock() - defer w.mu.Unlock() - w.shareIDs[fileShareID] = struct{}{} -} - -func (w *waiter) isWatching(fileShareID string) bool { - w.mu.Lock() - defer w.mu.Unlock() - _, ok := w.shareIDs[fileShareID] - return ok -} - -// start returns a channel to wait for any outstanding shares to be ready. -// -// If no shares are registered when this is called, nil is returned. -func (w *waiter) start() <-chan struct{} { - w.mu.Lock() - defer w.mu.Unlock() - if len(w.shareIDs) == 0 { - return nil - } - if w.done == nil { - w.done = make(chan struct{}) - } - return w.done -} - -func (w *waiter) shareDone(fileShareID string) { - w.mu.Lock() - defer w.mu.Unlock() - - delete(w.shareIDs, fileShareID) - if len(w.shareIDs) == 0 && w.done != nil { - close(w.done) - w.done = nil - } -} diff --git a/pkg/compose/create.go b/pkg/compose/create.go index 5c9d13436fc..7e531bf7d20 100644 --- a/pkg/compose/create.go +++ b/pkg/compose/create.go @@ -22,16 +22,12 @@ import ( "encoding/json" "errors" "fmt" - "io/fs" "os" "path/filepath" - "sort" "strconv" "strings" "github.com/compose-spec/compose-go/v2/types" - "github.com/docker/compose/v2/internal/desktop" - 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" @@ -155,58 +151,6 @@ func (s *composeService) ensureProjectVolumes(ctx context.Context, project *type ids[k] = id } - err := func() error { - if s.manageDesktopFileSharesEnabled(ctx) { - // collect all the bind mount paths and try to set up file shares in - // Docker Desktop for them - var paths []string - for _, svcName := range project.ServiceNames() { - svc := project.Services[svcName] - for _, vol := range svc.Volumes { - if vol.Type != string(mount.TypeBind) { - continue - } - p := filepath.Clean(vol.Source) - if !filepath.IsAbs(p) { - return fmt.Errorf("file share path is not absolute: %s", p) - } - if fi, err := os.Stat(p); errors.Is(err, fs.ErrNotExist) { - // actual directory will be implicitly created when the - // file share is initialized if it doesn't exist, so - // need to filter out any that should not be auto-created - if vol.Bind != nil && !vol.Bind.CreateHostPath { - logrus.Debugf("Skipping creating file share for %q: does not exist and `create_host_path` is false", p) - continue - } - } else if err != nil { - // if we can't read the path, we won't be able to make - // a file share for it - logrus.Debugf("Skipping creating file share for %q: %v", p, err) - continue - } else if !fi.IsDir() { - // ignore files & special types (e.g. Unix sockets) - logrus.Debugf("Skipping creating file share for %q: not a directory", p) - continue - } - - paths = append(paths, p) - } - } - - // remove duplicate/unnecessary child paths and sort them for predictability - paths = pathutil.EncompassingPaths(paths) - sort.Strings(paths) - - fileShareManager := desktop.NewFileShareManager(s.desktopCli, project.Name, paths) - if err := fileShareManager.EnsureExists(ctx); err != nil { - return fmt.Errorf("initializing file shares: %w", err) - } - } - return nil - }() - if err != nil { - progress.ContextWriter(ctx).TailMsgf("Failed to prepare Synchronized file shares: %v", err) - } return ids, nil } diff --git a/pkg/compose/desktop.go b/pkg/compose/desktop.go index a2ffb27573f..a50566aed2c 100644 --- a/pkg/compose/desktop.go +++ b/pkg/compose/desktop.go @@ -17,11 +17,8 @@ package compose import ( - "context" - "github.com/docker/compose/v2/internal/desktop" "github.com/docker/compose/v2/internal/experimental" - "github.com/sirupsen/logrus" ) func (s *composeService) SetDesktopClient(cli *desktop.Client) { @@ -31,18 +28,3 @@ func (s *composeService) SetDesktopClient(cli *desktop.Client) { func (s *composeService) SetExperiments(experiments *experimental.State) { s.experiments = experiments } - -func (s *composeService) manageDesktopFileSharesEnabled(ctx context.Context) bool { - if !s.isDesktopIntegrationActive() { - return false - } - - // synchronized file share support in Docker Desktop is dependent upon - // a variety of factors (settings, OS, etc), which this endpoint abstracts - fileSharesConfig, err := s.desktopCli.GetFileSharesConfig(ctx) - if err != nil { - logrus.Debugf("Failed to retrieve file shares config: %v", err) - return false - } - return fileSharesConfig.Active && fileSharesConfig.Compose.ManageBindMounts -} diff --git a/pkg/compose/down.go b/pkg/compose/down.go index 07b7e5840a0..7061be0932d 100644 --- a/pkg/compose/down.go +++ b/pkg/compose/down.go @@ -23,7 +23,6 @@ import ( "time" "github.com/compose-spec/compose-go/v2/types" - "github.com/docker/compose/v2/internal/desktop" "github.com/docker/compose/v2/pkg/api" "github.com/docker/compose/v2/pkg/progress" "github.com/docker/compose/v2/pkg/utils" @@ -157,13 +156,6 @@ func (s *composeService) ensureVolumesDown(ctx context.Context, project *types.P }) } - if s.manageDesktopFileSharesEnabled(ctx) { - ops = append(ops, func() error { - desktop.RemoveFileSharesForProject(ctx, s.desktopCli, project.Name) - return nil - }) - } - return ops } From 60385e6065e49585b4efbcc5cb1c0db1dbfe7ca8 Mon Sep 17 00:00:00 2001 From: Guillaume Lours <705411+glours@users.noreply.github.com> Date: Tue, 22 Apr 2025 17:03:53 +0200 Subject: [PATCH 08/28] bump compose-go to v2.6.1 fixing parsing of npipe as volume type Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b2450cfc34b..f4bbe5b2fd8 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( 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.6.0 + github.com/compose-spec/compose-go/v2 v2.6.1 github.com/containerd/containerd/v2 v2.0.5 github.com/containerd/platforms v1.0.0-rc.1 github.com/davecgh/go-spew v1.1.1 diff --git a/go.sum b/go.sum index 80ef4899f57..512a169bfe8 100644 --- a/go.sum +++ b/go.sum @@ -82,8 +82,8 @@ github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= github.com/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.6.0 h1:/+oBD2ixSENOeN/TlJqWZmUak0xM8A7J08w/z661Wd4= -github.com/compose-spec/compose-go/v2 v2.6.0/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA= +github.com/compose-spec/compose-go/v2 v2.6.1 h1:276YiQKRcGGtgkxiymzWHJ2CTv5joQA+7DTNrUA+rys= +github.com/compose-spec/compose-go/v2 v2.6.1/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA= 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= From 955e4ed94ec852da60a150e6cbdb2f75838e6a4b Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Wed, 23 Apr 2025 16:21:54 +0200 Subject: [PATCH 09/28] introduce networks.interface_name Signed-off-by: Nicolas De Loof --- go.mod | 2 +- go.sum | 4 +- pkg/compose/create.go | 44 ++++++++++++++----- pkg/compose/create_test.go | 12 +++-- .../network-interface-name/compose.yaml | 7 +++ pkg/e2e/networks_test.go | 18 ++++++++ 6 files changed, 68 insertions(+), 19 deletions(-) create mode 100644 pkg/e2e/fixtures/network-interface-name/compose.yaml diff --git a/go.mod b/go.mod index f4bbe5b2fd8..8a15ba21aed 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( 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.6.1 + github.com/compose-spec/compose-go/v2 v2.6.2-0.20250423090706-30ff01d36f76 github.com/containerd/containerd/v2 v2.0.5 github.com/containerd/platforms v1.0.0-rc.1 github.com/davecgh/go-spew v1.1.1 diff --git a/go.sum b/go.sum index 512a169bfe8..b4f626f4eae 100644 --- a/go.sum +++ b/go.sum @@ -82,8 +82,8 @@ github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= github.com/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.6.1 h1:276YiQKRcGGtgkxiymzWHJ2CTv5joQA+7DTNrUA+rys= -github.com/compose-spec/compose-go/v2 v2.6.1/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA= +github.com/compose-spec/compose-go/v2 v2.6.2-0.20250423090706-30ff01d36f76 h1:KZvD41eTRr9/n43zccAcGPBRgzHXdbLZY4IXSeJxqIw= +github.com/compose-spec/compose-go/v2 v2.6.2-0.20250423090706-30ff01d36f76/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA= 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= diff --git a/pkg/compose/create.go b/pkg/compose/create.go index 7e531bf7d20..825fe470986 100644 --- a/pkg/compose/create.go +++ b/pkg/compose/create.go @@ -154,6 +154,7 @@ func (s *composeService) ensureProjectVolumes(ctx context.Context, project *type return ids, nil } +//nolint:gocyclo func (s *composeService) getCreateConfigs(ctx context.Context, p *types.Project, service types.ServiceConfig, @@ -246,7 +247,10 @@ func (s *composeService) getCreateConfigs(ctx context.Context, if err != nil { return createConfigs{}, err } - networkMode, networkingConfig := defaultNetworkSettings(p, service, number, links, opts.UseNetworkAliases, apiVersion) + networkMode, networkingConfig, err := defaultNetworkSettings(p, service, number, links, opts.UseNetworkAliases, apiVersion) + if err != nil { + return createConfigs{}, err + } portBindings := buildContainerPortBindingOptions(service) // MISC @@ -356,7 +360,7 @@ func (s *composeService) prepareContainerMACAddress(ctx context.Context, service } if len(withMacAddress) > 1 { - 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) + return "", fmt.Errorf("a MAC address is specified for multiple networks (%s), but this feature requires Docker Engine v25 or later", strings.Join(withMacAddress, ", ")) } if mainNw != nil && mainNw.MacAddress != "" { @@ -379,6 +383,8 @@ func getAliases(project *types.Project, service types.ServiceConfig, serviceInde } func createEndpointSettings(p *types.Project, service types.ServiceConfig, serviceIndex int, networkKey string, links []string, useNetworkAliases bool) *network.EndpointSettings { + const ifname = "com.docker.network.endpoint.ifname" + config := service.Networks[networkKey] var ipam *network.EndpointIPAMConfig var ( @@ -398,6 +404,15 @@ func createEndpointSettings(p *types.Project, service types.ServiceConfig, servi } macAddress = config.MacAddress driverOpts = config.DriverOpts + if config.InterfaceName != "" { + if driverOpts == nil { + driverOpts = map[string]string{} + } + if name, ok := driverOpts[ifname]; ok && name != config.InterfaceName { + logrus.Warnf("ignoring services.%s.networks.%s.interface_name as %s driver_opts is already declared", service.Name, networkKey, ifname) + } + driverOpts[ifname] = config.InterfaceName + } gwPriority = config.GatewayPriority } return &network.EndpointSettings{ @@ -471,20 +486,17 @@ func (s *composeService) prepareLabels(labels types.Labels, service types.Servic } // defaultNetworkSettings determines the container.NetworkMode and corresponding network.NetworkingConfig (nil if not applicable). -func defaultNetworkSettings( - project *types.Project, - service types.ServiceConfig, - serviceIndex int, - links []string, - useNetworkAliases bool, +func defaultNetworkSettings(project *types.Project, + service types.ServiceConfig, serviceIndex int, + links []string, useNetworkAliases bool, version string, -) (container.NetworkMode, *network.NetworkingConfig) { +) (container.NetworkMode, *network.NetworkingConfig, error) { if service.NetworkMode != "" { - return container.NetworkMode(service.NetworkMode), nil + return container.NetworkMode(service.NetworkMode), nil, nil } if len(project.Networks) == 0 { - return "none", nil + return "none", nil, nil } var primaryNetworkKey string @@ -515,6 +527,14 @@ func defaultNetworkSettings( } } + if versions.LessThan(version, "1.49") { + for _, config := range service.Networks { + if config != nil && config.InterfaceName != "" { + return "", nil, fmt.Errorf("interface_name requires Docker Engine v28.1 or later") + } + } + } + endpointsConfig[primaryNetworkMobyNetworkName] = primaryNetworkEndpoint networkConfig := &network.NetworkingConfig{ EndpointsConfig: endpointsConfig, @@ -523,7 +543,7 @@ func defaultNetworkSettings( // From the Engine API docs: // > Supported standard values are: bridge, host, none, and container:. // > Any other value is taken as a custom network's name to which this container should connect to. - return container.NetworkMode(primaryNetworkMobyNetworkName), networkConfig + return container.NetworkMode(primaryNetworkMobyNetworkName), networkConfig, nil } func getRestartPolicy(service types.ServiceConfig) container.RestartPolicy { diff --git a/pkg/compose/create_test.go b/pkg/compose/create_test.go index bec42beef29..3394bf3c936 100644 --- a/pkg/compose/create_test.go +++ b/pkg/compose/create_test.go @@ -219,7 +219,8 @@ func TestDefaultNetworkSettings(t *testing.T) { }), } - networkMode, networkConfig := defaultNetworkSettings(&project, service, 1, nil, true, "1.43") + networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.43") + assert.NilError(t, err) assert.Equal(t, string(networkMode), "myProject_myNetwork2") assert.Check(t, cmp.Len(networkConfig.EndpointsConfig, 1)) assert.Check(t, cmp.Contains(networkConfig.EndpointsConfig, "myProject_myNetwork2")) @@ -247,7 +248,8 @@ func TestDefaultNetworkSettings(t *testing.T) { }), } - networkMode, networkConfig := defaultNetworkSettings(&project, service, 1, nil, true, "1.43") + networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.43") + assert.NilError(t, err) assert.Equal(t, string(networkMode), "myProject_default") assert.Check(t, cmp.Len(networkConfig.EndpointsConfig, 1)) assert.Check(t, cmp.Contains(networkConfig.EndpointsConfig, "myProject_default")) @@ -264,7 +266,8 @@ func TestDefaultNetworkSettings(t *testing.T) { }, } - networkMode, networkConfig := defaultNetworkSettings(&project, service, 1, nil, true, "1.43") + networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.43") + assert.NilError(t, err) assert.Equal(t, string(networkMode), "none") assert.Check(t, cmp.Nil(networkConfig)) }) @@ -284,7 +287,8 @@ func TestDefaultNetworkSettings(t *testing.T) { }), } - networkMode, networkConfig := defaultNetworkSettings(&project, service, 1, nil, true, "1.43") + networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.43") + assert.NilError(t, err) assert.Equal(t, string(networkMode), "host") assert.Check(t, cmp.Nil(networkConfig)) }) diff --git a/pkg/e2e/fixtures/network-interface-name/compose.yaml b/pkg/e2e/fixtures/network-interface-name/compose.yaml new file mode 100644 index 00000000000..701830a48a5 --- /dev/null +++ b/pkg/e2e/fixtures/network-interface-name/compose.yaml @@ -0,0 +1,7 @@ +services: + test: + image: alpine + command: ip link show + networks: + default: + interface_name: foobar diff --git a/pkg/e2e/networks_test.go b/pkg/e2e/networks_test.go index c74687adc6e..cbd4e0e71b1 100644 --- a/pkg/e2e/networks_test.go +++ b/pkg/e2e/networks_test.go @@ -181,3 +181,21 @@ func TestMacAddress(t *testing.T) { res := c.RunDockerCmd(t, "inspect", fmt.Sprintf("%s-test-1", projectName), "-f", "{{ (index .NetworkSettings.Networks \"network_mac_address_default\" ).MacAddress }}") res.Assert(t, icmd.Expected{Out: "00:e0:84:35:d0:e8"}) } + +func TestInterfaceName(t *testing.T) { + c := NewCLI(t) + + version := c.RunDockerCmd(t, "version", "-f", "{{.Server.Version}}") + major, _, found := strings.Cut(version.Combined(), ".") + assert.Assert(t, found) + if major == "26" || major == "27" { + t.Skip("Skipping test due to docker version < 28") + } + + const projectName = "network_interface_name" + res := c.RunDockerComposeCmd(t, "-f", "./fixtures/network-interface-name/compose.yaml", "--project-name", projectName, "run", "test") + t.Cleanup(func() { + c.cleanupWithDown(t, projectName) + }) + res.Assert(t, icmd.Expected{Out: "foobar@"}) +} From f8dae06df8fc3f44a26012c7848a19de2cde9d0f Mon Sep 17 00:00:00 2001 From: Anvar Umuraliev Date: Thu, 24 Apr 2025 12:09:18 +0200 Subject: [PATCH 10/28] Add support for COMPOSE_PROGRESS env variable COMPOSE_PROGRESS variable supports 5 values: - "auto" - detect console capabilities - "tty" - use terminal capability for advanced rendering - "plain" - dump raw events to output - "quiet" - don't display events - "json" - outputs a machine-readable JSON stream Signed-off-by: Anvar Umuraliev --- pkg/e2e/compose_run_test.go | 12 ++++++++++++ pkg/progress/writer.go | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/pkg/e2e/compose_run_test.go b/pkg/e2e/compose_run_test.go index fa8fda686a6..acf1bdf8172 100644 --- a/pkg/e2e/compose_run_test.go +++ b/pkg/e2e/compose_run_test.go @@ -170,6 +170,18 @@ func TestLocalComposeRun(t *testing.T) { assert.Assert(t, strings.Contains(res.Combined(), "Pulled"), res.Combined()) }) + t.Run("COMPOSE_PROGRESS quiet", func(t *testing.T) { + res := c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/quiet-pull.yaml", "down", "--remove-orphans", "--rmi", "all") + res.Assert(t, icmd.Success) + + cmd := c.NewDockerComposeCmd(t, "-f", "./fixtures/run-test/quiet-pull.yaml", "run", "backend") + res = icmd.RunCmd(cmd, func(c *icmd.Cmd) { + c.Env = append(c.Env, "COMPOSE_PROGRESS=quiet") + }) + assert.Assert(t, !strings.Contains(res.Combined(), "Pull complete"), res.Combined()) + assert.Assert(t, !strings.Contains(res.Combined(), "Pulled"), res.Combined()) + }) + t.Run("--pull", func(t *testing.T) { res := c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/pull.yaml", "down", "--remove-orphans", "--rmi", "all") res.Assert(t, icmd.Success) diff --git a/pkg/progress/writer.go b/pkg/progress/writer.go index 3f4a74f5626..35817156abe 100644 --- a/pkg/progress/writer.go +++ b/pkg/progress/writer.go @@ -19,6 +19,7 @@ package progress import ( "context" "io" + "os" "sync" "github.com/docker/cli/cli/streams" @@ -121,6 +122,9 @@ func NewWriter(ctx context.Context, out *streams.Out, progressTitle string) (Wri if !ok { dryRun = false } + if v, ok := os.LookupEnv("COMPOSE_PROGRESS"); ok && Mode == ModeAuto { + Mode = v + } if Mode == ModeQuiet { return quiet{}, nil } From 5bb46035cf12e5b2fc2f3b09522a392b7b753120 Mon Sep 17 00:00:00 2001 From: Anvar Umuraliev Date: Fri, 25 Apr 2025 11:27:16 +0200 Subject: [PATCH 11/28] Set --progress flag default value from env if provided Signed-off-by: Anvar Umuraliev --- cmd/compose/compose.go | 12 +++++++++++- pkg/progress/writer.go | 4 ---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/cmd/compose/compose.go b/cmd/compose/compose.go index a382f39ac78..1e1377194d7 100644 --- a/cmd/compose/compose.go +++ b/cmd/compose/compose.go @@ -69,6 +69,8 @@ const ( ComposeEnvFiles = "COMPOSE_ENV_FILES" // ComposeMenu defines if the navigation menu should be rendered. Can be also set via --menu ComposeMenu = "COMPOSE_MENU" + // ComposeProgress defines type of progress output, if --progress isn't used + ComposeProgress = "COMPOSE_PROGRESS" ) // rawEnv load a dot env file using docker/cli key=value parser, without attempt to interpolate or evaluate values @@ -228,7 +230,7 @@ func (o *ProjectOptions) addProjectFlags(f *pflag.FlagSet) { f.StringVar(&o.ProjectDir, "project-directory", "", "Specify an alternate working directory\n(default: the path of the, first specified, Compose file)") f.StringVar(&o.WorkDir, "workdir", "", "DEPRECATED! USE --project-directory INSTEAD.\nSpecify an alternate working directory\n(default: the path of the, first specified, Compose file)") f.BoolVar(&o.Compatibility, "compatibility", false, "Run compose in backward compatibility mode") - f.StringVar(&o.Progress, "progress", string(buildkit.AutoMode), fmt.Sprintf(`Set type of progress output (%s)`, strings.Join(printerModes, ", "))) + f.StringVar(&o.Progress, "progress", defaultStringVar(ComposeProgress, string(buildkit.AutoMode)), fmt.Sprintf(`Set type of progress output (%s)`, strings.Join(printerModes, ", "))) f.BoolVar(&o.All, "all-resources", false, "Include all resources, even those not used by services") _ = f.MarkHidden("workdir") } @@ -240,6 +242,14 @@ func defaultStringArrayVar(env string) []string { }) } +// get default value for a command line flag from the env variable, if the env variable is not set, it returns the provided default value 'def' +func defaultStringVar(env, def string) string { + if v, ok := os.LookupEnv(env); ok { + return v + } + return def +} + func (o *ProjectOptions) projectOrName(ctx context.Context, dockerCli command.Cli, services ...string) (*types.Project, string, error) { name := o.ProjectName var project *types.Project diff --git a/pkg/progress/writer.go b/pkg/progress/writer.go index 35817156abe..3f4a74f5626 100644 --- a/pkg/progress/writer.go +++ b/pkg/progress/writer.go @@ -19,7 +19,6 @@ package progress import ( "context" "io" - "os" "sync" "github.com/docker/cli/cli/streams" @@ -122,9 +121,6 @@ func NewWriter(ctx context.Context, out *streams.Out, progressTitle string) (Wri if !ok { dryRun = false } - if v, ok := os.LookupEnv("COMPOSE_PROGRESS"); ok && Mode == ModeAuto { - Mode = v - } if Mode == ModeQuiet { return quiet{}, nil } From 6e356521827fa969ba61cebfe8e8ca8e6d430633 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Mon, 28 Apr 2025 10:37:51 +0200 Subject: [PATCH 12/28] fix support for remote absolute path Signed-off-by: Nicolas De Loof --- go.mod | 2 +- go.sum | 4 ++-- pkg/compose/create.go | 29 ++++++++++++----------------- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index 8a15ba21aed..8248c5f950c 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( 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.6.2-0.20250423090706-30ff01d36f76 + github.com/compose-spec/compose-go/v2 v2.6.2-0.20250428082045-7eb3472a5a95 github.com/containerd/containerd/v2 v2.0.5 github.com/containerd/platforms v1.0.0-rc.1 github.com/davecgh/go-spew v1.1.1 diff --git a/go.sum b/go.sum index b4f626f4eae..7635f47abf5 100644 --- a/go.sum +++ b/go.sum @@ -82,8 +82,8 @@ github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= github.com/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.6.2-0.20250423090706-30ff01d36f76 h1:KZvD41eTRr9/n43zccAcGPBRgzHXdbLZY4IXSeJxqIw= -github.com/compose-spec/compose-go/v2 v2.6.2-0.20250423090706-30ff01d36f76/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA= +github.com/compose-spec/compose-go/v2 v2.6.2-0.20250428082045-7eb3472a5a95 h1:lWMFXVkl+LkMdllYT4P/5snsB8JNhQoFnSsRX1pbX1o= +github.com/compose-spec/compose-go/v2 v2.6.2-0.20250428082045-7eb3472a5a95/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA= 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= diff --git a/pkg/compose/create.go b/pkg/compose/create.go index 825fe470986..ab888dce71b 100644 --- a/pkg/compose/create.go +++ b/pkg/compose/create.go @@ -27,6 +27,7 @@ import ( "strconv" "strings" + "github.com/compose-spec/compose-go/v2/paths" "github.com/compose-spec/compose-go/v2/types" "github.com/docker/compose/v2/pkg/api" "github.com/docker/compose/v2/pkg/progress" @@ -1126,28 +1127,22 @@ func isUnixAbs(p string) bool { } func isWindowsAbs(p string) bool { - if strings.HasPrefix(p, "\\\\") { - return true - } - if len(p) > 2 && p[1] == ':' { - return p[2] == '\\' - } - return false + return paths.IsWindowsAbs(p) } func buildMount(project types.Project, volume types.ServiceVolumeConfig) (mount.Mount, error) { source := volume.Source - // on windows, filepath.IsAbs(source) is false for unix style abs path like /var/run/docker.sock. - // do not replace these with filepath.Abs(source) that will include a default drive. - if volume.Type == types.VolumeTypeBind && !filepath.IsAbs(source) && !strings.HasPrefix(source, "/") { - // volume source has already been prefixed with workdir if required, by compose-go project loader - var err error - source, err = filepath.Abs(source) - if err != nil { - return mount.Mount{}, err + switch volume.Type { + case types.VolumeTypeBind: + if !filepath.IsAbs(source) && !isUnixAbs(source) && !isWindowsAbs(source) { + // volume source has already been prefixed with workdir if required, by compose-go project loader + var err error + source, err = filepath.Abs(source) + if err != nil { + return mount.Mount{}, err + } } - } - if volume.Type == types.VolumeTypeVolume { + case types.VolumeTypeVolume: if volume.Source != "" { pVolume, ok := project.Volumes[volume.Source] if ok { From d2274ebe6c7dbd9985a68f1711d082eeefc9040a Mon Sep 17 00:00:00 2001 From: Guillaume Lours <705411+glours@users.noreply.github.com> Date: Mon, 28 Apr 2025 13:02:37 +0200 Subject: [PATCH 13/28] display proper event message for provider services on up and down Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com> --- pkg/compose/plugins.go | 61 +++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/pkg/compose/plugins.go b/pkg/compose/plugins.go index a905546fe8d..315f464cdcf 100644 --- a/pkg/compose/plugins.go +++ b/pkg/compose/plugins.go @@ -61,15 +61,33 @@ func (s *composeService) runPlugin(ctx context.Context, project *types.Project, cmd := s.setupPluginCommand(ctx, project, provider, plugin.Path, command) + variables, err := s.executePlugin(ctx, cmd, command, service) + if err != nil { + return err + } + + for name, s := range project.Services { + if _, ok := s.DependsOn[service.Name]; ok { + prefix := strings.ToUpper(service.Name) + "_" + for key, val := range variables { + s.Environment[prefix+key] = &val + } + project.Services[name] = s + } + } + return nil +} + +func (s *composeService) executePlugin(ctx context.Context, cmd *exec.Cmd, command string, service types.ServiceConfig) (types.Mapping, error) { eg := errgroup.Group{} stdout, err := cmd.StdoutPipe() if err != nil { - return err + return nil, err } err = cmd.Start() if err != nil { - return err + return nil, err } eg.Go(cmd.Wait) @@ -79,7 +97,17 @@ func (s *composeService) runPlugin(ctx context.Context, project *types.Project, variables := types.Mapping{} pw := progress.ContextWriter(ctx) - pw.Event(progress.CreatingEvent(service.Name)) + var action string + switch command { + case "up": + pw.Event(progress.CreatingEvent(service.Name)) + action = "create" + case "down": + pw.Event(progress.RemovingEvent(service.Name)) + action = "remove" + default: + return nil, fmt.Errorf("unsupported plugin command: %s", command) + } for { var msg JsonMessage err = decoder.Decode(&msg) @@ -87,42 +115,37 @@ func (s *composeService) runPlugin(ctx context.Context, project *types.Project, break } if err != nil { - return err + return nil, err } switch msg.Type { case ErrorType: pw.Event(progress.ErrorMessageEvent(service.Name, "error")) - return errors.New(msg.Message) + return nil, errors.New(msg.Message) case InfoType: pw.Event(progress.ErrorMessageEvent(service.Name, msg.Message)) case SetEnvType: key, val, found := strings.Cut(msg.Message, "=") if !found { - return fmt.Errorf("invalid response from plugin: %s", msg.Message) + return nil, fmt.Errorf("invalid response from plugin: %s", msg.Message) } variables[key] = val default: - return fmt.Errorf("invalid response from plugin: %s", msg.Type) + return nil, fmt.Errorf("invalid response from plugin: %s", msg.Type) } } err = eg.Wait() if err != nil { pw.Event(progress.ErrorMessageEvent(service.Name, err.Error())) - return fmt.Errorf("failed to create external service: %s", err.Error()) + return nil, fmt.Errorf("failed to %s external service: %s", action, err.Error()) } - pw.Event(progress.CreatedEvent(service.Name)) - - prefix := strings.ToUpper(service.Name) + "_" - for name, s := range project.Services { - if _, ok := s.DependsOn[service.Name]; ok { - for key, val := range variables { - s.Environment[prefix+key] = &val - } - project.Services[name] = s - } + switch command { + case "up": + pw.Event(progress.CreatedEvent(service.Name)) + case "down": + pw.Event(progress.RemovedEvent(service.Name)) } - return nil + return variables, nil } func (s *composeService) getPluginBinaryPath(providerType string) (*manager.Plugin, error) { From 91d04a5ca91270fc05684400f46164fb8561cd6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 09:30:40 +0000 Subject: [PATCH 14/28] build(deps): bump go.uber.org/mock from 0.5.1 to 0.5.2 Bumps [go.uber.org/mock](https://github.com/uber/mock) from 0.5.1 to 0.5.2. - [Release notes](https://github.com/uber/mock/releases) - [Changelog](https://github.com/uber-go/mock/blob/main/CHANGELOG.md) - [Commits](https://github.com/uber/mock/compare/v0.5.1...v0.5.2) --- updated-dependencies: - dependency-name: go.uber.org/mock dependency-version: 0.5.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8248c5f950c..e7e8ca6ebaa 100644 --- a/go.mod +++ b/go.mod @@ -52,7 +52,7 @@ require ( 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.1 + go.uber.org/mock v0.5.2 golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 golang.org/x/sync v0.13.0 golang.org/x/sys v0.32.0 diff --git a/go.sum b/go.sum index 7635f47abf5..adffb8422f1 100644 --- a/go.sum +++ b/go.sum @@ -535,8 +535,8 @@ go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeX 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.1 h1:ASgazW/qBmR+A32MYFDB6E2POoTgOwT509VP0CT/fjs= -go.uber.org/mock v0.5.1/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= +go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= From 0403f0d76d91f5ef673bfe8f18bc7bf831c48725 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Wed, 30 Apr 2025 10:47:54 +0200 Subject: [PATCH 15/28] e2e test for start_interval Signed-off-by: Nicolas De Loof --- pkg/compose/convert.go | 4 ++ pkg/e2e/fixtures/start_interval/compose.yaml | 15 ++++++ pkg/e2e/healthcheck_test.go | 54 ++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 pkg/e2e/fixtures/start_interval/compose.yaml create mode 100644 pkg/e2e/healthcheck_test.go diff --git a/pkg/compose/convert.go b/pkg/compose/convert.go index e9ff7838b98..17d5a901869 100644 --- a/pkg/compose/convert.go +++ b/pkg/compose/convert.go @@ -78,6 +78,10 @@ func (s *composeService) ToMobyHealthCheck(ctx context.Context, check *compose.H } else { startInterval = time.Duration(*check.StartInterval) } + if check.StartPeriod == nil { + // see https://github.com/moby/moby/issues/48874 + return nil, errors.New("healthcheck.start_interval requires healthcheck.start_period to be set") + } } return &container.HealthConfig{ Test: test, diff --git a/pkg/e2e/fixtures/start_interval/compose.yaml b/pkg/e2e/fixtures/start_interval/compose.yaml new file mode 100644 index 00000000000..c78a9f43a05 --- /dev/null +++ b/pkg/e2e/fixtures/start_interval/compose.yaml @@ -0,0 +1,15 @@ +services: + test: + image: "nginx" + healthcheck: + interval: 30s + start_period: 10s + start_interval: 1s + test: "/bin/true" + + error: + image: "nginx" + healthcheck: + interval: 30s + start_interval: 1s + test: "/bin/true" \ No newline at end of file diff --git a/pkg/e2e/healthcheck_test.go b/pkg/e2e/healthcheck_test.go new file mode 100644 index 00000000000..b578da5e504 --- /dev/null +++ b/pkg/e2e/healthcheck_test.go @@ -0,0 +1,54 @@ +/* + Copyright 2023 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 e2e + +import ( + "strings" + "testing" + "time" + + "gotest.tools/v3/assert" + "gotest.tools/v3/icmd" +) + +func TestStartInterval(t *testing.T) { + c := NewParallelCLI(t) + const projectName = "e2e-start-interval" + + t.Cleanup(func() { + c.cleanupWithDown(t, projectName) + }) + + res := c.RunDockerComposeCmdNoCheck(t, "-f", "fixtures/start_interval/compose.yaml", "--project-name", projectName, "up", "--wait", "-d", "error") + res.Assert(t, icmd.Expected{ExitCode: 1, Err: "healthcheck.start_interval requires healthcheck.start_period to be set"}) + + timeout := time.After(30 * time.Second) + done := make(chan bool) + go func() { + res := c.RunDockerComposeCmd(t, "-f", "fixtures/start_interval/compose.yaml", "--project-name", projectName, "up", "--wait", "-d", "test") + out := res.Combined() + assert.Assert(t, strings.Contains(out, "Healthy"), out) + done <- true + }() + + select { + case <-timeout: + t.Fatal("test did not finish in time") + case <-done: + break + } +} From 9c998a934fd364df1eadb868448a0ed0d74f8267 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Mon, 28 Apr 2025 08:30:22 +0200 Subject: [PATCH 16/28] fix collect image digests for service images built by bake Signed-off-by: Nicolas De Loof --- pkg/compose/build_bake.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/pkg/compose/build_bake.go b/pkg/compose/build_bake.go index ac474bc77d4..589ed540588 100644 --- a/pkg/compose/build_bake.go +++ b/pkg/compose/build_bake.go @@ -124,6 +124,7 @@ type bakeMetadata map[string]buildStatus type buildStatus struct { Digest string `json:"containerimage.digest"` + Image string `json:"image.name"` } func (s *composeService) doBuildBake(ctx context.Context, project *types.Project, serviceToBeBuild types.Services, options api.BuildOptions) (map[string]string, error) { //nolint:gocyclo @@ -142,9 +143,12 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project Groups: map[string]bakeGroup{}, Targets: map[string]bakeTarget{}, } - var group bakeGroup - var privileged bool - var read []string + var ( + group bakeGroup + privileged bool + read []string + expectedImages = make(map[string]string, len(serviceToBeBuild)) // service name -> expected image + ) for serviceName, service := range serviceToBeBuild { if service.Build == nil { @@ -161,6 +165,7 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project } image := api.GetImageNameOrDefault(service, project.Name) + expectedImages[serviceName] = image entitlements := build.Entitlements if slices.Contains(build.Entitlements, "security.insecure") { @@ -324,8 +329,12 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project cw := progress.ContextWriter(ctx) results := map[string]string{} - for name, m := range md { - results[name] = m.Digest + for service, name := range expectedImages { + built, ok := md[service] // bake target == service name + if !ok { + return nil, fmt.Errorf("build result not found in Bake metadata for service %s", service) + } + results[name] = built.Digest cw.Event(progress.BuiltEvent(name)) } return results, nil From fc8c56b4077a9c9a429a28bcd0a6b866d7f17c55 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Mon, 28 Apr 2025 08:59:07 +0200 Subject: [PATCH 17/28] select services implicitly declared by a service:xx build dependency Signed-off-by: Nicolas De Loof --- cmd/compose/build.go | 23 +---------------------- pkg/compose/build.go | 29 ++++++++++++++++++++++++++++- {cmd => pkg}/compose/build_test.go | 0 pkg/e2e/build_test.go | 20 ++++++++++++-------- 4 files changed, 41 insertions(+), 31 deletions(-) rename {cmd => pkg}/compose/build_test.go (100%) diff --git a/cmd/compose/build.go b/cmd/compose/build.go index bd3f10b00fe..58636e0dcda 100644 --- a/cmd/compose/build.go +++ b/cmd/compose/build.go @@ -27,7 +27,6 @@ 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" @@ -141,13 +140,11 @@ func buildCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) } func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Service, opts buildOptions, services []string) error { - project, _, err := opts.ToProject(ctx, dockerCli, services, cli.WithResolvedPaths(true), cli.WithoutEnvironmentResolution) + project, _, err := opts.ToProject(ctx, dockerCli, nil, cli.WithResolvedPaths(true), cli.WithoutEnvironmentResolution) if err != nil { return err } - services = addBuildDependencies(services, project) - if err := applyPlatforms(project, false); err != nil { return err } @@ -159,21 +156,3 @@ func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Service, o 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() -} diff --git a/pkg/compose/build.go b/pkg/compose/build.go index a32fe6528f6..d7c14cfa90a 100644 --- a/pkg/compose/build.go +++ b/pkg/compose/build.go @@ -85,7 +85,16 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti policy = types.IncludeDependencies } - err := project.ForEachService(options.Services, func(serviceName string, service *types.ServiceConfig) error { + if len(options.Services) > 0 { + // As user requested some services to be built, also include those used as additional_contexts + options.Services = addBuildDependencies(options.Services, project) + } + project, err := project.WithSelectedServices(options.Services) + if err != nil { + return nil, err + } + + err = project.ForEachService(options.Services, func(serviceName string, service *types.ServiceConfig) error { if service.Build == nil { return nil } @@ -613,3 +622,21 @@ func parsePlatforms(service types.ServiceConfig) ([]specs.Platform, error) { return ret, nil } + +func addBuildDependencies(services []string, project *types.Project) []string { + servicesWithDependencies := utils.NewSet(services...) + for _, service := range services { + b := project.Services[service].Build + if b != nil { + for _, target := range b.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() +} diff --git a/cmd/compose/build_test.go b/pkg/compose/build_test.go similarity index 100% rename from cmd/compose/build_test.go rename to pkg/compose/build_test.go diff --git a/pkg/e2e/build_test.go b/pkg/e2e/build_test.go index 9231ab1ec9a..1b31f6fbe02 100644 --- a/pkg/e2e/build_test.go +++ b/pkg/e2e/build_test.go @@ -117,14 +117,14 @@ func TestLocalComposeBuild(t *testing.T) { }) t.Run(env+" rebuild when up --build", func(t *testing.T) { - res := c.RunDockerComposeCmd(t, "--workdir", "fixtures/build-test", "up", "-d", "--build") + res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test", "up", "-d", "--build") res.Assert(t, icmd.Expected{Out: "COPY static /usr/share/nginx/html"}) res.Assert(t, icmd.Expected{Out: "COPY static2 /usr/share/nginx/html"}) }) t.Run(env+" build --push ignored for unnamed images", func(t *testing.T) { - res := c.RunDockerComposeCmd(t, "--workdir", "fixtures/build-test", "build", "--push", "nginx") + res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test", "build", "--push", "nginx") assert.Assert(t, !strings.Contains(res.Stdout(), "failed to push"), res.Stdout()) }) @@ -232,7 +232,7 @@ func TestBuildTags(t *testing.T) { } func TestBuildImageDependencies(t *testing.T) { - doTest := func(t *testing.T, cli *CLI) { + doTest := func(t *testing.T, cli *CLI, args ...string) { resetState := func() { cli.RunDockerComposeCmd(t, "down", "--rmi=all", "-t=0") res := cli.RunDockerOrExitError(t, "image", "rm", "build-dependencies-service") @@ -250,7 +250,7 @@ func TestBuildImageDependencies(t *testing.T) { Err: "No such image: build-dependencies-service", }) - res = cli.RunDockerComposeCmd(t, "build") + res = cli.RunDockerComposeCmd(t, args...) t.Log(res.Combined()) res = cli.RunDockerCmd(t, @@ -273,7 +273,8 @@ func TestBuildImageDependencies(t *testing.T) { "DOCKER_BUILDKIT=0", "COMPOSE_FILE=./fixtures/build-dependencies/classic.yaml", )) - doTest(t, cli) + doTest(t, cli, "build") + doTest(t, cli, "build", "--with-dependencies", "service") }) t.Run("BuildKit by dependency order", func(t *testing.T) { @@ -281,7 +282,8 @@ func TestBuildImageDependencies(t *testing.T) { "DOCKER_BUILDKIT=1", "COMPOSE_FILE=./fixtures/build-dependencies/classic.yaml", )) - doTest(t, cli) + doTest(t, cli, "build") + doTest(t, cli, "build", "--with-dependencies", "service") }) t.Run("BuildKit by additional contexts", func(t *testing.T) { @@ -289,7 +291,8 @@ func TestBuildImageDependencies(t *testing.T) { "DOCKER_BUILDKIT=1", "COMPOSE_FILE=./fixtures/build-dependencies/compose.yaml", )) - doTest(t, cli) + doTest(t, cli, "build") + doTest(t, cli, "build", "service") }) t.Run("Bake by additional contexts", func(t *testing.T) { @@ -297,7 +300,8 @@ func TestBuildImageDependencies(t *testing.T) { "DOCKER_BUILDKIT=1", "COMPOSE_BAKE=1", "COMPOSE_FILE=./fixtures/build-dependencies/compose.yaml", )) - doTest(t, cli) + doTest(t, cli, "build") + doTest(t, cli, "build", "service") }) } From cee6a3c660dcea2c55a1313ec33070a74e01cc6e Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Fri, 25 Apr 2025 09:32:57 +0200 Subject: [PATCH 18/28] document extensibility using service.provider Signed-off-by: Nicolas De Loof --- docs/extension.md | 104 +++++++++++++++++++++++++++++++++++++++++ pkg/compose/plugins.go | 7 ++- 2 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 docs/extension.md diff --git a/docs/extension.md b/docs/extension.md new file mode 100644 index 00000000000..9eeea7b1955 --- /dev/null +++ b/docs/extension.md @@ -0,0 +1,104 @@ +# About + +The Compose application model defines `service` as an abstraction for a computing unit managing (a subset of) +application needs, which can interact with other service by relying on network(s). Docker Compose is designed +to use the Docker Engine ("Moby") API to manage services as containers, but the abstraction _could_ also cover +many other runtimes, typically cloud services or services natively provided by host. + +The Compose extensibility model has been designed to extend the `service` support to runtimes accessible through +third-party tooling. + +# Architecture + +Compose extensibility relies on the `provider` attribute to select the actual binary responsible for managing +the resource(s) needed to run a service. + +```yaml + database: + provider: + type: awesomecloud + options: + type: mysql + size: 256 +``` + +`provider.type` tells Compose the binary to run, which can be either: +- Another Docker CLI plugin (typically, `model` to run `docker-model`) +- An executable in user's `PATH` + +To be a valid Compose extension, provider command *MUST* accept subcommand `compose` (which can be hidden) +with subcommands `up` and `down`. + +## Up lifecycle + +To execute an application's `up` lifecycle, Compose executes the provider's `compose up` command, passing +the project name, service name and additional options. The `provider.options` are translated +into command line flags. For example: +```console +awesomecloud compose --project-name up --type=mysql --size=256 "database" +``` + +> __Note:__ `project-name` _should_ be used by the provider to tag resources +> set for project, so that later execution with `down` subcommand releases +> all allocated resources set for the project. + +## Communication with Compose + +Providers can interact with Compose using `stdout` as a channel, sending JSON line delimited messages. +JSON messages MUST include a `type` and a `message` attribute. +```json +{ "type": "info", "message": "preparing mysql ..." } +``` + +`type` can be either: +- `info`: Reports status updates to the user. Compose will render message as the service state in the progress UI +- `error`: Lest the user know something went wrong with details about the error. Compose will render the message as the reason for the service failure. +- `setenv`: Let's the plugin tell Compose how dependent services can access the created resource. See next section for further details. + +```mermaid +sequenceDiagram + Shell->>Compose: docker compose up + Compose->>Provider: compose up --project-name=xx --foo=bar "database" + Provider--)Compose: json { "info": "pulling 25%" } + Compose-)Shell: pulling 25% + Provider--)Compose: json { "info": "pulling 50%" } + Compose-)Shell: pulling 50% + Provider--)Compose: json { "info": "pulling 75%" } + Compose-)Shell: pulling 75% + Provider--)Compose: json { "setenv": "URL=http://cloud.com/abcd:1234" } + Compose-)Compose: set DATABASE_URL + Provider-)Compose: EOF (command complete) exit 0 + Compose-)Shell: service started +``` + +## Connection to a service managed by a provider + +A service in the Compose application can declare dependency on a service managed by an external provider: + +```yaml +services: + app: + image: myapp + depends_on: + - database + + database: + provider: + type: awesomecloud +``` + +When the provider command sends a `setenv` JSON message, Compose injects the specified variable into any dependent service, +automatically prefixing it with the service name. For example, if `awesomecloud compose up` returns: +```json +{"type": "setenv", "message": "URL=https://awesomecloud.com/db:1234"} +``` +Then the `app` service, which depends on the service managed by the provider, will receive a `DATABASE_URL` environment variable injected +into its runtime environment. + +> __Note:__ The `compose up` provider command _MUST_ be idempotent. If resource is already running, the command _MUST_ set +> the same environment variables to ensure consistent configuration of dependent services. + +## Down lifecycle + +`down` lifecycle is equivalent to `up` with the ` compose --project-name down ` command. +The provider is responsible for releasing all resources associated with the service. \ No newline at end of file diff --git a/pkg/compose/plugins.go b/pkg/compose/plugins.go index 315f464cdcf..62ed324d5b5 100644 --- a/pkg/compose/plugins.go +++ b/pkg/compose/plugins.go @@ -59,7 +59,7 @@ func (s *composeService) runPlugin(ctx context.Context, project *types.Project, return err } - cmd := s.setupPluginCommand(ctx, project, provider, plugin.Path, command) + cmd := s.setupPluginCommand(ctx, project, service, plugin.Path, command) variables, err := s.executePlugin(ctx, cmd, command, service) if err != nil { @@ -153,11 +153,14 @@ func (s *composeService) getPluginBinaryPath(providerType string) (*manager.Plug return manager.GetPlugin(providerType, s.dockerCli, &cobra.Command{}) } -func (s *composeService) setupPluginCommand(ctx context.Context, project *types.Project, provider types.ServiceProviderConfig, path, command string) *exec.Cmd { +func (s *composeService) setupPluginCommand(ctx context.Context, project *types.Project, service types.ServiceConfig, path, command string) *exec.Cmd { + provider := *service.Provider + args := []string{"compose", "--project-name", project.Name, command} for k, v := range provider.Options { args = append(args, fmt.Sprintf("--%s=%s", k, v)) } + args = append(args, service.Name) cmd := exec.CommandContext(ctx, path, args...) // Remove DOCKER_CLI_PLUGIN... variable so plugin can detect it run standalone From 20f0ffec0ba623c7e5cf54c1b75503b712f0c1e7 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Fri, 25 Apr 2025 17:19:10 +0200 Subject: [PATCH 19/28] seach for provider binary in PATH Signed-off-by: Nicolas De Loof --- docs/extension.md | 4 ++-- pkg/compose/plugins.go | 42 +++++++++++++----------------------------- 2 files changed, 15 insertions(+), 31 deletions(-) diff --git a/docs/extension.md b/docs/extension.md index 9eeea7b1955..2ecae5e530d 100644 --- a/docs/extension.md +++ b/docs/extension.md @@ -26,13 +26,13 @@ the resource(s) needed to run a service. - Another Docker CLI plugin (typically, `model` to run `docker-model`) - An executable in user's `PATH` -To be a valid Compose extension, provider command *MUST* accept subcommand `compose` (which can be hidden) +To be a valid Compose extension, provider command *MUST* accept a `compose` command (which can be hidden) with subcommands `up` and `down`. ## Up lifecycle To execute an application's `up` lifecycle, Compose executes the provider's `compose up` command, passing -the project name, service name and additional options. The `provider.options` are translated +the project name, service name, and additional options. The `provider.options` are translated into command line flags. For example: ```console awesomecloud compose --project-name up --type=mysql --size=256 "database" diff --git a/pkg/compose/plugins.go b/pkg/compose/plugins.go index 62ed324d5b5..fd0b48ff76c 100644 --- a/pkg/compose/plugins.go +++ b/pkg/compose/plugins.go @@ -55,11 +55,7 @@ func (s *composeService) runPlugin(ctx context.Context, project *types.Project, return err } - if err := s.checkPluginEnabledInDD(ctx, plugin); err != nil { - return err - } - - cmd := s.setupPluginCommand(ctx, project, service, plugin.Path, command) + cmd := s.setupPluginCommand(ctx, project, service, plugin, command) variables, err := s.executePlugin(ctx, cmd, command, service) if err != nil { @@ -148,9 +144,18 @@ func (s *composeService) executePlugin(ctx context.Context, cmd *exec.Cmd, comma return variables, nil } -func (s *composeService) getPluginBinaryPath(providerType string) (*manager.Plugin, error) { - // Only support Docker CLI plugins for first iteration. Could support any binary from PATH - return manager.GetPlugin(providerType, s.dockerCli, &cobra.Command{}) +func (s *composeService) getPluginBinaryPath(provider string) (path string, err error) { + if provider == "compose" { + return "", errors.New("'compose' is not a valid provider type") + } + plugin, err := manager.GetPlugin(provider, s.dockerCli, &cobra.Command{}) + if err == nil { + path = plugin.Path + } + if manager.IsNotFound(err) { + path, err = exec.LookPath(provider) + } + return path, err } func (s *composeService) setupPluginCommand(ctx context.Context, project *types.Project, service types.ServiceConfig, path, command string) *exec.Cmd { @@ -182,24 +187,3 @@ func (s *composeService) setupPluginCommand(ctx context.Context, project *types. cmd.Env = append(cmd.Env, types.Mapping(carrier).Values()...) return cmd } - -func (s *composeService) checkPluginEnabledInDD(ctx context.Context, plugin *manager.Plugin) error { - if integrationEnabled := s.isDesktopIntegrationActive(); !integrationEnabled { - return fmt.Errorf("you should enable Docker Desktop integration to use %q provider services", plugin.Name) - } - - // Until we support more use cases, check explicitly status of model runner - if plugin.Name == "model" { - cmd := exec.CommandContext(ctx, "docker", "model", "status") - _, err := cmd.CombinedOutput() - if err != nil { - var exitErr *exec.ExitError - if errors.As(err, &exitErr) && exitErr.ExitCode() == 1 { - return fmt.Errorf("you should enable model runner to use %q provider services: %s", plugin.Name, err.Error()) - } - } - } else { - return fmt.Errorf("unsupported provider %q", plugin.Name) - } - return nil -} From 2dbef234dc45abe5a4bdc2394b16739ecd3069eb Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Mon, 5 May 2025 14:01:38 +0200 Subject: [PATCH 20/28] document behavior on missing extension Signed-off-by: Nicolas De Loof --- docs/extension.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/extension.md b/docs/extension.md index 2ecae5e530d..f4f15e52124 100644 --- a/docs/extension.md +++ b/docs/extension.md @@ -26,6 +26,8 @@ the resource(s) needed to run a service. - Another Docker CLI plugin (typically, `model` to run `docker-model`) - An executable in user's `PATH` +If `provider.type` doesn't resolve into any of those, Compose will report an error and interrupt the `up` command. + To be a valid Compose extension, provider command *MUST* accept a `compose` command (which can be hidden) with subcommands `up` and `down`. From 16e83f002df880c2c0d5b8112769a0c975bf3752 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Tue, 22 Apr 2025 16:42:18 +0200 Subject: [PATCH 21/28] introduce build --check Signed-off-by: Nicolas De Loof --- cmd/compose/build.go | 3 +++ docs/reference/compose_build.md | 1 + docs/reference/docker_compose_build.yaml | 10 +++++++++ pkg/api/api.go | 2 ++ pkg/compose/build_bake.go | 28 +++++++++++++++--------- pkg/e2e/build_test.go | 4 ++-- 6 files changed, 36 insertions(+), 12 deletions(-) diff --git a/cmd/compose/build.go b/cmd/compose/build.go index 58636e0dcda..f31fbd28ab3 100644 --- a/cmd/compose/build.go +++ b/cmd/compose/build.go @@ -45,6 +45,7 @@ type buildOptions struct { builder string deps bool print bool + check bool } func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions, error) { @@ -79,6 +80,7 @@ func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions, Deps: opts.deps, Memory: int64(opts.memory), Print: opts.print, + Check: opts.check, SSHs: SSHKeys, Builder: builderName, }, nil @@ -135,6 +137,7 @@ func buildCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) 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") + flags.BoolVar(&opts.check, "check", false, "Check build configuration") return cmd } diff --git a/docs/reference/compose_build.md b/docs/reference/compose_build.md index 98d573e44c3..5589a46934c 100644 --- a/docs/reference/compose_build.md +++ b/docs/reference/compose_build.md @@ -17,6 +17,7 @@ run `docker compose build` to rebuild it. |:----------------------|:--------------|:--------|:------------------------------------------------------------------------------------------------------------| | `--build-arg` | `stringArray` | | Set build-time variables for services | | `--builder` | `string` | | Set builder to use | +| `--check` | `bool` | | Check build configuration | | `--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 | diff --git a/docs/reference/docker_compose_build.yaml b/docs/reference/docker_compose_build.yaml index 3f53dcf7362..1197d5314c4 100644 --- a/docs/reference/docker_compose_build.yaml +++ b/docs/reference/docker_compose_build.yaml @@ -33,6 +33,16 @@ options: experimentalcli: false kubernetes: false swarm: false + - option: check + value_type: bool + default_value: "false" + description: Check build configuration + deprecated: false + hidden: false + experimental: false + experimentalcli: false + kubernetes: false + swarm: false - option: compress value_type: bool default_value: "true" diff --git a/pkg/api/api.go b/pkg/api/api.go index 6745991d448..0b159b22c2b 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -157,6 +157,8 @@ type BuildOptions struct { Builder string // Print don't actually run builder but print equivalent build config Print bool + // Check let builder validate build configuration + Check bool } // Apply mutates project according to build options diff --git a/pkg/compose/build_bake.go b/pkg/compose/build_bake.go index 589ed540588..31864cef802 100644 --- a/pkg/compose/build_bake.go +++ b/pkg/compose/build_bake.go @@ -176,12 +176,16 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project privileged = true } - var output string + var outputs []string + var call 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) + switch { + case options.Check: + call = "lint" + case len(service.Build.Platforms) > 1: + outputs = []string{fmt.Sprintf("type=image,push=%t", push)} + default: + outputs = []string{fmt.Sprintf("type=docker,load=true,push=%t", push)} } read = append(read, build.Context) @@ -212,7 +216,9 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project ShmSize: build.ShmSize, Ulimits: toBakeUlimits(build.Ulimits), Entitlements: entitlements, - Outputs: []string{output}, + + Outputs: outputs, + Call: call, } group.Targets = append(group.Targets, serviceName) } @@ -284,7 +290,7 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project return nil, err } - var errMessage string + var errMessage []string scanner := bufio.NewScanner(pipe) scanner.Split(bufio.ScanLines) @@ -300,7 +306,9 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project err := decoder.Decode(&status) if err != nil { if strings.HasPrefix(line, "ERROR: ") { - errMessage = line[7:] + errMessage = append(errMessage, line[7:]) + } else { + errMessage = append(errMessage, line) } continue } @@ -310,8 +318,8 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project err = eg.Wait() if err != nil { - if errMessage != "" { - return nil, errors.New(errMessage) + if len(errMessage) > 0 { + return nil, errors.New(strings.Join(errMessage, "\n")) } return nil, fmt.Errorf("failed to execute bake: %w", err) } diff --git a/pkg/e2e/build_test.go b/pkg/e2e/build_test.go index 1b31f6fbe02..6061ee106ec 100644 --- a/pkg/e2e/build_test.go +++ b/pkg/e2e/build_test.go @@ -300,8 +300,8 @@ func TestBuildImageDependencies(t *testing.T) { "DOCKER_BUILDKIT=1", "COMPOSE_BAKE=1", "COMPOSE_FILE=./fixtures/build-dependencies/compose.yaml", )) - doTest(t, cli, "build") - doTest(t, cli, "build", "service") + doTest(t, cli, "--verbose", "build") + doTest(t, cli, "--verbose", "build", "service") }) } From d6e3fa6d7432e061be1d22147aa06f8b7aeea6af Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Tue, 6 May 2025 18:41:48 +0200 Subject: [PATCH 22/28] Fix config --variables not honoring the --format flag When providing the --variables with --format flag, the current implementation always printed in human readable form. This patch correctly add the missing format in the formatter.Print function, making the commands behave as an user would expect. Example: `config --variables --format json` Signed-off-by: Alessio Perugini --- cmd/compose/config.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/cmd/compose/config.go b/cmd/compose/config.go index e3b85f43ffc..e1121d3a8ad 100644 --- a/cmd/compose/config.go +++ b/cmd/compose/config.go @@ -124,12 +124,15 @@ func configCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command { return runEnvironment(ctx, dockerCli, opts, args) } + if opts.Format == "" { + opts.Format = "yaml" + } return runConfig(ctx, dockerCli, opts, args) }), ValidArgsFunction: completeServiceNames(dockerCli, p), } flags := cmd.Flags() - flags.StringVar(&opts.Format, "format", "yaml", "Format the output. Values: [yaml | json]") + flags.StringVar(&opts.Format, "format", "", "Format the output. Values: [yaml | json]") flags.BoolVar(&opts.resolveImageDigests, "resolve-image-digests", false, "Pin image tags to digests") flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only validate the configuration, don't print anything") flags.BoolVar(&opts.noInterpolate, "no-interpolate", false, "Don't interpolate environment variables") @@ -408,7 +411,16 @@ func runVariables(ctx context.Context, dockerCli command.Cli, opts configOptions variables := template.ExtractVariables(model, template.DefaultPattern) - return formatter.Print(variables, "", dockerCli.Out(), func(w io.Writer) { + if opts.Format == "yaml" { + result, err := yaml.Marshal(variables) + if err != nil { + return err + } + fmt.Println(string(result)) + return nil + } + + return formatter.Print(variables, opts.Format, dockerCli.Out(), func(w io.Writer) { for name, variable := range variables { _, _ = fmt.Fprintf(w, "%s\t%t\t%s\t%s\n", name, variable.Required, variable.DefaultValue, variable.PresenceValue) } From f0f47a8aa80e18f48b2e3887ac0494f8a5ba6fa9 Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Tue, 6 May 2025 19:42:25 +0200 Subject: [PATCH 23/28] e2e: add tests Signed-off-by: Alessio Perugini --- cmd/compose/config.go | 2 +- pkg/e2e/config_test.go | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/cmd/compose/config.go b/cmd/compose/config.go index e1121d3a8ad..a30c8fe7073 100644 --- a/cmd/compose/config.go +++ b/cmd/compose/config.go @@ -416,7 +416,7 @@ func runVariables(ctx context.Context, dockerCli command.Cli, opts configOptions if err != nil { return err } - fmt.Println(string(result)) + fmt.Print(string(result)) return nil } diff --git a/pkg/e2e/config_test.go b/pkg/e2e/config_test.go index 6fef7bc02d8..15d3e3d932a 100644 --- a/pkg/e2e/config_test.go +++ b/pkg/e2e/config_test.go @@ -46,4 +46,25 @@ func TestLocalComposeConfig(t *testing.T) { res := c.RunDockerComposeCmd(t, "-f", "./fixtures/config/compose.yaml", "--project-name", projectName, "config", "--no-interpolate") res.Assert(t, icmd.Expected{Out: `- ${PORT:-8080}:80`}) }) + + t.Run("--variables --format json", func(t *testing.T) { + res := c.RunDockerComposeCmd(t, "-f", "./fixtures/config/compose.yaml", "--project-name", projectName, "config", "--variables", "--format", "json") + res.Assert(t, icmd.Expected{Out: `{ + "PORT": { + "Name": "PORT", + "DefaultValue": "8080", + "PresenceValue": "", + "Required": false + } +}`}) + }) + + t.Run("--variables --format yaml", func(t *testing.T) { + res := c.RunDockerComposeCmd(t, "-f", "./fixtures/config/compose.yaml", "--project-name", projectName, "config", "--variables", "--format", "yaml") + res.Assert(t, icmd.Expected{Out: `PORT: + name: PORT + defaultvalue: "8080" + presencevalue: "" + required: false`}) + }) } From 4bf18d2325d2464dde6d8d9e1ecb8d921ea66e52 Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Tue, 6 May 2025 20:54:30 +0200 Subject: [PATCH 24/28] docs: regenerate Signed-off-by: Alessio Perugini --- docs/reference/compose_config.md | 2 +- docs/reference/docker_compose_config.yaml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/reference/compose_config.md b/docs/reference/compose_config.md index 9e87efd29cb..78c1835a527 100644 --- a/docs/reference/compose_config.md +++ b/docs/reference/compose_config.md @@ -15,7 +15,7 @@ the canonical format. |:--------------------------|:---------|:--------|:----------------------------------------------------------------------------| | `--dry-run` | `bool` | | Execute command in dry run mode | | `--environment` | `bool` | | Print environment used for interpolation. | -| `--format` | `string` | `yaml` | Format the output. Values: [yaml \| json] | +| `--format` | `string` | | Format the output. Values: [yaml \| json] | | `--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 | diff --git a/docs/reference/docker_compose_config.yaml b/docs/reference/docker_compose_config.yaml index 15b1e7dc398..7ec479b2000 100644 --- a/docs/reference/docker_compose_config.yaml +++ b/docs/reference/docker_compose_config.yaml @@ -21,7 +21,6 @@ options: swarm: false - option: format value_type: string - default_value: yaml description: 'Format the output. Values: [yaml | json]' deprecated: false hidden: false From 9ee03c3fec2e2eef070341ca51e4bbecea7bc36a Mon Sep 17 00:00:00 2001 From: Guillaume Lours <705411+glours@users.noreply.github.com> Date: Wed, 7 May 2025 11:31:49 +0200 Subject: [PATCH 25/28] bump compose-go to v2.6.2 Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e7e8ca6ebaa..aafd895c1b8 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( 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.6.2-0.20250428082045-7eb3472a5a95 + github.com/compose-spec/compose-go/v2 v2.6.2 github.com/containerd/containerd/v2 v2.0.5 github.com/containerd/platforms v1.0.0-rc.1 github.com/davecgh/go-spew v1.1.1 diff --git a/go.sum b/go.sum index adffb8422f1..20dd5ac2851 100644 --- a/go.sum +++ b/go.sum @@ -82,8 +82,8 @@ github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= github.com/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.6.2-0.20250428082045-7eb3472a5a95 h1:lWMFXVkl+LkMdllYT4P/5snsB8JNhQoFnSsRX1pbX1o= -github.com/compose-spec/compose-go/v2 v2.6.2-0.20250428082045-7eb3472a5a95/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA= +github.com/compose-spec/compose-go/v2 v2.6.2 h1:31uZNNLeRrKjtUCc56CzPpPykW1Tm6SxLn4gx9Jjzqw= +github.com/compose-spec/compose-go/v2 v2.6.2/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA= 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= From 62633611903b27cfdd8bf588d508f8278f4bbf4b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 May 2025 09:57:35 +0000 Subject: [PATCH 26/28] build(deps): bump github.com/moby/buildkit from 0.21.0 to 0.21.1 Bumps [github.com/moby/buildkit](https://github.com/moby/buildkit) from 0.21.0 to 0.21.1. - [Release notes](https://github.com/moby/buildkit/releases) - [Commits](https://github.com/moby/buildkit/compare/v0.21.0...v0.21.1) --- updated-dependencies: - dependency-name: github.com/moby/buildkit dependency-version: 0.21.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index aafd895c1b8..d28446e16d5 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( 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.21.0 + github.com/moby/buildkit v0.21.1 github.com/moby/go-archive v0.1.0 github.com/moby/patternmatcher v0.6.0 github.com/moby/sys/atomicwriter v0.1.0 diff --git a/go.sum b/go.sum index 20dd5ac2851..1621ae5898c 100644 --- a/go.sum +++ b/go.sum @@ -318,8 +318,8 @@ github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/z github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/moby/buildkit v0.21.0 h1:+z4vVqgt0spLrOSxi4DLedRbIh2gbNVlZ5q4rsnNp60= -github.com/moby/buildkit v0.21.0/go.mod h1:mBq0D44uCyz2PdX8T/qym5LBbkBO3GGv0wqgX9ABYYw= +github.com/moby/buildkit v0.21.1 h1:wTjVLfirh7skZt9piaIlNo8WdiPjza1CDl2EArDV9bA= +github.com/moby/buildkit v0.21.1/go.mod h1:mBq0D44uCyz2PdX8T/qym5LBbkBO3GGv0wqgX9ABYYw= 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/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= From 480a556bf04e678b45f72cb1fffba1c6973c0bdc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 May 2025 10:09:59 +0000 Subject: [PATCH 27/28] build(deps): bump golang.org/x/sync from 0.13.0 to 0.14.0 Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.13.0 to 0.14.0. - [Commits](https://github.com/golang/sync/compare/v0.13.0...v0.14.0) --- updated-dependencies: - dependency-name: golang.org/x/sync dependency-version: 0.14.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d28446e16d5..3dd8e4c1c28 100644 --- a/go.mod +++ b/go.mod @@ -54,7 +54,7 @@ require ( go.uber.org/goleak v1.3.0 go.uber.org/mock v0.5.2 golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 - golang.org/x/sync v0.13.0 + golang.org/x/sync v0.14.0 golang.org/x/sys v0.32.0 google.golang.org/grpc v1.72.0 gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index 1621ae5898c..9ec360b62d9 100644 --- a/go.sum +++ b/go.sum @@ -574,8 +574,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.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.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= From 559a51e5902c0d6e3b24df771c92d65ec5dda3d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 May 2025 10:20:57 +0000 Subject: [PATCH 28/28] build(deps): bump golang.org/x/sys from 0.32.0 to 0.33.0 Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.32.0 to 0.33.0. - [Commits](https://github.com/golang/sys/compare/v0.32.0...v0.33.0) --- updated-dependencies: - dependency-name: golang.org/x/sys dependency-version: 0.33.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3dd8e4c1c28..11e866b77d5 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,7 @@ require ( go.uber.org/mock v0.5.2 golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 golang.org/x/sync v0.14.0 - golang.org/x/sys v0.32.0 + golang.org/x/sys v0.33.0 google.golang.org/grpc v1.72.0 gopkg.in/yaml.v3 v3.0.1 gotest.tools/v3 v3.5.2 diff --git a/go.sum b/go.sum index 9ec360b62d9..8230a381851 100644 --- a/go.sum +++ b/go.sum @@ -597,8 +597,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc 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.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/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.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=