diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 829087982..37b546967 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -12,6 +12,12 @@ body: Include both the current behavior (what you are seeing) as well as what you expected to happen. validations: required: true + - type: markdown + attributes: + value: | + [Docker Swarm](https://www.mirantis.com/software/swarm/) uses a distinct compose file parser and + as such doesn't support some of the recent features of Docker Compose. Please contact Mirantis + if you need assistance with compose file support in Docker Swarm. - type: textarea attributes: label: Steps To Reproduce diff --git a/.golangci.yml b/.golangci.yml index d00a1f9f7..1d8ae0bb3 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -30,6 +30,8 @@ linters: deny: - pkg: io/ioutil desc: io/ioutil package has been deprecated + - pkg: github.com/docker/docker/errdefs + desc: use github.com/containerd/errdefs instead. - pkg: golang.org/x/exp/maps desc: use stdlib maps package - pkg: golang.org/x/exp/slices diff --git a/README.md b/README.md index 3dfc0de10..58bbdb463 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,12 @@ your application are configured. Once you have a Compose file, you can create and start your application with a single command: `docker compose up`. +> **Note**: About Docker Swarm +> Docker Swarm used to rely on the legacy compose file format but did not adopted the compose specification +> so is missing some of the recent enhancements in the compose syntax. After +> [acquisition by Mirantis](https://www.mirantis.com/software/swarm/) swarm isn't maintained by Docker Inc, and +> as such some Docker Compose features aren't accessible to swarm users. + # Where to get Docker Compose ### Windows and macOS diff --git a/cmd/cmdtrace/cmd_span.go b/cmd/cmdtrace/cmd_span.go index 9226fa13c..e75056c29 100644 --- a/cmd/cmdtrace/cmd_span.go +++ b/cmd/cmdtrace/cmd_span.go @@ -55,8 +55,10 @@ func Setup(cmd *cobra.Command, dockerCli command.Cli, args []string) error { ctx, "cli/"+strings.Join(commandName(cmd), "-"), ) - cmdSpan.SetAttributes(attribute.StringSlice("cli.flags", getFlags(cmd.Flags()))) - cmdSpan.SetAttributes(attribute.Bool("cli.isatty", dockerCli.In().IsTerminal())) + cmdSpan.SetAttributes( + attribute.StringSlice("cli.flags", getFlags(cmd.Flags())), + attribute.Bool("cli.isatty", dockerCli.In().IsTerminal()), + ) cmd.SetContext(ctx) wrapRunE(cmd, cmdSpan, tracingShutdown) diff --git a/cmd/compose/build.go b/cmd/compose/build.go index e29a9d89e..bd3767aee 100644 --- a/cmd/compose/build.go +++ b/cmd/compose/build.go @@ -45,7 +45,8 @@ type buildOptions struct { deps bool print bool check bool - provenance bool + sbom string + provenance string } func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions, error) { @@ -84,6 +85,7 @@ func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions, Check: opts.check, SSHs: SSHKeys, Builder: builderName, + SBOM: opts.sbom, Provenance: opts.provenance, }, nil } @@ -119,12 +121,14 @@ func buildCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) } flags := cmd.Flags() flags.BoolVar(&opts.push, "push", false, "Push service images") - flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Don't print anything to STDOUT") + flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress the build output") flags.BoolVar(&opts.pull, "pull", false, "Always attempt to pull a newer version of the image") flags.StringArrayVar(&opts.args, "build-arg", []string{}, "Set build-time variables for services") flags.StringVar(&opts.ssh, "ssh", "", "Set SSH authentications used when building service images. (use 'default' for using your default SSH Agent)") flags.StringVar(&opts.builder, "builder", "", "Set builder to use") flags.BoolVar(&opts.deps, "with-dependencies", false, "Also build dependencies (transitively)") + flags.StringVar(&opts.provenance, "provenance", "", `Add a provenance attestation`) + flags.StringVar(&opts.sbom, "sbom", "", `Add a SBOM attestation`) flags.Bool("parallel", true, "Build images in parallel. DEPRECATED") flags.MarkHidden("parallel") //nolint:errcheck @@ -156,7 +160,7 @@ func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Service, o } apiBuildOptions, err := opts.toAPIBuildOptions(services) - apiBuildOptions.Provenance = true + apiBuildOptions.Attestations = true if err != nil { return err } diff --git a/cmd/compose/up.go b/cmd/compose/up.go index dcde8b4ae..0c8066d05 100644 --- a/cmd/compose/up.go +++ b/cmd/compose/up.go @@ -165,6 +165,7 @@ func upCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *c flags.BoolVar(&create.recreateDeps, "always-recreate-deps", false, "Recreate dependent containers. Incompatible with --no-recreate.") flags.BoolVarP(&create.noInherit, "renew-anon-volumes", "V", false, "Recreate anonymous volumes instead of retrieving data from the previous containers") flags.BoolVar(&create.quietPull, "quiet-pull", false, "Pull without printing progress information") + flags.BoolVar(&build.quiet, "quiet-build", false, "Suppress the build output") flags.StringArrayVar(&up.attach, "attach", []string{}, "Restrict attaching to the specified services. Incompatible with --attach-dependencies.") flags.StringArrayVar(&up.noAttach, "no-attach", []string{}, "Do not attach (stream logs) to the specified services") flags.BoolVar(&up.attachDependencies, "attach-dependencies", false, "Automatically attach to log output of dependent services") diff --git a/cmd/formatter/ansi.go b/cmd/formatter/ansi.go index 0d59cf501..14429687b 100644 --- a/cmd/formatter/ansi.go +++ b/cmd/formatter/ansi.go @@ -42,13 +42,6 @@ func restoreCursor() { fmt.Print(ansi("8")) } -func hideCursor() { - if disableAnsi { - return - } - fmt.Print(ansi("[?25l")) -} - func showCursor() { if disableAnsi { return diff --git a/cmd/formatter/colors.go b/cmd/formatter/colors.go index cc13bc209..ea0e1a263 100644 --- a/cmd/formatter/colors.go +++ b/cmd/formatter/colors.go @@ -19,6 +19,7 @@ package formatter import ( "fmt" "strconv" + "strings" "sync" "github.com/docker/cli/cli/command" @@ -58,6 +59,9 @@ const ( Auto = "auto" ) +// ansiColorOffset is the offset for basic foreground colors in ANSI escape codes. +const ansiColorOffset = 30 + // SetANSIMode configure formatter for colored output on ANSI-compliant console func SetANSIMode(streams command.Streams, ansi string) { if !useAnsi(streams, ansi) { @@ -91,11 +95,15 @@ func ansiColor(code, s string, formatOpts ...string) string { // Everything about ansiColorCode color https://hyperskill.org/learn/step/18193 func ansiColorCode(code string, formatOpts ...string) string { - res := "\033[" + var sb strings.Builder + sb.WriteString("\033[") for _, c := range formatOpts { - res = fmt.Sprintf("%s%s;", res, c) + sb.WriteString(c) + sb.WriteString(";") } - return fmt.Sprintf("%s%sm", res, code) + sb.WriteString(code) + sb.WriteString("m") + return sb.String() } func makeColorFunc(code string) colorFunc { @@ -122,8 +130,8 @@ func rainbowColor() colorFunc { func init() { colors := map[string]colorFunc{} for i, name := range names { - colors[name] = makeColorFunc(strconv.Itoa(30 + i)) - colors["intense_"+name] = makeColorFunc(strconv.Itoa(30+i) + ";1") + colors[name] = makeColorFunc(strconv.Itoa(ansiColorOffset + i)) + colors["intense_"+name] = makeColorFunc(strconv.Itoa(ansiColorOffset+i) + ";1") } rainbow = []colorFunc{ colors["cyan"], diff --git a/cmd/formatter/stopping.go b/cmd/formatter/stopping.go deleted file mode 100644 index afa248673..000000000 --- a/cmd/formatter/stopping.go +++ /dev/null @@ -1,119 +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 formatter - -import ( - "fmt" - "strings" - "time" - - "github.com/buger/goterm" - "github.com/docker/compose/v2/pkg/api" - "github.com/docker/compose/v2/pkg/progress" -) - -type Stopping struct { - api.LogConsumer - enabled bool - spinner *progress.Spinner - ticker *time.Ticker - startedAt time.Time -} - -func NewStopping(l api.LogConsumer) *Stopping { - s := &Stopping{} - s.LogConsumer = logDecorator{ - decorated: l, - Before: s.clear, - After: s.print, - } - return s -} - -func (s *Stopping) ApplicationTermination() { - if progress.Mode != progress.ModeAuto { - // User explicitly opted for output format - return - } - if disableAnsi { - return - } - s.enabled = true - s.spinner = progress.NewSpinner() - hideCursor() - s.startedAt = time.Now() - s.ticker = time.NewTicker(100 * time.Millisecond) - go func() { - for { - <-s.ticker.C - s.print() - } - }() -} - -func (s *Stopping) Close() { - showCursor() - if s.ticker != nil { - s.ticker.Stop() - } - s.clear() -} - -func (s *Stopping) clear() { - if !s.enabled { - return - } - - height := goterm.Height() - carriageReturn() - saveCursor() - - // clearLine() - for i := 0; i < height; i++ { - moveCursorDown(1) - clearLine() - } - restoreCursor() -} - -const stoppingBanner = "Gracefully Stopping... (press Ctrl+C again to force)" - -func (s *Stopping) print() { - if !s.enabled { - return - } - - height := goterm.Height() - width := goterm.Width() - carriageReturn() - saveCursor() - - moveCursor(height, 0) - clearLine() - elapsed := time.Since(s.startedAt).Seconds() - timer := fmt.Sprintf("%.1fs ", elapsed) - pad := width - len(timer) - len(stoppingBanner) - 5 - fmt.Printf("%s %s %s %s", - progress.CountColor(s.spinner.String()), - stoppingBanner, - strings.Repeat(" ", pad), - progress.TimerColor(timer), - ) - - carriageReturn() - restoreCursor() -} diff --git a/docs/reference/compose_build.md b/docs/reference/compose_build.md index 5589a4693..a715974df 100644 --- a/docs/reference/compose_build.md +++ b/docs/reference/compose_build.md @@ -22,9 +22,11 @@ run `docker compose build` to rebuild it. | `-m`, `--memory` | `bytes` | `0` | Set memory limit for the build container. Not supported by BuildKit. | | `--no-cache` | `bool` | | Do not use cache when building the image | | `--print` | `bool` | | Print equivalent bake file | +| `--provenance` | `string` | | Add a provenance attestation | | `--pull` | `bool` | | Always attempt to pull a newer version of the image | | `--push` | `bool` | | Push service images | -| `-q`, `--quiet` | `bool` | | Don't print anything to STDOUT | +| `-q`, `--quiet` | `bool` | | Suppress the build output | +| `--sbom` | `string` | | Add a SBOM attestation | | `--ssh` | `string` | | Set SSH authentications used when building service images. (use 'default' for using your default SSH Agent) | | `--with-dependencies` | `bool` | | Also build dependencies (transitively) | diff --git a/docs/reference/compose_up.md b/docs/reference/compose_up.md index b831cb16d..b7f17a0fa 100644 --- a/docs/reference/compose_up.md +++ b/docs/reference/compose_up.md @@ -44,6 +44,7 @@ If the process is interrupted using `SIGINT` (ctrl + C) or `SIGTERM`, the contai | `--no-recreate` | `bool` | | If containers already exist, don't recreate them. Incompatible with --force-recreate. | | `--no-start` | `bool` | | Don't start the services after creating them | | `--pull` | `string` | `policy` | Pull image before running ("always"\|"missing"\|"never") | +| `--quiet-build` | `bool` | | Suppress the build output | | `--quiet-pull` | `bool` | | Pull without printing progress information | | `--remove-orphans` | `bool` | | Remove containers for services not defined in the Compose file | | `-V`, `--renew-anon-volumes` | `bool` | | Recreate anonymous volumes instead of retrieving data from the previous containers | diff --git a/docs/reference/docker_compose_build.yaml b/docs/reference/docker_compose_build.yaml index 6d1446a51..e645a40aa 100644 --- a/docs/reference/docker_compose_build.yaml +++ b/docs/reference/docker_compose_build.yaml @@ -125,6 +125,15 @@ options: experimentalcli: false kubernetes: false swarm: false + - option: provenance + value_type: string + description: Add a provenance attestation + deprecated: false + hidden: false + experimental: false + experimentalcli: false + kubernetes: false + swarm: false - option: pull value_type: bool default_value: "false" @@ -149,7 +158,16 @@ options: shorthand: q value_type: bool default_value: "false" - description: Don't print anything to STDOUT + description: Suppress the build output + deprecated: false + hidden: false + experimental: false + experimentalcli: false + kubernetes: false + swarm: false + - option: sbom + value_type: string + description: Add a SBOM attestation deprecated: false hidden: false experimental: false diff --git a/docs/reference/docker_compose_up.yaml b/docs/reference/docker_compose_up.yaml index 47e0c5259..8c78a8fa6 100644 --- a/docs/reference/docker_compose_up.yaml +++ b/docs/reference/docker_compose_up.yaml @@ -211,6 +211,16 @@ options: experimentalcli: false kubernetes: false swarm: false + - option: quiet-build + value_type: bool + default_value: "false" + description: Suppress the build output + deprecated: false + hidden: false + experimental: false + experimentalcli: false + kubernetes: false + swarm: false - option: quiet-pull value_type: bool default_value: "false" diff --git a/go.mod b/go.mod index 9c1d26b79..34bc6a18f 100644 --- a/go.mod +++ b/go.mod @@ -8,13 +8,13 @@ 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.7.1 + github.com/compose-spec/compose-go/v2 v2.8.1 github.com/containerd/containerd/v2 v2.1.3 github.com/containerd/errdefs v1.0.0 github.com/containerd/platforms v1.0.0-rc.1 github.com/davecgh/go-spew v1.1.1 github.com/distribution/reference v0.6.0 - github.com/docker/buildx v0.25.0 + github.com/docker/buildx v0.26.1 github.com/docker/cli v28.3.2+incompatible github.com/docker/cli-docs-tool v0.10.0 github.com/docker/docker v28.3.2+incompatible @@ -29,7 +29,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.23.2 + github.com/moby/buildkit v0.23.0-rc1.0.20250618182037-9b91d20367db // master github.com/moby/go-archive v0.1.0 github.com/moby/patternmatcher v0.6.0 github.com/moby/sys/atomicwriter v0.1.0 @@ -41,21 +41,21 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/spf13/cobra v1.9.1 - github.com/spf13/pflag v1.0.6 + github.com/spf13/pflag v1.0.7 github.com/stretchr/testify v1.10.0 github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 - go.opentelemetry.io/otel v1.35.0 + go.opentelemetry.io/otel v1.36.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 - go.opentelemetry.io/otel/metric v1.35.0 - go.opentelemetry.io/otel/sdk v1.35.0 - go.opentelemetry.io/otel/trace v1.35.0 + go.opentelemetry.io/otel/metric v1.36.0 + go.opentelemetry.io/otel/sdk v1.36.0 + go.opentelemetry.io/otel/trace v1.36.0 go.uber.org/goleak v1.3.0 go.uber.org/mock v0.5.2 golang.org/x/sync v0.16.0 golang.org/x/sys v0.34.0 - google.golang.org/grpc v1.73.0 + google.golang.org/grpc v1.74.2 gopkg.in/yaml.v3 v3.0.1 gotest.tools/v3 v3.5.2 tags.cncf.io/container-device-interface v1.0.1 @@ -64,7 +64,7 @@ require ( require ( dario.cat/mergo v1.0.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect - github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect @@ -98,7 +98,7 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fvbommel/sortorder v1.1.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect @@ -175,20 +175,21 @@ require ( github.com/zclconf/go-cty v1.16.2 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect go.opentelemetry.io/proto/otlp v1.5.0 // indirect - golang.org/x/crypto v0.37.0 // indirect - golang.org/x/net v0.39.0 // indirect - golang.org/x/oauth2 v0.29.0 // indirect - golang.org/x/term v0.31.0 // indirect - golang.org/x/text v0.24.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/crypto v0.38.0 // indirect + golang.org/x/net v0.40.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/term v0.32.0 // indirect + golang.org/x/text v0.25.0 // indirect golang.org/x/time v0.11.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index 14e909e04..fd9317d07 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,8 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e h1:rd4bOvKmDIx0WeTv9Qz+hghsgyjikFiPrseXHlKepO0= github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e/go.mod h1:blbwPQh4DTlCZEfk1BLU4oMIhLda2U+A840Uag9DsZw= -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA= @@ -80,8 +80,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.7.1 h1:EUIbuaD0R/J1KA+FbJMNbcS9+jt/CVudbp5iHqUllSs= -github.com/compose-spec/compose-go/v2 v2.7.1/go.mod h1:TmjkIB9W73fwVxkYY+u2uhMbMUakjiif79DlYgXsyvU= +github.com/compose-spec/compose-go/v2 v2.8.1 h1:27O4dzyhiS/UEUKp1zHOHCBWD1WbxGsYGMNNaSejTk4= +github.com/compose-spec/compose-go/v2 v2.8.1/go.mod h1:veko/VB7URrg/tKz3vmIAQDaz+CGiXH8vZsW79NmAww= 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.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc= @@ -127,8 +127,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/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/buildx v0.25.0 h1:qs5WxBo0wQKSXcQ+v6UhWaeM2Pu+95ZCymaimRzInaE= -github.com/docker/buildx v0.25.0/go.mod h1:xJcOeBhz49tgqN174MMGuOU4bxNmgfaLnZn7Gm641EE= +github.com/docker/buildx v0.26.1 h1:nlj3bVhHK9fV7g6floRvGhPcR0u2hxCPMmObCS1ZKL4= +github.com/docker/buildx v0.26.1/go.mod h1:oxMC30cSHPaCCkY2j+EqN7uxFikjSzVC0c44lo9b4Fo= github.com/docker/cli v28.3.2+incompatible h1:mOt9fcLE7zaACbxW1GeS65RI67wIJrTnqS3hP2huFsY= github.com/docker/cli v28.3.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli-docs-tool v0.10.0 h1:bOD6mKynPQgojQi3s2jgcUWGp/Ebqy1SeCr9VfKQLLU= @@ -171,8 +171,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= @@ -317,8 +317,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.23.2 h1:gt/dkfcpgTXKx+B9I310kV767hhVqTvEyxGgI3mqsGQ= -github.com/moby/buildkit v0.23.2/go.mod h1:iEjAfPQKIuO+8y6OcInInvzqTMiKMbb2RdJz1K/95a0= +github.com/moby/buildkit v0.23.0-rc1.0.20250618182037-9b91d20367db h1:ZzrDuG9G1A/RwJvuogNplxCEKsIUQh1CqEnqbOGFgKE= +github.com/moby/buildkit v0.23.0-rc1.0.20250618182037-9b91d20367db/go.mod h1:v5jMDvQgUyidk3wu3NvVAAd5JJo83nfet9Gf/o0+EAQ= 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= @@ -450,8 +450,9 @@ github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wx github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431 h1:XTHrT015sxHyJ5FnQ0AeemSspZWaDq7DoTRW0EVsDCE= github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.0/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= +github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c h1:2EejZtjFjKJGk71ANb+wtFK5EjUzUkEM3R0xnp559xg= github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -508,36 +509,38 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= -go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 h1:4BZHA+B1wXEQoGNHxW8mURaLhcdGwvRnmhGbm+odRbc= -go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0/go.mod h1:3qi2EEwMgB4xnKgPLqsDP3j9qxnHDZeHsnAxfjQqTko= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 h1:FZ6ei8GFW7kyPYdxJaV2rgI6M+4tvZzhYsQ2wgyVC08= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0/go.mod h1:MdEu/mC6j3D+tTEfvI15b5Ci2Fn7NneJ71YMoiS3tpI= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0 h1:ZsXq73BERAiNuuFXYqP4MR5hBrjXfMGSO+Cx7qoOZiM= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0/go.mod h1:hg1zaDMpyZJuUzjFxFsRYBoccE86tM9Uf4IqNMUxvrY= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0 h1:0NIXxOCFx+SKbhCVxwl3ETG8ClLPAa0KuKV6p3yhxP8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0/go.mod h1:ChZSJbbfbl/DcRZNc9Gqh6DYGlfjw4PvO1pEOZH1ZsE= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= 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.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= 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= @@ -546,8 +549,8 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= -golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -562,10 +565,10 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.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.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= -golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= 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= @@ -601,14 +604,14 @@ golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= -golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -622,13 +625,13 @@ 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-20250324211829-b45e905df463 h1:hE3bRWtU6uceqlh4fhrSnUyjKHMKB9KrTLLG+bc0ddM= -google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a h1:SGktgSolFCo75dnHJF2yMvnns6jCmHFJ0vE4Vn2JKvQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go.mod h1:a77HrdMjoeKbnd2jmgcWdaS++ZLZAEq3orIOAEIKiVw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= -google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= +google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4= +google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= diff --git a/internal/tracing/attributes.go b/internal/tracing/attributes.go index 3365616eb..2c8779bc8 100644 --- a/internal/tracing/attributes.go +++ b/internal/tracing/attributes.go @@ -77,11 +77,13 @@ func ProjectOptions(ctx context.Context, proj *types.Project) SpanOptions { attribute.StringSlice("project.networks", proj.NetworkNames()), attribute.StringSlice("project.secrets", proj.SecretNames()), attribute.StringSlice("project.configs", proj.ConfigNames()), + attribute.StringSlice("project.models", proj.ModelNames()), attribute.StringSlice("project.extensions", keys(proj.Extensions)), attribute.StringSlice("project.services.active", proj.ServiceNames()), attribute.StringSlice("project.services.disabled", proj.DisabledServiceNames()), attribute.StringSlice("project.services.build", proj.ServicesWithBuild()), attribute.StringSlice("project.services.depends_on", proj.ServicesWithDependsOn()), + attribute.StringSlice("project.services.models", proj.ServicesWithModels()), attribute.StringSlice("project.services.capabilities", capabilities), attribute.StringSlice("project.services.capabilities.gpu", gpu), attribute.StringSlice("project.services.capabilities.tpu", tpu), @@ -110,6 +112,7 @@ func ServiceOptions(service types.ServiceConfig) SpanOptions { attribute.String("service.name", service.Name), attribute.String("service.image", service.Image), attribute.StringSlice("service.networks", keys(service.Networks)), + attribute.StringSlice("service.models", keys(service.Models)), } configNames := make([]string, len(service.Configs)) diff --git a/pkg/api/api.go b/pkg/api/api.go index b57a142a6..a48b5ab5f 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -19,6 +19,7 @@ package api import ( "context" "fmt" + "io" "slices" "strings" "time" @@ -170,8 +171,14 @@ type BuildOptions struct { Print bool // Check let builder validate build configuration Check bool - // Provenance - Provenance bool + // Attestations allows to enable attestations generation + Attestations bool + // Provenance generate a provenance attestation + Provenance string + // SBOM generate a SBOM attestation + SBOM string + // Out is the stream to write build progress + Out io.Writer } // Apply mutates project according to build options diff --git a/pkg/api/io.go b/pkg/api/io.go index cb83ac18d..6aa77eaf2 100644 --- a/pkg/api/io.go +++ b/pkg/api/io.go @@ -20,6 +20,7 @@ import ( "github.com/docker/cli/cli/streams" ) +// Streams defines the standard streams (stdin, stdout, stderr) used by the CLI. type Streams interface { Out() *streams.Out Err() *streams.Out diff --git a/pkg/bridge/convert.go b/pkg/bridge/convert.go index 6bd34f5a9..8e9995b82 100644 --- a/pkg/bridge/convert.go +++ b/pkg/bridge/convert.go @@ -23,10 +23,11 @@ import ( "os" "os/user" "path/filepath" + "runtime" "strconv" "github.com/compose-spec/compose-go/v2/types" - cerrdefs "github.com/containerd/errdefs" + "github.com/containerd/errdefs" "github.com/docker/cli/cli/command" cli "github.com/docker/cli/cli/command/container" "github.com/docker/compose/v2/pkg/api" @@ -112,15 +113,20 @@ func convert(ctx context.Context, dockerCli command.Cli, model map[string]any, o return err } - usr, err := user.Current() - if err != nil { - return err - } - created, err := dockerCli.Client().ContainerCreate(ctx, &container.Config{ + containerConfig := &container.Config{ Image: transformation, Env: []string{"LICENSE_AGREEMENT=true"}, - User: usr.Uid, - }, &container.HostConfig{ + } + // On POSIX systems, this is a decimal number representing the uid. + // On Windows, this is a security identifier (SID) in a string format and the engine isn't able to manage it + if runtime.GOOS != "windows" { + usr, err := user.Current() + if err != nil { + return err + } + containerConfig.User = usr.Uid + } + created, err := dockerCli.Client().ContainerCreate(ctx, containerConfig, &container.HostConfig{ AutoRemove: true, Binds: binds, }, &network.NetworkingConfig{}, nil, "") @@ -198,7 +204,7 @@ func loadFileObject(conf types.FileObjectConfig) (types.FileObjectConfig, error) func inspectWithPull(ctx context.Context, dockerCli command.Cli, imageName string) (image.InspectResponse, error) { inspect, err := dockerCli.Client().ImageInspect(ctx, imageName) - if cerrdefs.IsNotFound(err) { + if errdefs.IsNotFound(err) { var stream io.ReadCloser stream, err = dockerCli.Client().ImagePull(ctx, imageName, image.PullOptions{}) if err != nil { diff --git a/pkg/compose/build.go b/pkg/compose/build.go index d404fd0a6..c2fadee41 100644 --- a/pkg/compose/build.go +++ b/pkg/compose/build.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "os" + "strconv" "strings" "time" @@ -397,6 +398,7 @@ func resolveAndMergeBuildArgs(dockerCli command.Cli, project *types.Project, ser return result } +//nolint:gocyclo func (s *composeService) toBuildOptions(project *types.Project, service types.ServiceConfig, options api.BuildOptions) (build.Options, error) { plats, err := parsePlatforms(service) if err != nil { @@ -471,8 +473,19 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se } attests := map[string]*string{} - if !options.Provenance { - attests["provenance"] = nil + if options.Attestations { + if service.Build.Provenance != "" { + attests["provenance"] = attestation(service.Build.Provenance, "provenance") + } + if service.Build.SBOM != "" { + attests["sbom"] = attestation(service.Build.SBOM, "sbom") + } + } + if options.Provenance != "" { + attests["provenance"] = attestation(options.Provenance, "provenance") + } + if options.SBOM != "" { + attests["sbom"] = attestation(options.SBOM, "sbom") } return build.Options{ @@ -502,6 +515,16 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se }, nil } +func attestation(attest string, val string) *string { + if b, err := strconv.ParseBool(val); err == nil { + s := fmt.Sprintf("type=%s,disabled=%t", attest, b) + return &s + } else { + s := fmt.Sprintf("type=%s,%s", attest, val) + return &s + } +} + func toUlimitOpt(ulimits map[string]*types.UlimitsConfig) *cliopts.UlimitOpt { ref := map[string]*container.Ulimit{} for _, limit := range toUlimits(ulimits) { diff --git a/pkg/compose/build_bake.go b/pkg/compose/build_bake.go index 0a7b31689..299136042 100644 --- a/pkg/compose/build_bake.go +++ b/pkg/compose/build_bake.go @@ -58,6 +58,9 @@ func buildWithBake(dockerCli command.Cli) (bool, error) { return false, err } if !bake { + if ok { + logrus.Warnf("COMPOSE_BAKE=false is deprecated, support for internal compose builder will be removed in next release") + } return false, nil } @@ -127,7 +130,16 @@ type buildStatus struct { func (s *composeService) doBuildBake(ctx context.Context, project *types.Project, serviceToBeBuild types.Services, options api.BuildOptions) (map[string]string, error) { //nolint:gocyclo eg := errgroup.Group{} ch := make(chan *client.SolveStatus) - display, err := progressui.NewDisplay(os.Stdout, progressui.DisplayMode(options.Progress)) + displayMode := progressui.DisplayMode(options.Progress) + out := options.Out + if out == nil { + cout := s.dockerCli.Out() + if !cout.IsTerminal() { + displayMode = progressui.PlainMode + } + out = cout + } + display, err := progressui.NewDisplay(out, displayMode) if err != nil { return nil, err } @@ -193,7 +205,11 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project 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)} + if push { + outputs = []string{"type=registry"} + } else { + outputs = []string{"type=docker"} + } } read = append(read, build.Context) diff --git a/pkg/compose/cp.go b/pkg/compose/cp.go index 52f6685de..b7db32969 100644 --- a/pkg/compose/cp.go +++ b/pkg/compose/cp.go @@ -31,7 +31,6 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/compose/v2/pkg/api" "github.com/docker/docker/api/types/container" - "github.com/docker/docker/pkg/system" "github.com/moby/go-archive" ) @@ -161,7 +160,7 @@ func (s *composeService) copyToContainer(ctx context.Context, containerID string // If the destination is a symbolic link, we should evaluate it. if err == nil && dstStat.Mode&os.ModeSymlink != 0 { linkTarget := dstStat.LinkTarget - if !system.IsAbs(linkTarget) { + if !isAbs(linkTarget) { // Join with the parent directory. dstParent, _ := archive.SplitPathDirEntry(dstPath) linkTarget = filepath.Join(dstParent, linkTarget) @@ -264,7 +263,7 @@ func (s *composeService) copyFromContainer(ctx context.Context, containerID, src // If the destination is a symbolic link, we should follow it. if err == nil && srcStat.Mode&os.ModeSymlink != 0 { linkTarget := srcStat.LinkTarget - if !system.IsAbs(linkTarget) { + if !isAbs(linkTarget) { // Join with the parent directory. srcParent, _ := archive.SplitPathDirEntry(srcPath) linkTarget = filepath.Join(srcParent, linkTarget) @@ -302,8 +301,20 @@ func (s *composeService) copyFromContainer(ctx context.Context, containerID, src return archive.CopyTo(preArchive, srcInfo, dstPath) } +// IsAbs is a platform-agnostic wrapper for filepath.IsAbs. +// +// On Windows, golang filepath.IsAbs does not consider a path \windows\system32 +// as absolute as it doesn't start with a drive-letter/colon combination. However, +// in docker we need to verify things such as WORKDIR /windows/system32 in +// a Dockerfile (which gets translated to \windows\system32 when being processed +// by the daemon). This SHOULD be treated as absolute from a docker processing +// perspective. +func isAbs(path string) bool { + return filepath.IsAbs(path) || strings.HasPrefix(path, string(os.PathSeparator)) +} + func splitCpArg(arg string) (ctr, path string) { - if system.IsAbs(arg) { + if isAbs(arg) { // Explicit local absolute path, e.g., `C:\foo` or `/foo`. return "", arg } diff --git a/pkg/compose/create.go b/pkg/compose/create.go index b8b0afea4..04c887a7e 100644 --- a/pkg/compose/create.go +++ b/pkg/compose/create.go @@ -30,7 +30,7 @@ import ( "github.com/compose-spec/compose-go/v2/paths" "github.com/compose-spec/compose-go/v2/types" - cerrdefs "github.com/containerd/errdefs" + "github.com/containerd/errdefs" "github.com/docker/docker/api/types/blkiodev" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" @@ -1262,7 +1262,7 @@ func (s *composeService) ensureNetwork(ctx context.Context, project *types.Proje } id, err := s.resolveOrCreateNetwork(ctx, project, name, n) - if cerrdefs.IsConflict(err) { + if errdefs.IsConflict(err) { // Maybe another execution of `docker compose up|run` created same network // let's retry once return s.resolveOrCreateNetwork(ctx, project, name, n) @@ -1497,7 +1497,7 @@ func (s *composeService) resolveExternalNetwork(ctx context.Context, n *types.Ne sn, err := s.apiClient().NetworkInspect(ctx, n.Name, network.InspectOptions{}) if err == nil { networks = append(networks, sn) - } else if !cerrdefs.IsNotFound(err) { + } else if !errdefs.IsNotFound(err) { return "", err } @@ -1534,7 +1534,7 @@ func (s *composeService) resolveExternalNetwork(ctx context.Context, n *types.Ne func (s *composeService) ensureVolume(ctx context.Context, name string, volume types.VolumeConfig, project *types.Project, assumeYes bool) (string, error) { inspected, err := s.apiClient().VolumeInspect(ctx, volume.Name) if err != nil { - if !cerrdefs.IsNotFound(err) { + if !errdefs.IsNotFound(err) { return "", err } if volume.External { diff --git a/pkg/compose/down.go b/pkg/compose/down.go index b64258dff..e9619fbe6 100644 --- a/pkg/compose/down.go +++ b/pkg/compose/down.go @@ -23,7 +23,7 @@ import ( "time" "github.com/compose-spec/compose-go/v2/types" - cerrdefs "github.com/containerd/errdefs" + "github.com/containerd/errdefs" "github.com/docker/compose/v2/pkg/api" "github.com/docker/compose/v2/pkg/progress" "github.com/docker/compose/v2/pkg/utils" @@ -219,7 +219,7 @@ func (s *composeService) removeNetwork(ctx context.Context, composeNetworkName s continue } nw, err := s.apiClient().NetworkInspect(ctx, net.ID, network.InspectOptions{}) - if cerrdefs.IsNotFound(err) { + if errdefs.IsNotFound(err) { w.Event(progress.NewEvent(eventName, progress.Warning, "No resource found to remove")) return nil } @@ -233,7 +233,7 @@ func (s *composeService) removeNetwork(ctx context.Context, composeNetworkName s } if err := s.apiClient().NetworkRemove(ctx, net.ID); err != nil { - if cerrdefs.IsNotFound(err) { + if errdefs.IsNotFound(err) { continue } w.Event(progress.ErrorEvent(eventName)) @@ -261,11 +261,11 @@ func (s *composeService) removeImage(ctx context.Context, image string, w progre w.Event(progress.NewEvent(id, progress.Done, "Removed")) return nil } - if cerrdefs.IsConflict(err) { + if errdefs.IsConflict(err) { w.Event(progress.NewEvent(id, progress.Warning, "Resource is still in use")) return nil } - if cerrdefs.IsNotFound(err) { + if errdefs.IsNotFound(err) { w.Event(progress.NewEvent(id, progress.Done, "Warning: No resource found to remove")) return nil } @@ -276,7 +276,7 @@ func (s *composeService) removeVolume(ctx context.Context, id string, w progress resource := fmt.Sprintf("Volume %s", id) _, err := s.apiClient().VolumeInspect(ctx, id) - if cerrdefs.IsNotFound(err) { + if errdefs.IsNotFound(err) { // Already gone return nil } @@ -287,11 +287,11 @@ func (s *composeService) removeVolume(ctx context.Context, id string, w progress w.Event(progress.NewEvent(resource, progress.Done, "Removed")) return nil } - if cerrdefs.IsConflict(err) { + if errdefs.IsConflict(err) { w.Event(progress.NewEvent(resource, progress.Warning, "Resource is still in use")) return nil } - if cerrdefs.IsNotFound(err) { + if errdefs.IsNotFound(err) { w.Event(progress.NewEvent(resource, progress.Done, "Warning: No resource found to remove")) return nil } @@ -311,7 +311,7 @@ func (s *composeService) stopContainer( err := s.runHook(ctx, ctr, *service, hook, listener) if err != nil { // Ignore errors indicating that some containers were already stopped or removed. - if cerrdefs.IsNotFound(err) || cerrdefs.IsConflict(err) { + if errdefs.IsNotFound(err) || errdefs.IsConflict(err) { return nil } return err @@ -357,7 +357,7 @@ func (s *composeService) stopAndRemoveContainer(ctx context.Context, ctr contain w := progress.ContextWriter(ctx) eventName := getContainerProgressName(ctr) err := s.stopContainer(ctx, w, service, ctr, timeout, nil) - if cerrdefs.IsNotFound(err) { + if errdefs.IsNotFound(err) { w.Event(progress.RemovedEvent(eventName)) return nil } @@ -369,7 +369,7 @@ func (s *composeService) stopAndRemoveContainer(ctx context.Context, ctr contain Force: true, RemoveVolumes: volumes, }) - if err != nil && !cerrdefs.IsNotFound(err) && !cerrdefs.IsConflict(err) { + if err != nil && !errdefs.IsNotFound(err) && !errdefs.IsConflict(err) { w.Event(progress.ErrorMessageEvent(eventName, "Error while Removing")) return err } diff --git a/pkg/compose/down_test.go b/pkg/compose/down_test.go index faeb0a81d..58c1ec0b1 100644 --- a/pkg/compose/down_test.go +++ b/pkg/compose/down_test.go @@ -24,13 +24,13 @@ import ( "testing" "github.com/compose-spec/compose-go/v2/types" + "github.com/containerd/errdefs" "github.com/docker/cli/cli/streams" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/volume" - "github.com/docker/docker/errdefs" "go.uber.org/mock/gomock" "gotest.tools/v3/assert" @@ -326,7 +326,7 @@ func TestDownRemoveImages(t *testing.T) { if exists { resp.RepoTags = []string{img} } else { - err = errdefs.NotFound(fmt.Errorf("test specified that image %q should not exist", img)) + err = errdefs.ErrNotFound.WithMessage(fmt.Sprintf("test specified that image %q should not exist", img)) } api.EXPECT().ImageInspect(gomock.Any(), img). diff --git a/pkg/compose/image_pruner.go b/pkg/compose/image_pruner.go index ea6ed6c57..bb4d0bc47 100644 --- a/pkg/compose/image_pruner.go +++ b/pkg/compose/image_pruner.go @@ -23,7 +23,7 @@ import ( "sync" "github.com/compose-spec/compose-go/v2/types" - cerrdefs "github.com/containerd/errdefs" + "github.com/containerd/errdefs" "github.com/distribution/reference" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/image" @@ -204,7 +204,7 @@ func (p *ImagePruner) filterImagesByExistence(ctx context.Context, imageNames [] for _, img := range imageNames { eg.Go(func() error { _, err := p.client.ImageInspect(ctx, img) - if cerrdefs.IsNotFound(err) { + if errdefs.IsNotFound(err) { // err on the side of caution: only skip if we successfully // queried the API and got back a definitive "not exists" return nil diff --git a/pkg/compose/images.go b/pkg/compose/images.go index 4db036266..a91d6159f 100644 --- a/pkg/compose/images.go +++ b/pkg/compose/images.go @@ -23,7 +23,7 @@ import ( "strings" "sync" - cerrdefs "github.com/containerd/errdefs" + "github.com/containerd/errdefs" "github.com/containerd/platforms" "github.com/distribution/reference" "github.com/docker/docker/api/types/container" @@ -121,7 +121,7 @@ func (s *composeService) getImageSummaries(ctx context.Context, repoTags []strin eg.Go(func() error { inspect, err := s.apiClient().ImageInspect(ctx, repoTag) if err != nil { - if cerrdefs.IsNotFound(err) { + if errdefs.IsNotFound(err) { return nil } return fmt.Errorf("unable to get image '%s': %w", repoTag, err) diff --git a/pkg/compose/monitor.go b/pkg/compose/monitor.go index b0f9cc0af..6952b4e68 100644 --- a/pkg/compose/monitor.go +++ b/pkg/compose/monitor.go @@ -79,7 +79,7 @@ func (c *monitor) Start(ctx context.Context) error { } restarting := utils.Set[string]{} - evtCh, errCh := c.api.Events(ctx, events.ListOptions{ + evtCh, errCh := c.api.Events(context.Background(), events.ListOptions{ Filters: filters.NewArgs( filters.Arg("type", "container"), projectFilter(c.project)), @@ -89,8 +89,6 @@ func (c *monitor) Start(ctx context.Context) error { return nil } select { - case <-ctx.Done(): - return nil case err := <-errCh: return err case event := <-evtCh: diff --git a/pkg/compose/up.go b/pkg/compose/up.go index 04cf06e99..124976b48 100644 --- a/pkg/compose/up.go +++ b/pkg/compose/up.go @@ -18,7 +18,6 @@ package compose import ( "context" - "errors" "fmt" "os" "os/signal" @@ -26,13 +25,12 @@ import ( "syscall" "github.com/compose-spec/compose-go/v2/types" - cerrdefs "github.com/containerd/errdefs" + "github.com/containerd/errdefs" "github.com/docker/cli/cli" "github.com/docker/compose/v2/cmd/formatter" "github.com/docker/compose/v2/internal/tracing" "github.com/docker/compose/v2/pkg/api" "github.com/docker/compose/v2/pkg/progress" - "github.com/docker/docker/errdefs" "github.com/eiannone/keyboard" "github.com/hashicorp/go-multierror" "github.com/sirupsen/logrus" @@ -92,10 +90,6 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options } } - tui := formatter.NewStopping(logConsumer) - defer tui.Close() - logConsumer = tui - watcher, err := NewWatcher(project, options, s.watch, logConsumer) if err != nil && options.Start.Watch { return err @@ -111,7 +105,8 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options eg.Go(func() error { first := true gracefulTeardown := func() { - tui.ApplicationTermination() + first = false + fmt.Println("Gracefully Stopping... press Ctrl+C again to force") eg.Go(func() error { return progress.RunWithLog(context.WithoutCancel(ctx), func(ctx context.Context) error { return s.stop(ctx, project.Name, api.StopOptions{ @@ -121,7 +116,6 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options }, s.stdinfo(), logConsumer) }) isTerminated.Store(true) - first = false } for { @@ -148,7 +142,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options All: true, }) // Ignore errors indicating that some of the containers were already stopped or removed. - if cerrdefs.IsNotFound(err) || cerrdefs.IsConflict(err) { + if errdefs.IsNotFound(err) || errdefs.IsConflict(err) { return nil } @@ -226,8 +220,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options Follow: true, Since: ctr.State.StartedAt, }) - var notImplErr errdefs.ErrNotImplemented - if errors.As(err, ¬ImplErr) { + if errdefs.IsNotImplemented(err) { // container may be configured with logging_driver: none // as container already started, we might miss the very first logs. But still better than none return s.doAttachContainer(ctx, event.Service, event.ID, event.Source, printer.HandleEvent) @@ -238,7 +231,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options }) eg.Go(func() error { - err := monitor.Start(ctx) + err := monitor.Start(context.Background()) // Signal for the signal-handler goroutines to stop close(doneCh) return err diff --git a/pkg/compose/watch.go b/pkg/compose/watch.go index f8edf3418..60281dd0e 100644 --- a/pkg/compose/watch.go +++ b/pkg/compose/watch.go @@ -29,14 +29,17 @@ import ( gsync "sync" "time" - "github.com/compose-spec/compose-go/v2/types" - "github.com/compose-spec/compose-go/v2/utils" - ccli "github.com/docker/cli/cli/command/container" pathutil "github.com/docker/compose/v2/internal/paths" "github.com/docker/compose/v2/internal/sync" "github.com/docker/compose/v2/internal/tracing" "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/progress" + cutils "github.com/docker/compose/v2/pkg/utils" "github.com/docker/compose/v2/pkg/watch" + + "github.com/compose-spec/compose-go/v2/types" + "github.com/compose-spec/compose-go/v2/utils" + ccli "github.com/docker/cli/cli/command/container" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/image" @@ -61,7 +64,6 @@ func NewWatcher(project *types.Project, options api.UpOptions, w WatchFunc, cons if service.Develop != nil && service.Develop.Watch != nil { build := options.Create.Build - build.Quiet = true return &Watcher{ project: project, options: api.WatchOptions{ @@ -598,6 +600,10 @@ func (s *composeService) rebuild(ctx context.Context, project *types.Project, se options.LogTo.Log(api.WatchLogger, fmt.Sprintf("Rebuilding service(s) %q after changes were detected...", services)) // restrict the build to ONLY this service, not any of its dependencies options.Build.Services = services + options.Build.Progress = progress.ModePlain + options.Build.Out = cutils.GetWriter(func(line string) { + options.LogTo.Log(api.WatchLogger, line) + }) var ( imageNameToIdMap map[string]string diff --git a/pkg/progress/json_test.go b/pkg/progress/json_test.go new file mode 100644 index 000000000..fffe535af --- /dev/null +++ b/pkg/progress/json_test.go @@ -0,0 +1,89 @@ +/* + 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 progress + +import ( + "bytes" + "context" + "encoding/json" + "testing" + + "gotest.tools/v3/assert" +) + +func TestJsonWriter_Event(t *testing.T) { + var out bytes.Buffer + w := &jsonWriter{ + out: &out, + done: make(chan bool), + dryRun: true, + } + + event := Event{ + ID: "service1", + ParentID: "project", + Text: "Creating", + StatusText: "Working", + Current: 50, + Total: 100, + Percent: 50, + } + w.Event(event) + + var actual jsonMessage + err := json.Unmarshal(out.Bytes(), &actual) + assert.NilError(t, err) + + expected := jsonMessage{ + DryRun: true, + ID: event.ID, + ParentID: event.ParentID, + Text: event.Text, + Status: event.StatusText, + Current: event.Current, + Total: event.Total, + Percent: event.Percent, + } + assert.DeepEqual(t, expected, actual) +} + +func TestJsonWriter_TailMsgf(t *testing.T) { + var out bytes.Buffer + w := &jsonWriter{ + out: &out, + done: make(chan bool), + dryRun: false, + } + + go func() { + _ = w.Start(context.Background()) + }() + + w.TailMsgf("hello %s", "world") + + w.Stop() + + var actual jsonMessage + err := json.Unmarshal(out.Bytes(), &actual) + assert.NilError(t, err) + + expected := jsonMessage{ + Tail: true, + Text: "hello world", + } + assert.DeepEqual(t, expected, actual) +} diff --git a/pkg/remote/git.go b/pkg/remote/git.go index ca170daf5..a7d27afba 100644 --- a/pkg/remote/git.go +++ b/pkg/remote/git.go @@ -133,12 +133,12 @@ func (g gitRemoteLoader) resolveGitRef(ctx context.Context, path string, ref *gi if !commitSHA.MatchString(ref.Commit) { cmd := exec.CommandContext(ctx, "git", "ls-remote", "--exit-code", ref.Remote, ref.Commit) cmd.Env = g.gitCommandEnv() - out, err := cmd.Output() + out, err := cmd.CombinedOutput() if err != nil { if cmd.ProcessState.ExitCode() == 2 { return fmt.Errorf("repository does not contain ref %s, output: %q: %w", path, string(out), err) } - return err + return fmt.Errorf("failed to access repository at %s:\n %s", ref.Remote, out) } if len(out) < 40 { return fmt.Errorf("unexpected git command output: %q", string(out))