mirror of
https://github.com/docker/compose.git
synced 2026-02-13 20:19:29 +08:00
Compare commits
212 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6007d4c7e7 | ||
|
|
69bcb962bf | ||
|
|
9b4fcce034 | ||
|
|
da5c57c29d | ||
|
|
e25265dd55 | ||
|
|
e19e1278b5 | ||
|
|
585c4db4f9 | ||
|
|
be8c7e6c60 | ||
|
|
27f59d7f42 | ||
|
|
2681ed17a7 | ||
|
|
ee75be342b | ||
|
|
157617480a | ||
|
|
88aae9c46e | ||
|
|
7755302348 | ||
|
|
147923c44c | ||
|
|
289faae5fa | ||
|
|
e7aa484b78 | ||
|
|
ae3309afab | ||
|
|
0b5fb36eb5 | ||
|
|
63920c4cc0 | ||
|
|
a03f2562df | ||
|
|
a07f2b8ded | ||
|
|
f45a3ebcfd | ||
|
|
7fec70b6c7 | ||
|
|
ce463d50b2 | ||
|
|
fa7e85ed83 | ||
|
|
d9423f6872 | ||
|
|
5add90240d | ||
|
|
07602f2070 | ||
|
|
cf7e31f731 | ||
|
|
fa08127456 | ||
|
|
4ee52ad168 | ||
|
|
4a4776ec57 | ||
|
|
713de5bb9e | ||
|
|
9ded1684cd | ||
|
|
8bc8593fd0 | ||
|
|
8978c1027d | ||
|
|
032e0309ee | ||
|
|
38ba35e165 | ||
|
|
56e0ba8080 | ||
|
|
9752fa5500 | ||
|
|
4761fd88b0 | ||
|
|
02c8e63545 | ||
|
|
ab7a6e9322 | ||
|
|
2ca7b96e33 | ||
|
|
a32dc3da72 | ||
|
|
db260938c1 | ||
|
|
5aea94794c | ||
|
|
d07c437ce8 | ||
|
|
da72230c39 | ||
|
|
a429c09dfa | ||
|
|
df3c27c864 | ||
|
|
956891af54 | ||
|
|
a473341058 | ||
|
|
385b3f5c96 | ||
|
|
2d482e61ce | ||
|
|
c75418ee07 | ||
|
|
0cdc5c9bff | ||
|
|
b768232c0e | ||
|
|
09689400e5 | ||
|
|
cb3691154b | ||
|
|
b387ba4a05 | ||
|
|
7cd569922e | ||
|
|
eec2bb7ea6 | ||
|
|
2c15aef2ed | ||
|
|
290366205b | ||
|
|
a91ca95a71 | ||
|
|
beb81a73f9 | ||
|
|
f217207876 | ||
|
|
02ffe2ac6c | ||
|
|
f48131fb66 | ||
|
|
4dd369bdcb | ||
|
|
ad73766bf2 | ||
|
|
3c1f5a1815 | ||
|
|
42d1e4c333 | ||
|
|
6ca8663bda | ||
|
|
b33ecf65e8 | ||
|
|
04b8ac5fe4 | ||
|
|
d09948da41 | ||
|
|
f1efbb8322 | ||
|
|
1d52012b82 | ||
|
|
1d69f4a68c | ||
|
|
6078b4d99d | ||
|
|
73e593e69a | ||
|
|
51499f645b | ||
|
|
5165b0f814 | ||
|
|
93dd1a4558 | ||
|
|
ba3f5664c0 | ||
|
|
c420bc44c4 | ||
|
|
60681a824c | ||
|
|
19ad737ee7 | ||
|
|
d3a260e533 | ||
|
|
e75329dce2 | ||
|
|
1dc0be2c30 | ||
|
|
3bac9ffd08 | ||
|
|
f266715dd0 | ||
|
|
c2cb0aef6b | ||
|
|
fbc62d111e | ||
|
|
0d40064ce8 | ||
|
|
91a6eafa1d | ||
|
|
f36ee00f71 | ||
|
|
29ede3ba7d | ||
|
|
bf6d7bf47e | ||
|
|
fc66da06db | ||
|
|
909211dd61 | ||
|
|
0dc9852c67 | ||
|
|
a478702236 | ||
|
|
2c12ad19db | ||
|
|
038ea8441a | ||
|
|
9e98e6101e | ||
|
|
52f04229c0 | ||
|
|
28895d0322 | ||
|
|
a926f7d717 | ||
|
|
fe046915eb | ||
|
|
adbd61e5d6 | ||
|
|
e37ac04329 | ||
|
|
cab2c2a44e | ||
|
|
1946de598d | ||
|
|
8e29a138aa | ||
|
|
3c8da0afee | ||
|
|
1b12c867c5 | ||
|
|
1a4fc55fd7 | ||
|
|
efc939dcee | ||
|
|
d6e9f79ba6 | ||
|
|
b4c44a431f | ||
|
|
fb5a8644c3 | ||
|
|
95660c5e5a | ||
|
|
f6ddd6ae88 | ||
|
|
4ae7066955 | ||
|
|
fd954f266c | ||
|
|
d62e21025c | ||
|
|
6a2d16bd10 | ||
|
|
4d47da6dc2 | ||
|
|
8f91793fb5 | ||
|
|
1d2223fb23 | ||
|
|
d4f6000712 | ||
|
|
c50d16cd78 | ||
|
|
3875e13fad | ||
|
|
c89f30170d | ||
|
|
41a9b91887 | ||
|
|
5fc2b2a71c | ||
|
|
b1cd40c316 | ||
|
|
362ab0733f | ||
|
|
f35d2cfb3b | ||
|
|
17ba6c7188 | ||
|
|
1c37f1abb6 | ||
|
|
485b6200ee | ||
|
|
8c17a35609 | ||
|
|
6b9667401a | ||
|
|
9a1e589ce8 | ||
|
|
5e147e852e | ||
|
|
29308cb97e | ||
|
|
0b0242d0ac | ||
|
|
5a704004d3 | ||
|
|
cb95910018 | ||
|
|
f42226e352 | ||
|
|
0cc3c7a550 | ||
|
|
f7ee9c8a0c | ||
|
|
35efa97b7d | ||
|
|
9e17a091be | ||
|
|
4bbc6c609f | ||
|
|
69f1430a49 | ||
|
|
7cf7c6414f | ||
|
|
0e0ed91a39 | ||
|
|
66524e7728 | ||
|
|
c626befee1 | ||
|
|
60ee6adcd2 | ||
|
|
8faf1eb808 | ||
|
|
3b0601b671 | ||
|
|
f42374bb18 | ||
|
|
b046a5ef72 | ||
|
|
1570c6c076 | ||
|
|
674e13fb6d | ||
|
|
6fa173124a | ||
|
|
2c69fc3d4d | ||
|
|
317ebcd3b0 | ||
|
|
5cf1f0e2a4 | ||
|
|
6198ed5bd2 | ||
|
|
00ccddbde8 | ||
|
|
63b441401e | ||
|
|
3a7982fe45 | ||
|
|
5430caa172 | ||
|
|
ee1b1e0a93 | ||
|
|
26e46d7cc8 | ||
|
|
a9e76943f6 | ||
|
|
b6a0df8d3c | ||
|
|
5a063b7510 | ||
|
|
ae49bba9be | ||
|
|
51acc58453 | ||
|
|
7c999d7f93 | ||
|
|
ad750d6143 | ||
|
|
fe382df507 | ||
|
|
6501d59efc | ||
|
|
33a782572f | ||
|
|
65803ea12e | ||
|
|
f613379373 | ||
|
|
3553aa26a6 | ||
|
|
257ea7b75d | ||
|
|
d219aa66f4 | ||
|
|
c9ebfad78e | ||
|
|
8e57362a0f | ||
|
|
29630f184e | ||
|
|
6514c680a5 | ||
|
|
3394bf031b | ||
|
|
832a08f579 | ||
|
|
aadce87b16 | ||
|
|
b3207c455d | ||
|
|
769b7391ba | ||
|
|
149b882ebf | ||
|
|
c97e40e2b8 | ||
|
|
22e23bd4dc | ||
|
|
2dde5faeb8 |
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -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
|
||||
|
||||
13
.github/workflows/ci.yml
vendored
13
.github/workflows/ci.yml
vendored
@@ -156,9 +156,8 @@ jobs:
|
||||
- plugin
|
||||
- standalone
|
||||
engine:
|
||||
- 26
|
||||
- 27
|
||||
- 28
|
||||
- 27 # old stable (latest major - 1)
|
||||
- 28 # current stable
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
@@ -183,6 +182,11 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Set up Docker Model
|
||||
run: |
|
||||
sudo apt-get install docker-model-plugin
|
||||
docker model version
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
@@ -190,6 +194,9 @@ jobs:
|
||||
check-latest: true
|
||||
cache: true
|
||||
|
||||
- name: Build example provider
|
||||
run: make example-provider
|
||||
|
||||
- name: Build
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,12 +4,15 @@
|
||||
* Windows:
|
||||
* [Docker Desktop](https://docs.docker.com/desktop/setup/install/windows-install/)
|
||||
* make
|
||||
* go (see [go.mod](go.mod) for minimum version)
|
||||
* macOS:
|
||||
* [Docker Desktop](https://docs.docker.com/desktop/setup/install/mac-install/)
|
||||
* make
|
||||
* go (see [go.mod](go.mod) for minimum version)
|
||||
* Linux:
|
||||
* [Docker 20.10 or later](https://docs.docker.com/engine/install/)
|
||||
* make
|
||||
* go (see [go.mod](go.mod) for minimum version)
|
||||
|
||||
### Building the CLI
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ARG GO_VERSION=1.23.8
|
||||
ARG GO_VERSION=1.24.9
|
||||
ARG XX_VERSION=1.6.1
|
||||
ARG GOLANGCI_LINT_VERSION=v2.0.2
|
||||
ARG ADDLICENSE_VERSION=v1.0.0
|
||||
|
||||
124
MAINTAINERS
124
MAINTAINERS
@@ -1,124 +0,0 @@
|
||||
# Docker maintainers file
|
||||
#
|
||||
# This file describes who runs the docker/compose project and how.
|
||||
# This is a living document - if you see something out of date or missing, speak up!
|
||||
#
|
||||
# It is structured to be consumable by both humans and programs.
|
||||
# To extract its contents programmatically, use any TOML-compliant
|
||||
# parser.
|
||||
#
|
||||
# This file is compiled into the MAINTAINERS file in docker/opensource.
|
||||
#
|
||||
[Org]
|
||||
|
||||
[Org."Core maintainers"]
|
||||
|
||||
# The Core maintainers are the ghostbusters of the project: when there's a problem others
|
||||
# can't solve, they show up and fix it with bizarre devices and weaponry.
|
||||
# They have final say on technical implementation and coding style.
|
||||
# They are ultimately responsible for quality in all its forms: usability polish,
|
||||
# bugfixes, performance, stability, etc. When ownership can cleanly be passed to
|
||||
# a subsystem, they are responsible for doing so and holding the
|
||||
# subsystem maintainers accountable. If ownership is unclear, they are the de facto owners.
|
||||
|
||||
people = [
|
||||
"glours",
|
||||
"jhrotko",
|
||||
"milas",
|
||||
"ndeloof",
|
||||
"nicksieger",
|
||||
"StefanScherer",
|
||||
"ulyssessouza"
|
||||
]
|
||||
|
||||
[Org."Regular maintainers"]
|
||||
# The Regular maintainers are people who aren't Core maintainers but are around
|
||||
# to help reviewing and fixing bugs, just on a less regular basis than previously.
|
||||
# Most of them were previously Core maintainers of Compose.
|
||||
people = [
|
||||
"aiordache",
|
||||
"chris-crone",
|
||||
"gtardif",
|
||||
"laurazard",
|
||||
"maxcleme",
|
||||
"rumpl",
|
||||
"thaJeztah"
|
||||
]
|
||||
|
||||
[people]
|
||||
|
||||
# A reference list of all people associated with the project.
|
||||
# All other sections should refer to people by their canonical key
|
||||
# in the people section.
|
||||
|
||||
# ADD YOURSELF HERE IN ALPHABETICAL ORDER
|
||||
|
||||
[people.aiordache]
|
||||
Name = "Anca Iordache"
|
||||
Email = "anca.iordache@docker.com"
|
||||
GitHub = "aiordache "
|
||||
|
||||
[people.chris-crone]
|
||||
Name = "Christopher Crone"
|
||||
Email = "christopher.crone@docker.com"
|
||||
GitHub = "chris-crone"
|
||||
|
||||
[people.glours]
|
||||
Name = "Guillaume Lours"
|
||||
Email = "guillaume.lours@docker.com"
|
||||
GitHub = "glours"
|
||||
|
||||
[people.gtardif]
|
||||
Name = "Guillaume Tardif"
|
||||
Email = "guillaume.tardif@docker.com"
|
||||
GitHub = "gtardif"
|
||||
|
||||
[people.jhrotko]
|
||||
Name = "Joana Hrotko"
|
||||
Email = "joana.hrotko@docker.com"
|
||||
Github = "jhrotko"
|
||||
|
||||
[people.laurazard]
|
||||
Name = "Laura Brehm"
|
||||
Email = "laura.brehm@docker.com"
|
||||
GitHub = "laurazard"
|
||||
|
||||
[people.maxcleme]
|
||||
Name = "Maxime Clement"
|
||||
Email = "maxime.clement@docker.com"
|
||||
GitHub = "maxcleme"
|
||||
|
||||
[people.milas]
|
||||
Name = "Milas Bowman"
|
||||
Email = "milas.bowman@docker.com"
|
||||
GitHub = "milas"
|
||||
|
||||
[people.nicksieger]
|
||||
Name = "Nick Sieger"
|
||||
Email = "nick.sieger@docker.com"
|
||||
GitHub = "nicksieger"
|
||||
|
||||
[people.ndeloof]
|
||||
Name = "Nicolas Deloof"
|
||||
Email = "nicolas.deloof@docker.com"
|
||||
GitHub = "ndeloof"
|
||||
|
||||
[people.rumpl]
|
||||
Name = "Djordje Lukic"
|
||||
Email = "djordje.lukic@docker.com"
|
||||
GitHub = "rumpl"
|
||||
|
||||
[people.thaJeztah]
|
||||
Name = "Sebastiaan van Stijn"
|
||||
Email = "sebastiaan.vanstijn@docker.com"
|
||||
GitHub = "thaJeztah "
|
||||
|
||||
[people.StefanScherer]
|
||||
Name = "Stefan Scherer"
|
||||
Email = "stefan.scherer@docker.com"
|
||||
GitHub = "StefanScherer"
|
||||
|
||||
[people.ulyssessouza]
|
||||
Name = "Ulysses Souza"
|
||||
Email = "<ulysses.souza@docker.com"
|
||||
Github = "ulyssessouza"
|
||||
6
Makefile
6
Makefile
@@ -74,7 +74,7 @@ install: binary
|
||||
install $(or $(DESTDIR),./bin/build)/docker-compose ~/.docker/cli-plugins/docker-compose
|
||||
|
||||
.PHONY: e2e-compose
|
||||
e2e-compose: ## Run end to end local tests in plugin mode. Set E2E_TEST=TestName to run a single test
|
||||
e2e-compose: example-provider ## Run end to end local tests in plugin mode. Set E2E_TEST=TestName to run a single test
|
||||
go run gotest.tools/gotestsum@latest --format testname --junitfile "/tmp/report/report.xml" -- -v $(TEST_FLAGS) -count=1 ./pkg/e2e
|
||||
|
||||
.PHONY: e2e-compose-standalone
|
||||
@@ -87,6 +87,10 @@ build-and-e2e-compose: build e2e-compose ## Compile the compose cli-plugin and r
|
||||
.PHONY: build-and-e2e-compose-standalone
|
||||
build-and-e2e-compose-standalone: build e2e-compose-standalone ## Compile the compose cli-plugin and run End to end local tests in standalone mode. Set E2E_TEST=TestName to run a single test
|
||||
|
||||
.PHONY: example-provider
|
||||
example-provider: ## build example provider for e2e tests
|
||||
go build -o bin/build/example-provider docs/examples/provider.go
|
||||
|
||||
.PHONY: mocks
|
||||
mocks:
|
||||
mockgen --version >/dev/null 2>&1 || go install go.uber.org/mock/mockgen@v0.4.0
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
)
|
||||
|
||||
// alphaCommand groups all experimental subcommands
|
||||
func alphaCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func alphaCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Short: "Experimental commands",
|
||||
Use: "alpha [COMMAND]",
|
||||
|
||||
@@ -35,7 +35,7 @@ type attachOpts struct {
|
||||
proxy bool
|
||||
}
|
||||
|
||||
func attachCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func attachCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := attachOpts{
|
||||
composeOptions: &composeOptions{
|
||||
ProjectOptions: p,
|
||||
@@ -63,7 +63,7 @@ func attachCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service
|
||||
return runCmd
|
||||
}
|
||||
|
||||
func runAttach(ctx context.Context, dockerCli command.Cli, backend api.Service, opts attachOpts) error {
|
||||
func runAttach(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts attachOpts) error {
|
||||
projectName, err := opts.toProjectName(ctx, dockerCli)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -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,11 +85,12 @@ func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions,
|
||||
Check: opts.check,
|
||||
SSHs: SSHKeys,
|
||||
Builder: builderName,
|
||||
SBOM: opts.sbom,
|
||||
Provenance: opts.provenance,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func buildCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func buildCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := buildOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -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
|
||||
@@ -144,7 +148,8 @@ func buildCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Service, opts buildOptions, services []string) error {
|
||||
func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts buildOptions, services []string) error {
|
||||
opts.All = true // do not drop resources as build may involve some dependencies by additional_contexts
|
||||
project, _, err := opts.ToProject(ctx, dockerCli, nil, cli.WithResolvedPaths(true), cli.WithoutEnvironmentResolution)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -155,10 +160,10 @@ func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Service, o
|
||||
}
|
||||
|
||||
apiBuildOptions, err := opts.toAPIBuildOptions(services)
|
||||
apiBuildOptions.Provenance = true
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
apiBuildOptions.Attestations = true
|
||||
|
||||
return backend.Build(ctx, project, apiBuildOptions)
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ type commitOptions struct {
|
||||
index int
|
||||
}
|
||||
|
||||
func commitCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func commitCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
options := commitOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -73,13 +73,13 @@ func commitCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCommit(ctx context.Context, dockerCli command.Cli, backend api.Service, options commitOptions) error {
|
||||
func runCommit(ctx context.Context, dockerCli command.Cli, backend api.Compose, options commitOptions) error {
|
||||
projectName, err := options.toProjectName(ctx, dockerCli)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
commitOptions := api.CommitOptions{
|
||||
return backend.Commit(ctx, projectName, api.CommitOptions{
|
||||
Service: options.service,
|
||||
Reference: options.reference,
|
||||
Pause: options.pause,
|
||||
@@ -87,7 +87,5 @@ func runCommit(ctx context.Context, dockerCli command.Cli, backend api.Service,
|
||||
Author: options.author,
|
||||
Changes: options.changes,
|
||||
Index: options.index,
|
||||
}
|
||||
|
||||
return backend.Commit(ctx, projectName, commitOptions)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ func completeServiceNames(dockerCli command.Cli, p *ProjectOptions) validArgsFn
|
||||
}
|
||||
}
|
||||
|
||||
func completeProjectNames(backend api.Service) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
func completeProjectNames(backend api.Compose) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
list, err := backend.List(cmd.Context(), api.ListOptions{
|
||||
All: true,
|
||||
|
||||
@@ -36,12 +36,10 @@ import (
|
||||
composegoutils "github.com/compose-spec/compose-go/v2/utils"
|
||||
"github.com/docker/buildx/util/logutil"
|
||||
dockercli "github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/pkg/kvfile"
|
||||
"github.com/docker/compose/v2/cmd/formatter"
|
||||
"github.com/docker/compose/v2/internal/desktop"
|
||||
"github.com/docker/compose/v2/internal/experimental"
|
||||
"github.com/docker/compose/v2/internal/tracing"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
ui "github.com/docker/compose/v2/pkg/progress"
|
||||
@@ -91,14 +89,6 @@ func init() {
|
||||
dotenv.RegisterFormat("raw", rawEnv)
|
||||
}
|
||||
|
||||
type Backend interface {
|
||||
api.Service
|
||||
|
||||
SetDesktopClient(cli *desktop.Client)
|
||||
|
||||
SetExperiments(experiments *experimental.State)
|
||||
}
|
||||
|
||||
// Command defines a compose CLI command as a func with args
|
||||
type Command func(context.Context, []string) error
|
||||
|
||||
@@ -245,7 +235,7 @@ func (o *ProjectOptions) projectOrName(ctx context.Context, dockerCli command.Cl
|
||||
name := o.ProjectName
|
||||
var project *types.Project
|
||||
if len(o.ConfigPaths) > 0 || o.ProjectName == "" {
|
||||
p, _, err := o.ToProject(ctx, dockerCli, services, cli.WithDiscardEnvFile)
|
||||
p, _, err := o.ToProject(ctx, dockerCli, services, cli.WithDiscardEnvFile, cli.WithoutEnvironmentResolution)
|
||||
if err != nil {
|
||||
envProjectName := os.Getenv(ComposeProjectName)
|
||||
if envProjectName != "" {
|
||||
@@ -383,32 +373,38 @@ func (o *ProjectOptions) remoteLoaders(dockerCli command.Cli) []loader.ResourceL
|
||||
}
|
||||
|
||||
func (o *ProjectOptions) toProjectOptions(po ...cli.ProjectOptionsFn) (*cli.ProjectOptions, error) {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
opts := []cli.ProjectOptionsFn{
|
||||
cli.WithWorkingDirectory(o.ProjectDir),
|
||||
// First apply os.Environment, always win
|
||||
cli.WithOsEnv,
|
||||
}
|
||||
|
||||
return cli.NewProjectOptions(o.ConfigPaths,
|
||||
append(po,
|
||||
cli.WithWorkingDirectory(o.ProjectDir),
|
||||
// First apply os.Environment, always win
|
||||
cli.WithOsEnv,
|
||||
// set PWD as this variable is not consistently supported on Windows
|
||||
cli.WithEnv([]string{"PWD=" + pwd}),
|
||||
// Load PWD/.env if present and no explicit --env-file has been set
|
||||
cli.WithEnvFiles(o.EnvFiles...),
|
||||
// read dot env file to populate project environment
|
||||
cli.WithDotEnv,
|
||||
// get compose file path set by COMPOSE_FILE
|
||||
cli.WithConfigFileEnv,
|
||||
// if none was selected, get default compose.yaml file from current dir or parent folder
|
||||
cli.WithDefaultConfigPath,
|
||||
// .. and then, a project directory != PWD maybe has been set so let's load .env file
|
||||
cli.WithEnvFiles(o.EnvFiles...),
|
||||
cli.WithDotEnv,
|
||||
// eventually COMPOSE_PROFILES should have been set
|
||||
cli.WithDefaultProfiles(o.Profiles...),
|
||||
cli.WithName(o.ProjectName))...)
|
||||
if _, present := os.LookupEnv("PWD"); !present {
|
||||
if pwd, err := os.Getwd(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
opts = append(opts, cli.WithEnv([]string{"PWD=" + pwd}))
|
||||
}
|
||||
}
|
||||
|
||||
opts = append(opts,
|
||||
// Load PWD/.env if present and no explicit --env-file has been set
|
||||
cli.WithEnvFiles(o.EnvFiles...),
|
||||
// read dot env file to populate project environment
|
||||
cli.WithDotEnv,
|
||||
// get compose file path set by COMPOSE_FILE
|
||||
cli.WithConfigFileEnv,
|
||||
// if none was selected, get default compose.yaml file from current dir or parent folder
|
||||
cli.WithDefaultConfigPath,
|
||||
// .. and then, a project directory != PWD maybe has been set so let's load .env file
|
||||
cli.WithEnvFiles(o.EnvFiles...),
|
||||
cli.WithDotEnv,
|
||||
// eventually COMPOSE_PROFILES should have been set
|
||||
cli.WithDefaultProfiles(o.Profiles...),
|
||||
cli.WithName(o.ProjectName),
|
||||
)
|
||||
|
||||
return cli.NewProjectOptions(o.ConfigPaths, append(po, opts...)...)
|
||||
}
|
||||
|
||||
// PluginName is the name of the plugin
|
||||
@@ -416,11 +412,11 @@ const PluginName = "compose"
|
||||
|
||||
// RunningAsStandalone detects when running as a standalone program
|
||||
func RunningAsStandalone() bool {
|
||||
return len(os.Args) < 2 || os.Args[1] != manager.MetadataSubcommandName && os.Args[1] != PluginName
|
||||
return len(os.Args) < 2 || os.Args[1] != metadata.MetadataSubcommandName && os.Args[1] != PluginName
|
||||
}
|
||||
|
||||
// RootCommand returns the compose command with its child commands
|
||||
func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //nolint:gocyclo
|
||||
func RootCommand(dockerCli command.Cli, backend api.Compose) *cobra.Command { //nolint:gocyclo
|
||||
// filter out useless commandConn.CloseWrite warning message that can occur
|
||||
// when using a remote context that is unreachable: "commandConn.CloseWrite: commandconn: failed to wait: signal: killed"
|
||||
// https://github.com/docker/cli/blob/e1f24d3c93df6752d3c27c8d61d18260f141310c/cli/connhelper/commandconn/commandconn.go#L203-L215
|
||||
@@ -431,7 +427,6 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
|
||||
"commandConn.CloseRead:",
|
||||
))
|
||||
|
||||
experiments := experimental.NewState()
|
||||
opts := ProjectOptions{}
|
||||
var (
|
||||
ansi string
|
||||
@@ -575,27 +570,6 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
|
||||
}
|
||||
cmd.SetContext(ctx)
|
||||
|
||||
// (6) Desktop integration
|
||||
var desktopCli *desktop.Client
|
||||
if !dryRun {
|
||||
if desktopCli, err = desktop.NewFromDockerClient(ctx, dockerCli); desktopCli != nil {
|
||||
logrus.Debugf("Enabled Docker Desktop integration (experimental) @ %s", desktopCli.Endpoint())
|
||||
backend.SetDesktopClient(desktopCli)
|
||||
} else if err != nil {
|
||||
// not fatal, Compose will still work but behave as though
|
||||
// it's not running as part of Docker Desktop
|
||||
logrus.Debugf("failed to enable Docker Desktop integration: %v", err)
|
||||
} else {
|
||||
logrus.Trace("Docker Desktop integration not enabled")
|
||||
}
|
||||
}
|
||||
|
||||
// (7) experimental features
|
||||
if err := experiments.Load(ctx, desktopCli); err != nil {
|
||||
logrus.Debugf("Failed to query feature flags from Desktop: %v", err)
|
||||
}
|
||||
backend.SetExperiments(experiments)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -636,6 +610,7 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
|
||||
publishCommand(&opts, dockerCli, backend),
|
||||
alphaCommand(&opts, dockerCli, backend),
|
||||
bridgeCommand(&opts, dockerCli),
|
||||
volumesCommand(&opts, dockerCli, backend),
|
||||
)
|
||||
|
||||
c.Flags().SetInterspersed(false)
|
||||
@@ -660,6 +635,10 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
|
||||
"profile",
|
||||
completeProfileNames(dockerCli, &opts),
|
||||
)
|
||||
c.RegisterFlagCompletionFunc( //nolint:errcheck
|
||||
"progress",
|
||||
cobra.FixedCompletions(printerModes, cobra.ShellCompDirectiveNoFileComp),
|
||||
)
|
||||
|
||||
c.Flags().StringVar(&ansi, "ansi", "auto", `Control when to print ANSI control characters ("never"|"always"|"auto")`)
|
||||
c.Flags().IntVar(¶llel, "parallel", -1, `Control max parallelism, -1 for unlimited`)
|
||||
@@ -688,7 +667,7 @@ func setEnvWithDotEnv(opts ProjectOptions) error {
|
||||
return nil
|
||||
}
|
||||
for k, v := range envFromFile {
|
||||
if _, ok := os.LookupEnv(k); !ok {
|
||||
if _, ok := os.LookupEnv(k); !ok && strings.HasPrefix(k, "COMPOSE_") {
|
||||
if err = os.Setenv(k, v); err != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -704,15 +683,3 @@ var printerModes = []string{
|
||||
ui.ModeJSON,
|
||||
ui.ModeQuiet,
|
||||
}
|
||||
|
||||
func SetUnchangedOption(name string, experimentalFlag bool) bool {
|
||||
var value bool
|
||||
// If the var is defined we use that value first
|
||||
if envVar, ok := os.LookupEnv(name); ok {
|
||||
value = utils.StringToBool(envVar)
|
||||
} else {
|
||||
// if not, we try to get it from experimental feature flag
|
||||
value = experimentalFlag
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ type configOptions struct {
|
||||
noResolveEnv bool
|
||||
services bool
|
||||
volumes bool
|
||||
networks bool
|
||||
models bool
|
||||
profiles bool
|
||||
images bool
|
||||
hash string
|
||||
@@ -111,6 +113,12 @@ func configCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
|
||||
if opts.volumes {
|
||||
return runVolumes(ctx, dockerCli, opts)
|
||||
}
|
||||
if opts.networks {
|
||||
return runNetworks(ctx, dockerCli, opts)
|
||||
}
|
||||
if opts.models {
|
||||
return runModels(ctx, dockerCli, opts)
|
||||
}
|
||||
if opts.hash != "" {
|
||||
return runHash(ctx, dockerCli, opts)
|
||||
}
|
||||
@@ -147,6 +155,8 @@ func configCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
|
||||
|
||||
flags.BoolVar(&opts.services, "services", false, "Print the service names, one per line.")
|
||||
flags.BoolVar(&opts.volumes, "volumes", false, "Print the volume names, one per line.")
|
||||
flags.BoolVar(&opts.networks, "networks", false, "Print the network names, one per line.")
|
||||
flags.BoolVar(&opts.models, "models", false, "Print the model names, one per line.")
|
||||
flags.BoolVar(&opts.profiles, "profiles", false, "Print the profile names, one per line.")
|
||||
flags.BoolVar(&opts.images, "images", false, "Print the image names, one per line.")
|
||||
flags.StringVar(&opts.hash, "hash", "", "Print the service config hash, one per line.")
|
||||
@@ -367,6 +377,30 @@ func runVolumes(ctx context.Context, dockerCli command.Cli, opts configOptions)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runNetworks(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
|
||||
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for n := range project.Networks {
|
||||
_, _ = fmt.Fprintln(dockerCli.Out(), n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runModels(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
|
||||
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, model := range project.Models {
|
||||
if model.Model != "" {
|
||||
_, _ = fmt.Fprintln(dockerCli.Out(), model.Model)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runHash(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
|
||||
var services []string
|
||||
if opts.hash != "*" {
|
||||
|
||||
@@ -38,7 +38,7 @@ type copyOptions struct {
|
||||
copyUIDGID bool
|
||||
}
|
||||
|
||||
func copyCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func copyCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := copyOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -73,7 +73,7 @@ func copyCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return copyCmd
|
||||
}
|
||||
|
||||
func runCopy(ctx context.Context, dockerCli command.Cli, backend api.Service, opts copyOptions) error {
|
||||
func runCopy(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts copyOptions) error {
|
||||
name, err := opts.toProjectName(ctx, dockerCli)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -51,7 +51,7 @@ type createOptions struct {
|
||||
AssumeYes bool
|
||||
}
|
||||
|
||||
func createCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func createCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := createOptions{}
|
||||
buildOpts := buildOptions{
|
||||
ProjectOptions: p,
|
||||
@@ -95,7 +95,7 @@ func createCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCreate(ctx context.Context, _ command.Cli, backend api.Service, createOpts createOptions, buildOpts buildOptions, project *types.Project, services []string) error {
|
||||
func runCreate(ctx context.Context, _ command.Cli, backend api.Compose, createOpts createOptions, buildOpts buildOptions, project *types.Project, services []string) error {
|
||||
if err := createOpts.Apply(project); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ type downOptions struct {
|
||||
images string
|
||||
}
|
||||
|
||||
func downCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func downCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := downOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -77,7 +77,7 @@ func downCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return downCmd
|
||||
}
|
||||
|
||||
func runDown(ctx context.Context, dockerCli command.Cli, backend api.Service, opts downOptions, services []string) error {
|
||||
func runDown(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts downOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(ctx, dockerCli, services...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -29,10 +29,12 @@ import (
|
||||
|
||||
type eventsOpts struct {
|
||||
*composeOptions
|
||||
json bool
|
||||
json bool
|
||||
since string
|
||||
until string
|
||||
}
|
||||
|
||||
func eventsCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func eventsCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := eventsOpts{
|
||||
composeOptions: &composeOptions{
|
||||
ProjectOptions: p,
|
||||
@@ -48,10 +50,12 @@ func eventsCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&opts.json, "json", false, "Output events as a stream of json objects")
|
||||
cmd.Flags().StringVar(&opts.since, "since", "", "Show all events created since timestamp")
|
||||
cmd.Flags().StringVar(&opts.until, "until", "", "Stream events until this timestamp")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runEvents(ctx context.Context, dockerCli command.Cli, backend api.Service, opts eventsOpts, services []string) error {
|
||||
func runEvents(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts eventsOpts, services []string) error {
|
||||
name, err := opts.toProjectName(ctx, dockerCli)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -59,6 +63,8 @@ func runEvents(ctx context.Context, dockerCli command.Cli, backend api.Service,
|
||||
|
||||
return backend.Events(ctx, name, api.EventsOptions{
|
||||
Services: services,
|
||||
Since: opts.since,
|
||||
Until: opts.until,
|
||||
Consumer: func(event api.Event) error {
|
||||
if opts.json {
|
||||
marshal, err := json.Marshal(map[string]interface{}{
|
||||
|
||||
@@ -18,13 +18,18 @@ package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/compose"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
type execOpts struct {
|
||||
@@ -43,7 +48,7 @@ type execOpts struct {
|
||||
interactive bool
|
||||
}
|
||||
|
||||
func execCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func execCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := execOpts{
|
||||
composeOptions: &composeOptions{
|
||||
ProjectOptions: p,
|
||||
@@ -59,7 +64,15 @@ func execCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return nil
|
||||
}),
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runExec(ctx, dockerCli, backend, opts)
|
||||
err := runExec(ctx, dockerCli, backend, opts)
|
||||
if err != nil {
|
||||
logrus.Debugf("%v", err)
|
||||
var cliError cli.StatusError
|
||||
if ok := errors.As(err, &cliError); ok {
|
||||
os.Exit(err.(cli.StatusError).StatusCode) //nolint: errorlint
|
||||
}
|
||||
}
|
||||
return err
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
}
|
||||
@@ -69,7 +82,7 @@ func execCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
runCmd.Flags().IntVar(&opts.index, "index", 0, "Index of the container if service has multiple replicas")
|
||||
runCmd.Flags().BoolVarP(&opts.privileged, "privileged", "", false, "Give extended privileges to the process")
|
||||
runCmd.Flags().StringVarP(&opts.user, "user", "u", "", "Run the command as this user")
|
||||
runCmd.Flags().BoolVarP(&opts.noTty, "no-TTY", "T", !dockerCli.Out().IsTerminal(), "Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY.")
|
||||
runCmd.Flags().BoolVarP(&opts.noTty, "no-tty", "T", !dockerCli.Out().IsTerminal(), "Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY.")
|
||||
runCmd.Flags().StringVarP(&opts.workingDir, "workdir", "w", "", "Path to workdir directory for this command")
|
||||
|
||||
runCmd.Flags().BoolVarP(&opts.interactive, "interactive", "i", true, "Keep STDIN open even if not attached")
|
||||
@@ -78,10 +91,16 @@ func execCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
runCmd.Flags().MarkHidden("tty") //nolint:errcheck
|
||||
|
||||
runCmd.Flags().SetInterspersed(false)
|
||||
runCmd.Flags().SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||
if name == "no-TTY" { // legacy
|
||||
name = "no-tty"
|
||||
}
|
||||
return pflag.NormalizedName(name)
|
||||
})
|
||||
return runCmd
|
||||
}
|
||||
|
||||
func runExec(ctx context.Context, dockerCli command.Cli, backend api.Service, opts execOpts) error {
|
||||
func runExec(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts execOpts) error {
|
||||
projectName, err := opts.toProjectName(ctx, dockerCli)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -109,8 +128,8 @@ func runExec(ctx context.Context, dockerCli command.Cli, backend api.Service, op
|
||||
|
||||
exitCode, err := backend.Exec(ctx, projectName, execOpts)
|
||||
if exitCode != 0 {
|
||||
errMsg := ""
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("exit status %d", exitCode)
|
||||
if err != nil && err.Error() != "" {
|
||||
errMsg = err.Error()
|
||||
}
|
||||
return cli.StatusError{StatusCode: exitCode, Status: errMsg}
|
||||
|
||||
@@ -33,7 +33,7 @@ type exportOptions struct {
|
||||
index int
|
||||
}
|
||||
|
||||
func exportCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func exportCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
options := exportOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -58,7 +58,7 @@ func exportCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runExport(ctx context.Context, dockerCli command.Cli, backend api.Service, options exportOptions) error {
|
||||
func runExport(ctx context.Context, dockerCli command.Cli, backend api.Compose, options exportOptions) error {
|
||||
projectName, err := options.toProjectName(ctx, dockerCli)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -30,7 +30,7 @@ type generateOptions struct {
|
||||
Format string
|
||||
}
|
||||
|
||||
func generateCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
func generateCommand(p *ProjectOptions, backend api.Compose) *cobra.Command {
|
||||
opts := generateOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -52,7 +52,7 @@ func generateCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runGenerate(ctx context.Context, backend api.Service, opts generateOptions, containers []string) error {
|
||||
func runGenerate(ctx context.Context, backend api.Compose, opts generateOptions, containers []string) error {
|
||||
_, _ = fmt.Fprintln(os.Stderr, "generate command is EXPERIMENTAL")
|
||||
if len(containers) == 0 {
|
||||
return fmt.Errorf("at least one container must be specified")
|
||||
|
||||
@@ -41,7 +41,7 @@ type imageOptions struct {
|
||||
Format string
|
||||
}
|
||||
|
||||
func imagesCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func imagesCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := imageOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -58,7 +58,7 @@ func imagesCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service
|
||||
return imgCmd
|
||||
}
|
||||
|
||||
func runImages(ctx context.Context, dockerCli command.Cli, backend api.Service, opts imageOptions, services []string) error {
|
||||
func runImages(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts imageOptions, services []string) error {
|
||||
projectName, err := opts.toProjectName(ctx, dockerCli)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -101,6 +101,10 @@ func runImages(ctx context.Context, dockerCli command.Cli, backend api.Service,
|
||||
// Convert map to slice
|
||||
var imageList []img
|
||||
for ctr, i := range images {
|
||||
lastTagTime := i.LastTagTime
|
||||
if lastTagTime.IsZero() {
|
||||
lastTagTime = i.Created
|
||||
}
|
||||
imageList = append(imageList, img{
|
||||
ContainerName: ctr,
|
||||
ID: i.ID,
|
||||
@@ -108,7 +112,7 @@ func runImages(ctx context.Context, dockerCli command.Cli, backend api.Service,
|
||||
Tag: i.Tag,
|
||||
Platform: platforms.Format(i.Platform),
|
||||
Size: i.Size,
|
||||
LastTagTime: i.LastTagTime,
|
||||
LastTagTime: lastTagTime,
|
||||
})
|
||||
}
|
||||
json, err := formatter.ToJSON(imageList, "", "")
|
||||
|
||||
@@ -33,7 +33,7 @@ type killOptions struct {
|
||||
signal string
|
||||
}
|
||||
|
||||
func killCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func killCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := killOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -54,7 +54,7 @@ func killCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runKill(ctx context.Context, dockerCli command.Cli, backend api.Service, opts killOptions, services []string) error {
|
||||
func runKill(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts killOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(ctx, dockerCli, services...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -38,7 +38,7 @@ type lsOptions struct {
|
||||
Filter opts.FilterOpt
|
||||
}
|
||||
|
||||
func listCommand(dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func listCommand(dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
lsOpts := lsOptions{Filter: opts.NewFilterOpt()}
|
||||
lsCmd := &cobra.Command{
|
||||
Use: "ls [OPTIONS]",
|
||||
@@ -61,7 +61,7 @@ var acceptedListFilters = map[string]bool{
|
||||
"name": true,
|
||||
}
|
||||
|
||||
func runList(ctx context.Context, dockerCli command.Cli, backend api.Service, lsOpts lsOptions) error {
|
||||
func runList(ctx context.Context, dockerCli command.Cli, backend api.Compose, lsOpts lsOptions) error {
|
||||
filters := lsOpts.Filter.Value()
|
||||
err := filters.Validate(acceptedListFilters)
|
||||
if err != nil {
|
||||
|
||||
@@ -40,7 +40,7 @@ type logsOptions struct {
|
||||
timestamps bool
|
||||
}
|
||||
|
||||
func logsCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func logsCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := logsOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -70,7 +70,7 @@ func logsCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return logsCmd
|
||||
}
|
||||
|
||||
func runLogs(ctx context.Context, dockerCli command.Cli, backend api.Service, opts logsOptions, services []string) error {
|
||||
func runLogs(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts logsOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(ctx, dockerCli, services...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -30,9 +30,9 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/template"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/cmd/prompt"
|
||||
"github.com/docker/compose/v2/internal/tracing"
|
||||
ui "github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/compose/v2/pkg/prompt"
|
||||
)
|
||||
|
||||
func applyPlatforms(project *types.Project, buildForSinglePlatform bool) error {
|
||||
|
||||
@@ -29,7 +29,7 @@ type pauseOptions struct {
|
||||
*ProjectOptions
|
||||
}
|
||||
|
||||
func pauseCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func pauseCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := pauseOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func pauseCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runPause(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pauseOptions, services []string) error {
|
||||
func runPause(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts pauseOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(ctx, dockerCli, services...)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -60,7 +60,7 @@ type unpauseOptions struct {
|
||||
*ProjectOptions
|
||||
}
|
||||
|
||||
func unpauseCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func unpauseCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := unpauseOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -75,7 +75,7 @@ func unpauseCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Servic
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runUnPause(ctx context.Context, dockerCli command.Cli, backend api.Service, opts unpauseOptions, services []string) error {
|
||||
func runUnPause(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts unpauseOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(ctx, dockerCli, services...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -35,7 +35,7 @@ type portOptions struct {
|
||||
index int
|
||||
}
|
||||
|
||||
func portCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func portCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := portOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func portCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runPort(ctx context.Context, dockerCli command.Cli, backend api.Service, opts portOptions, service string) error {
|
||||
func runPort(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts portOptions, service string) error {
|
||||
projectName, err := opts.toProjectName(ctx, dockerCli)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -64,7 +64,7 @@ func (p *psOptions) parseFilter() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func psCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func psCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := psOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -91,7 +91,7 @@ func psCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *c
|
||||
return psCmd
|
||||
}
|
||||
|
||||
func runPs(ctx context.Context, dockerCli command.Cli, backend api.Service, services []string, opts psOptions) error {
|
||||
func runPs(ctx context.Context, dockerCli command.Cli, backend api.Compose, services []string, opts psOptions) error {
|
||||
project, name, err := opts.projectOrName(ctx, dockerCli, services...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -34,9 +34,10 @@ type publishOptions struct {
|
||||
ociVersion string
|
||||
withEnvironment bool
|
||||
assumeYes bool
|
||||
app bool
|
||||
}
|
||||
|
||||
func publishCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func publishCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := publishOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -53,6 +54,7 @@ func publishCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Servic
|
||||
flags.StringVar(&opts.ociVersion, "oci-version", "", "OCI image/artifact specification version (automatically determined by default)")
|
||||
flags.BoolVar(&opts.withEnvironment, "with-env", false, "Include environment variables in the published OCI artifact")
|
||||
flags.BoolVarP(&opts.assumeYes, "yes", "y", false, `Assume "yes" as answer to all prompts`)
|
||||
flags.BoolVar(&opts.app, "app", false, "Published compose application (includes referenced images)")
|
||||
flags.SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||
// assumeYes was introduced by mistake as `--y`
|
||||
if name == "y" {
|
||||
@@ -65,7 +67,7 @@ func publishCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Servic
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runPublish(ctx context.Context, dockerCli command.Cli, backend api.Service, opts publishOptions, repository string) error {
|
||||
func runPublish(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts publishOptions, repository string) error {
|
||||
project, metrics, err := opts.ToProject(ctx, dockerCli, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -76,7 +78,8 @@ func runPublish(ctx context.Context, dockerCli command.Cli, backend api.Service,
|
||||
}
|
||||
|
||||
return backend.Publish(ctx, project, repository, api.PublishOptions{
|
||||
ResolveImageDigests: opts.resolveImageDigests,
|
||||
ResolveImageDigests: opts.resolveImageDigests || opts.app,
|
||||
Application: opts.app,
|
||||
OCIVersion: api.OCIVersion(opts.ociVersion),
|
||||
WithEnvironment: opts.withEnvironment,
|
||||
AssumeYes: opts.assumeYes,
|
||||
|
||||
@@ -42,7 +42,7 @@ type pullOptions struct {
|
||||
policy string
|
||||
}
|
||||
|
||||
func pullCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func pullCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := pullOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -97,7 +97,7 @@ func (opts pullOptions) apply(project *types.Project, services []string) (*types
|
||||
return project, nil
|
||||
}
|
||||
|
||||
func runPull(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pullOptions, services []string) error {
|
||||
func runPull(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts pullOptions, services []string) error {
|
||||
project, _, err := opts.ToProject(ctx, dockerCli, services, cli.WithoutEnvironmentResolution)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -34,7 +34,7 @@ type pushOptions struct {
|
||||
Quiet bool
|
||||
}
|
||||
|
||||
func pushCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func pushCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := pushOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -53,7 +53,7 @@ func pushCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return pushCmd
|
||||
}
|
||||
|
||||
func runPush(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pushOptions, services []string) error {
|
||||
func runPush(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts pushOptions, services []string) error {
|
||||
project, _, err := opts.ToProject(ctx, dockerCli, services)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -31,7 +31,7 @@ type removeOptions struct {
|
||||
volumes bool
|
||||
}
|
||||
|
||||
func removeCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func removeCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := removeOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -59,7 +59,7 @@ Any data which is not in a volume will be lost.`,
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRemove(ctx context.Context, dockerCli command.Cli, backend api.Service, opts removeOptions, services []string) error {
|
||||
func runRemove(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts removeOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(ctx, dockerCli, services...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -33,7 +33,7 @@ type restartOptions struct {
|
||||
noDeps bool
|
||||
}
|
||||
|
||||
func restartCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func restartCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := restartOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -55,7 +55,7 @@ func restartCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Servic
|
||||
return restartCmd
|
||||
}
|
||||
|
||||
func runRestart(ctx context.Context, dockerCli command.Cli, backend api.Service, opts restartOptions, services []string) error {
|
||||
func runRestart(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts restartOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(ctx, dockerCli)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -50,7 +50,6 @@ type runOptions struct {
|
||||
Detach bool
|
||||
Remove bool
|
||||
noTty bool
|
||||
tty bool
|
||||
interactive bool
|
||||
user string
|
||||
workdir string
|
||||
@@ -120,8 +119,8 @@ func (options runOptions) apply(project *types.Project) (*types.Project, error)
|
||||
return project, nil
|
||||
}
|
||||
|
||||
func (options runOptions) getEnvironment() (types.Mapping, error) {
|
||||
environment := types.NewMappingWithEquals(options.environment).Resolve(os.LookupEnv).ToMapping()
|
||||
func (options runOptions) getEnvironment(resolve func(string) (string, bool)) (types.Mapping, error) {
|
||||
environment := types.NewMappingWithEquals(options.environment).Resolve(resolve).ToMapping()
|
||||
for _, file := range options.envFiles {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
@@ -143,7 +142,7 @@ func (options runOptions) getEnvironment() (types.Mapping, error) {
|
||||
return environment, nil
|
||||
}
|
||||
|
||||
func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
options := runOptions{
|
||||
composeOptions: &composeOptions{
|
||||
ProjectOptions: p,
|
||||
@@ -155,6 +154,10 @@ func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
|
||||
buildOpts := buildOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
// We remove the attribute from the option struct and use a dedicated var, to limit confusion and avoid anyone to use options.tty.
|
||||
// The tty flag is here for convenience and let user do "docker compose run -it" the same way as they use the "docker run" command.
|
||||
var ttyFlag bool
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "run [OPTIONS] SERVICE [COMMAND] [ARGS...]",
|
||||
Short: "Run a one-off command on a service",
|
||||
@@ -178,9 +181,16 @@ func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
|
||||
if cmd.Flags().Changed("no-TTY") {
|
||||
return fmt.Errorf("--tty and --no-TTY can't be used together")
|
||||
} else {
|
||||
options.noTty = !options.tty
|
||||
options.noTty = !ttyFlag
|
||||
}
|
||||
} else if !cmd.Flags().Changed("no-TTY") && !cmd.Flags().Changed("interactive") && !dockerCli.In().IsTerminal() {
|
||||
// while `docker run` requires explicit `-it` flags, Compose enables interactive mode and TTY by default
|
||||
// but when compose is used from a scripr has stdin piped from another command, we just can't
|
||||
// Here, we detect we run "by default" (user didn't passed explicit flags) and disable TTY allocation if
|
||||
// we don't have an actual terminal to attach to for interactive mode
|
||||
options.noTty = true
|
||||
}
|
||||
|
||||
if options.quiet {
|
||||
progress.Mode = progress.ModeQuiet
|
||||
devnull, err := os.Open(os.DevNull)
|
||||
@@ -238,7 +248,7 @@ func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
|
||||
flags.BoolVar(&options.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file")
|
||||
|
||||
cmd.Flags().BoolVarP(&options.interactive, "interactive", "i", true, "Keep STDIN open even if not attached")
|
||||
cmd.Flags().BoolVarP(&options.tty, "tty", "t", true, "Allocate a pseudo-TTY")
|
||||
cmd.Flags().BoolVarP(&ttyFlag, "tty", "t", true, "Allocate a pseudo-TTY")
|
||||
cmd.Flags().MarkHidden("tty") //nolint:errcheck
|
||||
|
||||
flags.SetNormalizeFunc(normalizeRunFlags)
|
||||
@@ -256,7 +266,7 @@ func normalizeRunFlags(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||
return pflag.NormalizedName(name)
|
||||
}
|
||||
|
||||
func runRun(ctx context.Context, backend api.Service, project *types.Project, options runOptions, createOpts createOptions, buildOpts buildOptions, dockerCli command.Cli) error {
|
||||
func runRun(ctx context.Context, backend api.Compose, project *types.Project, options runOptions, createOpts createOptions, buildOpts buildOptions, dockerCli command.Cli) error {
|
||||
project, err := options.apply(project)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -282,14 +292,14 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
|
||||
|
||||
var buildForRun *api.BuildOptions
|
||||
if !createOpts.noBuild {
|
||||
bo, err := buildOpts.toAPIBuildOptions(project.ServiceNames())
|
||||
bo, err := buildOpts.toAPIBuildOptions(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buildForRun = &bo
|
||||
}
|
||||
|
||||
environment, err := options.getEnvironment()
|
||||
environment, err := options.getEnvironment(project.Environment.Resolve)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ type scaleOptions struct {
|
||||
noDeps bool
|
||||
}
|
||||
|
||||
func scaleCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func scaleCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := scaleOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -58,7 +58,7 @@ func scaleCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return scaleCmd
|
||||
}
|
||||
|
||||
func runScale(ctx context.Context, dockerCli command.Cli, backend api.Service, opts scaleOptions, serviceReplicaTuples map[string]int) error {
|
||||
func runScale(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts scaleOptions, serviceReplicaTuples map[string]int) error {
|
||||
services := slices.Sorted(maps.Keys(serviceReplicaTuples))
|
||||
project, _, err := opts.ToProject(ctx, dockerCli, services)
|
||||
if err != nil {
|
||||
|
||||
@@ -28,7 +28,7 @@ type startOptions struct {
|
||||
*ProjectOptions
|
||||
}
|
||||
|
||||
func startCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func startCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := startOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -43,7 +43,7 @@ func startCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return startCmd
|
||||
}
|
||||
|
||||
func runStart(ctx context.Context, dockerCli command.Cli, backend api.Service, opts startOptions, services []string) error {
|
||||
func runStart(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts startOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(ctx, dockerCli, services...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -32,7 +32,7 @@ type stopOptions struct {
|
||||
timeout int
|
||||
}
|
||||
|
||||
func stopCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func stopCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := stopOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -53,7 +53,7 @@ func stopCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runStop(ctx context.Context, dockerCli command.Cli, backend api.Service, opts stopOptions, services []string) error {
|
||||
func runStop(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts stopOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(ctx, dockerCli, services...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -34,7 +34,7 @@ type topOptions struct {
|
||||
*ProjectOptions
|
||||
}
|
||||
|
||||
func topCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func topCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := topOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -54,7 +54,7 @@ type (
|
||||
topEntries map[string]string
|
||||
)
|
||||
|
||||
func runTop(ctx context.Context, dockerCli command.Cli, backend api.Service, opts topOptions, services []string) error {
|
||||
func runTop(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts topOptions, services []string) error {
|
||||
projectName, err := opts.toProjectName(ctx, dockerCli)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -202,7 +202,7 @@ var topTestCases = []struct {
|
||||
}
|
||||
|
||||
// TestRunTopCore only tests the core functionality of runTop: formatting
|
||||
// and printing of the output of (api.Service).Top().
|
||||
// and printing of the output of (api.Compose).Top().
|
||||
func TestRunTopCore(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ func (opts upOptions) OnExit() api.Cascade {
|
||||
}
|
||||
}
|
||||
|
||||
func upCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func upCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
up := upOptions{}
|
||||
create := createOptions{}
|
||||
build := buildOptions{ProjectOptions: p}
|
||||
@@ -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")
|
||||
@@ -223,10 +224,11 @@ func validateFlags(up *upOptions, create *createOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func runUp(
|
||||
ctx context.Context,
|
||||
dockerCli command.Cli,
|
||||
backend api.Service,
|
||||
backend api.Compose,
|
||||
createOptions createOptions,
|
||||
upOptions upOptions,
|
||||
buildOptions buildOptions,
|
||||
@@ -330,7 +332,7 @@ func runUp(
|
||||
WaitTimeout: timeout,
|
||||
Watch: upOptions.watch,
|
||||
Services: services,
|
||||
NavigationMenu: upOptions.navigationMenu && ui.Mode != "plain",
|
||||
NavigationMenu: upOptions.navigationMenu && ui.Mode != "plain" && dockerCli.In().IsTerminal(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ type vizOptions struct {
|
||||
indentationStr string
|
||||
}
|
||||
|
||||
func vizCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func vizCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := vizOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -63,7 +63,7 @@ func vizCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runViz(ctx context.Context, dockerCli command.Cli, backend api.Service, opts *vizOptions) error {
|
||||
func runViz(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts *vizOptions) error {
|
||||
_, _ = fmt.Fprintln(os.Stderr, "viz command is EXPERIMENTAL")
|
||||
project, _, err := opts.ToProject(ctx, dockerCli, nil)
|
||||
if err != nil {
|
||||
|
||||
92
cmd/compose/volumes.go
Normal file
92
cmd/compose/volumes.go
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/cli/cli/flags"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type volumesOptions struct {
|
||||
*ProjectOptions
|
||||
Quiet bool
|
||||
Format string
|
||||
}
|
||||
|
||||
func volumesCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
options := volumesOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "volumes [OPTIONS] [SERVICE...]",
|
||||
Short: "List volumes",
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runVol(ctx, dockerCli, backend, args, options)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVarP(&options.Quiet, "quiet", "q", false, "Only display volume names")
|
||||
cmd.Flags().StringVar(&options.Format, "format", "table", flags.FormatHelp)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runVol(ctx context.Context, dockerCli command.Cli, backend api.Compose, services []string, options volumesOptions) error {
|
||||
project, name, err := options.projectOrName(ctx, dockerCli, services...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if project != nil {
|
||||
names := project.ServiceNames()
|
||||
for _, service := range services {
|
||||
if !slices.Contains(names, service) {
|
||||
return fmt.Errorf("no such service: %s", service)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
volumes, err := backend.Volumes(ctx, name, api.VolumesOptions{
|
||||
Services: services,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if options.Quiet {
|
||||
for _, v := range volumes {
|
||||
_, _ = fmt.Fprintln(dockerCli.Out(), v.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
volumeCtx := formatter.Context{
|
||||
Output: dockerCli.Out(),
|
||||
Format: formatter.NewVolumeFormat(options.Format, options.Quiet),
|
||||
}
|
||||
|
||||
return formatter.VolumeWrite(volumeCtx, volumes)
|
||||
}
|
||||
@@ -34,7 +34,7 @@ type waitOptions struct {
|
||||
downProject bool
|
||||
}
|
||||
|
||||
func waitCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func waitCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
opts := waitOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func waitCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runWait(ctx context.Context, dockerCli command.Cli, backend api.Service, opts *waitOptions) (int64, error) {
|
||||
func runWait(ctx context.Context, dockerCli command.Cli, backend api.Compose, opts *waitOptions) (int64, error) {
|
||||
_, name, err := opts.projectOrName(ctx, dockerCli)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
||||
@@ -36,7 +36,7 @@ type watchOptions struct {
|
||||
noUp bool
|
||||
}
|
||||
|
||||
func watchCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
func watchCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Compose) *cobra.Command {
|
||||
watchOpts := watchOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
@@ -64,7 +64,7 @@ func watchCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runWatch(ctx context.Context, dockerCli command.Cli, backend api.Service, watchOpts watchOptions, buildOpts buildOptions, services []string) error {
|
||||
func runWatch(ctx context.Context, dockerCli command.Cli, backend api.Compose, watchOpts watchOptions, buildOpts buildOptions, services []string) error {
|
||||
project, _, err := watchOpts.ToProject(ctx, dockerCli, services)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -28,49 +28,42 @@ func ansi(code string) string {
|
||||
return fmt.Sprintf("\033%s", code)
|
||||
}
|
||||
|
||||
func SaveCursor() {
|
||||
func saveCursor() {
|
||||
if disableAnsi {
|
||||
return
|
||||
}
|
||||
fmt.Print(ansi("7"))
|
||||
}
|
||||
|
||||
func RestoreCursor() {
|
||||
func restoreCursor() {
|
||||
if disableAnsi {
|
||||
return
|
||||
}
|
||||
fmt.Print(ansi("8"))
|
||||
}
|
||||
|
||||
func HideCursor() {
|
||||
if disableAnsi {
|
||||
return
|
||||
}
|
||||
fmt.Print(ansi("[?25l"))
|
||||
}
|
||||
|
||||
func ShowCursor() {
|
||||
func showCursor() {
|
||||
if disableAnsi {
|
||||
return
|
||||
}
|
||||
fmt.Print(ansi("[?25h"))
|
||||
}
|
||||
|
||||
func MoveCursor(y, x int) {
|
||||
func moveCursor(y, x int) {
|
||||
if disableAnsi {
|
||||
return
|
||||
}
|
||||
fmt.Print(ansi(fmt.Sprintf("[%d;%dH", y, x)))
|
||||
}
|
||||
|
||||
func MoveCursorX(pos int) {
|
||||
func carriageReturn() {
|
||||
if disableAnsi {
|
||||
return
|
||||
}
|
||||
fmt.Print(ansi(fmt.Sprintf("[%dG", pos)))
|
||||
fmt.Print(ansi(fmt.Sprintf("[%dG", 0)))
|
||||
}
|
||||
|
||||
func ClearLine() {
|
||||
func clearLine() {
|
||||
if disableAnsi {
|
||||
return
|
||||
}
|
||||
@@ -78,7 +71,7 @@ func ClearLine() {
|
||||
fmt.Print(ansi("[2K"))
|
||||
}
|
||||
|
||||
func MoveCursorUp(lines int) {
|
||||
func moveCursorUp(lines int) {
|
||||
if disableAnsi {
|
||||
return
|
||||
}
|
||||
@@ -86,7 +79,7 @@ func MoveCursorUp(lines int) {
|
||||
fmt.Print(ansi(fmt.Sprintf("[%dA", lines)))
|
||||
}
|
||||
|
||||
func MoveCursorDown(lines int) {
|
||||
func moveCursorDown(lines int) {
|
||||
if disableAnsi {
|
||||
return
|
||||
}
|
||||
@@ -94,7 +87,7 @@ func MoveCursorDown(lines int) {
|
||||
fmt.Print(ansi(fmt.Sprintf("[%dB", lines)))
|
||||
}
|
||||
|
||||
func NewLine() {
|
||||
func newLine() {
|
||||
// Like \n
|
||||
fmt.Print("\012")
|
||||
}
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -56,10 +56,6 @@ func NewLogConsumer(ctx context.Context, stdout, stderr io.Writer, color, prefix
|
||||
}
|
||||
}
|
||||
|
||||
func (l *logConsumer) Register(name string) {
|
||||
l.register(name)
|
||||
}
|
||||
|
||||
func (l *logConsumer) register(name string) *presenter {
|
||||
var p *presenter
|
||||
root, _, found := strings.Cut(name, " ")
|
||||
@@ -73,9 +69,12 @@ func (l *logConsumer) register(name string) *presenter {
|
||||
} else {
|
||||
cf := monochrome
|
||||
if l.color {
|
||||
if name == api.WatchLogger {
|
||||
switch name {
|
||||
case "":
|
||||
cf = monochrome
|
||||
case api.WatchLogger:
|
||||
cf = makeColorFunc("92")
|
||||
} else {
|
||||
default:
|
||||
cf = nextColor()
|
||||
}
|
||||
}
|
||||
@@ -118,23 +117,15 @@ func (l *logConsumer) write(w io.Writer, container, message string) {
|
||||
if l.ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
if KeyboardManager != nil {
|
||||
KeyboardManager.ClearKeyboardInfo()
|
||||
}
|
||||
|
||||
p := l.getPresenter(container)
|
||||
timestamp := time.Now().Format(jsonmessage.RFC3339NanoFixed)
|
||||
for _, line := range strings.Split(message, "\n") {
|
||||
if l.timestamp {
|
||||
_, _ = fmt.Fprintf(w, "%s%s%s\n", p.prefix, timestamp, line)
|
||||
_, _ = fmt.Fprintf(w, "%s%s %s\n", p.prefix, timestamp, line)
|
||||
} else {
|
||||
_, _ = fmt.Fprintf(w, "%s%s\n", p.prefix, line)
|
||||
}
|
||||
}
|
||||
|
||||
if KeyboardManager != nil {
|
||||
KeyboardManager.PrintKeyboardInfo()
|
||||
}
|
||||
}
|
||||
|
||||
func (l *logConsumer) Status(container, msg string) {
|
||||
@@ -168,3 +159,27 @@ func (p *presenter) setPrefix(width int) {
|
||||
}
|
||||
p.prefix = p.colors(fmt.Sprintf("%-"+strconv.Itoa(width)+"s | ", p.name))
|
||||
}
|
||||
|
||||
type logDecorator struct {
|
||||
decorated api.LogConsumer
|
||||
Before func()
|
||||
After func()
|
||||
}
|
||||
|
||||
func (l logDecorator) Log(containerName, message string) {
|
||||
l.Before()
|
||||
l.decorated.Log(containerName, message)
|
||||
l.After()
|
||||
}
|
||||
|
||||
func (l logDecorator) Err(containerName, message string) {
|
||||
l.Before()
|
||||
l.decorated.Err(containerName, message)
|
||||
l.After()
|
||||
}
|
||||
|
||||
func (l logDecorator) Status(container, msg string) {
|
||||
l.Before()
|
||||
l.decorated.Status(container, msg)
|
||||
l.After()
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"reflect"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -49,8 +48,8 @@ func (ke *KeyboardError) printError(height int, info string) {
|
||||
if ke.shouldDisplay() {
|
||||
errMessage := ke.err.Error()
|
||||
|
||||
MoveCursor(height-1-extraLines(info)-extraLines(errMessage), 0)
|
||||
ClearLine()
|
||||
moveCursor(height-1-extraLines(info)-extraLines(errMessage), 0)
|
||||
clearLine()
|
||||
|
||||
fmt.Print(errMessage)
|
||||
}
|
||||
@@ -70,12 +69,12 @@ func (ke *KeyboardError) error() string {
|
||||
}
|
||||
|
||||
type KeyboardWatch struct {
|
||||
Watching bool
|
||||
Watcher Toggle
|
||||
IsConfigured bool
|
||||
Watching bool
|
||||
Watcher Feature
|
||||
}
|
||||
|
||||
type Toggle interface {
|
||||
// Feature is an compose feature that can be started/stopped by a menu command
|
||||
type Feature interface {
|
||||
Start(context.Context) error
|
||||
Stop() error
|
||||
}
|
||||
@@ -90,31 +89,26 @@ const (
|
||||
|
||||
type LogKeyboard struct {
|
||||
kError KeyboardError
|
||||
Watch KeyboardWatch
|
||||
Watch *KeyboardWatch
|
||||
IsDockerDesktopActive bool
|
||||
logLevel KEYBOARD_LOG_LEVEL
|
||||
signalChannel chan<- os.Signal
|
||||
}
|
||||
|
||||
// FIXME(ndeloof) we should avoid use of such a global reference. see use in logConsumer
|
||||
var KeyboardManager *LogKeyboard
|
||||
|
||||
func NewKeyboardManager(isDockerDesktopActive bool, sc chan<- os.Signal, w bool, watcher Toggle) *LogKeyboard {
|
||||
KeyboardManager = &LogKeyboard{
|
||||
Watch: KeyboardWatch{
|
||||
Watching: w,
|
||||
Watcher: watcher,
|
||||
IsConfigured: !reflect.ValueOf(watcher).IsNil(),
|
||||
},
|
||||
func NewKeyboardManager(isDockerDesktopActive bool, sc chan<- os.Signal) *LogKeyboard {
|
||||
return &LogKeyboard{
|
||||
IsDockerDesktopActive: isDockerDesktopActive,
|
||||
logLevel: INFO,
|
||||
signalChannel: sc,
|
||||
}
|
||||
return KeyboardManager
|
||||
}
|
||||
|
||||
func (lk *LogKeyboard) ClearKeyboardInfo() {
|
||||
lk.clearNavigationMenu()
|
||||
func (lk *LogKeyboard) Decorate(l api.LogConsumer) api.LogConsumer {
|
||||
return logDecorator{
|
||||
decorated: l,
|
||||
Before: lk.clearNavigationMenu,
|
||||
After: lk.PrintKeyboardInfo,
|
||||
}
|
||||
}
|
||||
|
||||
func (lk *LogKeyboard) PrintKeyboardInfo() {
|
||||
@@ -139,7 +133,7 @@ func (lk *LogKeyboard) createBuffer(lines int) {
|
||||
|
||||
if lines > 0 {
|
||||
allocateSpace(lines)
|
||||
MoveCursorUp(lines)
|
||||
moveCursorUp(lines)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,17 +146,17 @@ func (lk *LogKeyboard) printNavigationMenu() {
|
||||
height := goterm.Height()
|
||||
menu := lk.navigationMenu()
|
||||
|
||||
MoveCursorX(0)
|
||||
SaveCursor()
|
||||
carriageReturn()
|
||||
saveCursor()
|
||||
|
||||
lk.kError.printError(height, menu)
|
||||
|
||||
MoveCursor(height-extraLines(menu), 0)
|
||||
ClearLine()
|
||||
moveCursor(height-extraLines(menu), 0)
|
||||
clearLine()
|
||||
fmt.Print(menu)
|
||||
|
||||
MoveCursorX(0)
|
||||
RestoreCursor()
|
||||
carriageReturn()
|
||||
restoreCursor()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +179,7 @@ func (lk *LogKeyboard) navigationMenu() string {
|
||||
watchInfo = navColor(" ")
|
||||
}
|
||||
isEnabled := " Enable"
|
||||
if lk.Watch.Watching {
|
||||
if lk.Watch != nil && lk.Watch.Watching {
|
||||
isEnabled = " Disable"
|
||||
}
|
||||
watchInfo = watchInfo + shortcutKeyColor("w") + navColor(isEnabled+" Watch")
|
||||
@@ -194,15 +188,15 @@ func (lk *LogKeyboard) navigationMenu() string {
|
||||
|
||||
func (lk *LogKeyboard) clearNavigationMenu() {
|
||||
height := goterm.Height()
|
||||
MoveCursorX(0)
|
||||
SaveCursor()
|
||||
carriageReturn()
|
||||
saveCursor()
|
||||
|
||||
// ClearLine()
|
||||
// clearLine()
|
||||
for i := 0; i < height; i++ {
|
||||
MoveCursorDown(1)
|
||||
ClearLine()
|
||||
moveCursorDown(1)
|
||||
clearLine()
|
||||
}
|
||||
RestoreCursor()
|
||||
restoreCursor()
|
||||
}
|
||||
|
||||
func (lk *LogKeyboard) openDockerDesktop(ctx context.Context, project *types.Project) {
|
||||
@@ -268,7 +262,7 @@ func (lk *LogKeyboard) keyboardError(prefix string, err error) {
|
||||
}
|
||||
|
||||
func (lk *LogKeyboard) ToggleWatch(ctx context.Context, options api.UpOptions) {
|
||||
if !lk.Watch.IsConfigured {
|
||||
if lk.Watch == nil {
|
||||
return
|
||||
}
|
||||
if lk.Watch.Watching {
|
||||
@@ -299,7 +293,7 @@ func (lk *LogKeyboard) HandleKeyEvents(ctx context.Context, event keyboard.KeyEv
|
||||
case 'v':
|
||||
lk.openDockerDesktop(ctx, project)
|
||||
case 'w':
|
||||
if !lk.Watch.IsConfigured {
|
||||
if lk.Watch == nil {
|
||||
// we try to open watch docs if DD is installed
|
||||
if lk.IsDockerDesktopActive {
|
||||
lk.openDDWatchDocs(ctx, project)
|
||||
@@ -322,22 +316,31 @@ func (lk *LogKeyboard) HandleKeyEvents(ctx context.Context, event keyboard.KeyEv
|
||||
case keyboard.KeyCtrlC:
|
||||
_ = keyboard.Close()
|
||||
lk.clearNavigationMenu()
|
||||
ShowCursor()
|
||||
showCursor()
|
||||
|
||||
lk.logLevel = NONE
|
||||
// will notify main thread to kill and will handle gracefully
|
||||
lk.signalChannel <- syscall.SIGINT
|
||||
case keyboard.KeyCtrlZ:
|
||||
handleCtrlZ()
|
||||
case keyboard.KeyEnter:
|
||||
NewLine()
|
||||
newLine()
|
||||
lk.printNavigationMenu()
|
||||
}
|
||||
}
|
||||
|
||||
func (lk *LogKeyboard) EnableWatch(enabled bool, watcher Feature) {
|
||||
lk.Watch = &KeyboardWatch{
|
||||
Watching: enabled,
|
||||
Watcher: watcher,
|
||||
}
|
||||
}
|
||||
|
||||
func allocateSpace(lines int) {
|
||||
for i := 0; i < lines; i++ {
|
||||
ClearLine()
|
||||
NewLine()
|
||||
MoveCursorX(0)
|
||||
clearLine()
|
||||
newLine()
|
||||
carriageReturn()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
25
cmd/formatter/shortcut_unix.go
Normal file
25
cmd/formatter/shortcut_unix.go
Normal file
@@ -0,0 +1,25 @@
|
||||
//go:build !windows
|
||||
|
||||
/*
|
||||
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 "syscall"
|
||||
|
||||
func handleCtrlZ() {
|
||||
_ = syscall.Kill(0, syscall.SIGSTOP)
|
||||
}
|
||||
25
cmd/formatter/shortcut_windows.go
Normal file
25
cmd/formatter/shortcut_windows.go
Normal file
@@ -0,0 +1,25 @@
|
||||
//go:build windows
|
||||
|
||||
/*
|
||||
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
|
||||
|
||||
// handleCtrlZ is a no-op on Windows as SIGSTOP is not supported
|
||||
func handleCtrlZ() {
|
||||
// Windows doesn't support SIGSTOP/SIGCONT signals
|
||||
// Ctrl+Z behavior is handled differently by the Windows terminal
|
||||
}
|
||||
83
cmd/main.go
83
cmd/main.go
@@ -20,11 +20,11 @@ import (
|
||||
"os"
|
||||
|
||||
dockercli "github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/docker/cli/cli-plugins/plugin"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/cmd/cmdtrace"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/compose/v2/cmd/prompt"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -35,60 +35,43 @@ import (
|
||||
)
|
||||
|
||||
func pluginMain() {
|
||||
plugin.Run(func(dockerCli command.Cli) *cobra.Command {
|
||||
// TODO(milas): this cast is safe but we should not need to do this,
|
||||
// we should expose the concrete service type so that we do not need
|
||||
// to rely on the `api.Service` interface internally
|
||||
backend := compose.NewComposeService(dockerCli).(commands.Backend)
|
||||
cmd := commands.RootCommand(dockerCli, backend)
|
||||
originalPreRunE := cmd.PersistentPreRunE
|
||||
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
|
||||
// initialize the dockerCli instance
|
||||
if err := plugin.PersistentPreRunE(cmd, args); err != nil {
|
||||
return err
|
||||
}
|
||||
// compose-specific initialization
|
||||
dockerCliPostInitialize(dockerCli)
|
||||
plugin.Run(
|
||||
func(cli command.Cli) *cobra.Command {
|
||||
backend := compose.NewComposeService(cli,
|
||||
compose.WithPrompt(prompt.NewPrompt(cli.In(), cli.Out()).Confirm),
|
||||
)
|
||||
cmd := commands.RootCommand(cli, backend)
|
||||
originalPreRunE := cmd.PersistentPreRunE
|
||||
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
|
||||
// initialize the cli instance
|
||||
if err := plugin.PersistentPreRunE(cmd, args); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmdtrace.Setup(cmd, cli, os.Args[1:]); err != nil {
|
||||
logrus.Debugf("failed to enable tracing: %v", err)
|
||||
}
|
||||
|
||||
if err := cmdtrace.Setup(cmd, dockerCli, os.Args[1:]); err != nil {
|
||||
logrus.Debugf("failed to enable tracing: %v", err)
|
||||
if originalPreRunE != nil {
|
||||
return originalPreRunE(cmd, args)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if originalPreRunE != nil {
|
||||
return originalPreRunE(cmd, args)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd.SetFlagErrorFunc(func(c *cobra.Command, err error) error {
|
||||
return dockercli.StatusError{
|
||||
StatusCode: 1,
|
||||
Status: err.Error(),
|
||||
}
|
||||
})
|
||||
return cmd
|
||||
},
|
||||
manager.Metadata{
|
||||
cmd.SetFlagErrorFunc(func(c *cobra.Command, err error) error {
|
||||
return dockercli.StatusError{
|
||||
StatusCode: 1,
|
||||
Status: err.Error(),
|
||||
}
|
||||
})
|
||||
return cmd
|
||||
},
|
||||
metadata.Metadata{
|
||||
SchemaVersion: "0.1.0",
|
||||
Vendor: "Docker Inc.",
|
||||
Version: internal.Version,
|
||||
})
|
||||
}
|
||||
|
||||
// dockerCliPostInitialize performs Compose-specific configuration for the
|
||||
// command.Cli instance provided by the plugin.Run() initialization.
|
||||
//
|
||||
// NOTE: This must be called AFTER plugin.PersistentPreRunE.
|
||||
func dockerCliPostInitialize(dockerCli command.Cli) {
|
||||
// HACK(milas): remove once docker/cli#4574 is merged; for now,
|
||||
// set it in a rather roundabout way by grabbing the underlying
|
||||
// concrete client and manually invoking an option on it
|
||||
_ = dockerCli.Apply(func(cli *command.DockerCli) error {
|
||||
if mobyClient, ok := cli.Client().(*client.Client); ok {
|
||||
_ = client.WithUserAgent("compose/" + internal.Version)(mobyClient)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
},
|
||||
command.WithUserAgent("compose/"+internal.Version),
|
||||
)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Container: github.com/docker/compose-cli/pkg/prompt (interfaces: UI)
|
||||
// Source: github.com/docker/compose-cli/pkg/prompt (interfaces: UI)
|
||||
|
||||
// Package prompt is a generated GoMock package.
|
||||
package prompt
|
||||
@@ -39,20 +39,30 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
type options struct {
|
||||
db string
|
||||
size int
|
||||
}
|
||||
|
||||
func composeCommand() *cobra.Command {
|
||||
c := &cobra.Command{
|
||||
Use: "compose EVENT",
|
||||
TraverseChildren: true,
|
||||
}
|
||||
c.PersistentFlags().String("project-name", "", "compose project name") // unused
|
||||
|
||||
var options options
|
||||
upCmd := &cobra.Command{
|
||||
Use: "up",
|
||||
Run: up,
|
||||
Use: "up",
|
||||
Run: func(_ *cobra.Command, args []string) {
|
||||
up(options, args)
|
||||
},
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
upCmd.Flags().String("type", "", "Database type (mysql, postgres, etc.)")
|
||||
|
||||
upCmd.Flags().StringVar(&options.db, "type", "", "Database type (mysql, postgres, etc.)")
|
||||
_ = upCmd.MarkFlagRequired("type")
|
||||
upCmd.Flags().Int("size", 10, "Database size in GB")
|
||||
upCmd.Flags().IntVar(&options.size, "size", 10, "Database size in GB")
|
||||
upCmd.Flags().String("name", "", "Name of the database to be created")
|
||||
_ = upCmd.MarkFlagRequired("name")
|
||||
|
||||
@@ -71,13 +81,13 @@ func composeCommand() *cobra.Command {
|
||||
|
||||
const lineSeparator = "\n"
|
||||
|
||||
func up(_ *cobra.Command, args []string) {
|
||||
func up(options options, args []string) {
|
||||
servicename := args[0]
|
||||
fmt.Printf(`{ "type": "debug", "message": "Starting %s" }%s`, servicename, lineSeparator)
|
||||
|
||||
for i := 0; i < 100; i += 10 {
|
||||
for i := 0; i < options.size; i += 10 {
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Printf(`{ "type": "info", "message": "Processing ... %d%%" }%s`, i, lineSeparator)
|
||||
fmt.Printf(`{ "type": "info", "message": "Processing ... %d%%" }%s`, i*100/options.size, lineSeparator)
|
||||
}
|
||||
fmt.Printf(`{ "type": "setenv", "message": "URL=https://magic.cloud/%s" }%s`, servicename, lineSeparator)
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ Define and run multi-container applications with Docker
|
||||
| [`unpause`](compose_unpause.md) | Unpause services |
|
||||
| [`up`](compose_up.md) | Create and start containers |
|
||||
| [`version`](compose_version.md) | Show the Docker Compose version information |
|
||||
| [`volumes`](compose_volumes.md) | List volumes |
|
||||
| [`wait`](compose_wait.md) | Block until containers of all (or specified) services stop. |
|
||||
| [`watch`](compose_watch.md) | Watch build context for service and rebuild/refresh containers when files are updated |
|
||||
|
||||
|
||||
@@ -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) |
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ the canonical format.
|
||||
| `--hash` | `string` | | Print the service config hash, one per line. |
|
||||
| `--images` | `bool` | | Print the image names, one per line. |
|
||||
| `--lock-image-digests` | `bool` | | Produces an override file with image digests |
|
||||
| `--models` | `bool` | | Print the model names, one per line. |
|
||||
| `--networks` | `bool` | | Print the network names, one per line. |
|
||||
| `--no-consistency` | `bool` | | Don't check model consistency - warning: may produce invalid Compose output |
|
||||
| `--no-env-resolution` | `bool` | | Don't resolve service env files |
|
||||
| `--no-interpolate` | `bool` | | Don't interpolate environment variables |
|
||||
|
||||
@@ -23,10 +23,12 @@ The events that can be received using this can be seen [here](/reference/cli/doc
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:------------|:-------|:--------|:------------------------------------------|
|
||||
| `--dry-run` | `bool` | | Execute command in dry run mode |
|
||||
| `--json` | `bool` | | Output events as a stream of json objects |
|
||||
| Name | Type | Default | Description |
|
||||
|:------------|:---------|:--------|:------------------------------------------|
|
||||
| `--dry-run` | `bool` | | Execute command in dry run mode |
|
||||
| `--json` | `bool` | | Output events as a stream of json objects |
|
||||
| `--since` | `string` | | Show all events created since timestamp |
|
||||
| `--until` | `string` | | Stream events until this timestamp |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -6,6 +6,12 @@ This is the equivalent of `docker exec` targeting a Compose service.
|
||||
With this subcommand, you can run arbitrary commands in your services. Commands allocate a TTY by default, so
|
||||
you can use a command such as `docker compose exec web sh` to get an interactive prompt.
|
||||
|
||||
By default, Compose will enter container in interactive mode and allocate a TTY, while the equivalent `docker exec`
|
||||
command requires passing `--interactive --tty` flags to get the same behavior. Compose also support those two flags
|
||||
to offer a smooth migration between commands, whenever they are no-op by default. Still, `interactive` can be used to
|
||||
force disabling interactive mode (`--interactive=false`), typically when `docker compose exec` command is used inside
|
||||
a script.
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
@@ -14,7 +20,7 @@ you can use a command such as `docker compose exec web sh` to get an interactive
|
||||
| `--dry-run` | `bool` | | Execute command in dry run mode |
|
||||
| `-e`, `--env` | `stringArray` | | Set environment variables |
|
||||
| `--index` | `int` | `0` | Index of the container if service has multiple replicas |
|
||||
| `-T`, `--no-TTY` | `bool` | `true` | Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY. |
|
||||
| `-T`, `--no-tty` | `bool` | `true` | Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY. |
|
||||
| `--privileged` | `bool` | | Give extended privileges to the process |
|
||||
| `-u`, `--user` | `string` | | Run the command as this user |
|
||||
| `-w`, `--workdir` | `string` | | Path to workdir directory for this command |
|
||||
@@ -28,3 +34,9 @@ This is the equivalent of `docker exec` targeting a Compose service.
|
||||
|
||||
With this subcommand, you can run arbitrary commands in your services. Commands allocate a TTY by default, so
|
||||
you can use a command such as `docker compose exec web sh` to get an interactive prompt.
|
||||
|
||||
By default, Compose will enter container in interactive mode and allocate a TTY, while the equivalent `docker exec`
|
||||
command requires passing `--interactive --tty` flags to get the same behavior. Compose also support those two flags
|
||||
to offer a smooth migration between commands, whenever they are no-op by default. Still, `interactive` can be used to
|
||||
force disabling interactive mode (`--interactive=false`), typically when `docker compose exec` command is used inside
|
||||
a script.
|
||||
@@ -7,6 +7,7 @@ Publish compose application
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------------------|:---------|:--------|:-------------------------------------------------------------------------------|
|
||||
| `--app` | `bool` | | Published compose application (includes referenced images) |
|
||||
| `--dry-run` | `bool` | | Execute command in dry run mode |
|
||||
| `--oci-version` | `string` | | OCI image/artifact specification version (automatically determined by default) |
|
||||
| `--resolve-image-digests` | `bool` | | Pin image tags to digests |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
16
docs/reference/compose_volumes.md
Normal file
16
docs/reference/compose_volumes.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# docker compose volumes
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
List volumes
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------|:---------|:--------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `--dry-run` | `bool` | | Execute command in dry run mode |
|
||||
| `--format` | `string` | `table` | Format output using a custom template:<br>'table': Print output in table format with column headers (default)<br>'table TEMPLATE': Print output in table format using the given Go template<br>'json': Print in JSON format<br>'TEMPLATE': Print output using the given Go template.<br>Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates |
|
||||
| `-q`, `--quiet` | `bool` | | Only display volume names |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -37,6 +37,7 @@ cname:
|
||||
- docker compose unpause
|
||||
- docker compose up
|
||||
- docker compose version
|
||||
- docker compose volumes
|
||||
- docker compose wait
|
||||
- docker compose watch
|
||||
clink:
|
||||
@@ -72,6 +73,7 @@ clink:
|
||||
- docker_compose_unpause.yaml
|
||||
- docker_compose_up.yaml
|
||||
- docker_compose_version.yaml
|
||||
- docker_compose_volumes.yaml
|
||||
- docker_compose_wait.yaml
|
||||
- docker_compose_watch.yaml
|
||||
options:
|
||||
|
||||
@@ -5,6 +5,16 @@ usage: docker compose alpha publish [OPTIONS] REPOSITORY[:TAG]
|
||||
pname: docker compose alpha
|
||||
plink: docker_compose_alpha.yaml
|
||||
options:
|
||||
- option: app
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Published compose application (includes referenced images)
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: oci-version
|
||||
value_type: string
|
||||
description: |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -56,6 +56,26 @@ options:
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: models
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Print the model names, one per line.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: networks
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Print the network names, one per line.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: no-consistency
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
|
||||
@@ -34,6 +34,24 @@ options:
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: since
|
||||
value_type: string
|
||||
description: Show all events created since timestamp
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: until
|
||||
value_type: string
|
||||
description: Stream events until this timestamp
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
|
||||
@@ -5,6 +5,12 @@ long: |-
|
||||
|
||||
With this subcommand, you can run arbitrary commands in your services. Commands allocate a TTY by default, so
|
||||
you can use a command such as `docker compose exec web sh` to get an interactive prompt.
|
||||
|
||||
By default, Compose will enter container in interactive mode and allocate a TTY, while the equivalent `docker exec`
|
||||
command requires passing `--interactive --tty` flags to get the same behavior. Compose also support those two flags
|
||||
to offer a smooth migration between commands, whenever they are no-op by default. Still, `interactive` can be used to
|
||||
force disabling interactive mode (`--interactive=false`), typically when `docker compose exec` command is used inside
|
||||
a script.
|
||||
usage: docker compose exec [OPTIONS] SERVICE COMMAND [ARGS...]
|
||||
pname: docker compose
|
||||
plink: docker_compose.yaml
|
||||
@@ -52,7 +58,7 @@ options:
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: no-TTY
|
||||
- option: no-tty
|
||||
shorthand: T
|
||||
value_type: bool
|
||||
default_value: "true"
|
||||
|
||||
@@ -5,6 +5,16 @@ usage: docker compose publish [OPTIONS] REPOSITORY[:TAG]
|
||||
pname: docker compose
|
||||
plink: docker_compose.yaml
|
||||
options:
|
||||
- option: app
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Published compose application (includes referenced images)
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: oci-version
|
||||
value_type: string
|
||||
description: |
|
||||
|
||||
@@ -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"
|
||||
|
||||
52
docs/reference/docker_compose_volumes.yaml
Normal file
52
docs/reference/docker_compose_volumes.yaml
Normal file
@@ -0,0 +1,52 @@
|
||||
command: docker compose volumes
|
||||
short: List volumes
|
||||
long: List volumes
|
||||
usage: docker compose volumes [OPTIONS] [SERVICE...]
|
||||
pname: docker compose
|
||||
plink: docker_compose.yaml
|
||||
options:
|
||||
- option: format
|
||||
value_type: string
|
||||
default_value: table
|
||||
description: |-
|
||||
Format output using a custom template:
|
||||
'table': Print output in table format with column headers (default)
|
||||
'table TEMPLATE': Print output in table format using the given Go template
|
||||
'json': Print in JSON format
|
||||
'TEMPLATE': Print output using the given Go template.
|
||||
Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: quiet
|
||||
shorthand: q
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Only display volume names
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Execute command in dry run mode
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
|
||||
85
go.mod
85
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/docker/compose/v2
|
||||
|
||||
go 1.23.8
|
||||
go 1.24.9
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||
@@ -8,28 +8,28 @@ 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.5
|
||||
github.com/containerd/containerd/v2 v2.1.2
|
||||
github.com/compose-spec/compose-go/v2 v2.9.0
|
||||
github.com/containerd/containerd/v2 v2.1.4
|
||||
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/cli v28.2.2+incompatible
|
||||
github.com/docker/buildx v0.29.1
|
||||
github.com/docker/cli v28.5.1+incompatible
|
||||
github.com/docker/cli-docs-tool v0.10.0
|
||||
github.com/docker/docker v28.2.2+incompatible
|
||||
github.com/docker/go-connections v0.5.0
|
||||
github.com/docker/docker v28.5.1+incompatible
|
||||
github.com/docker/go-connections v0.6.0
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203
|
||||
github.com/fsnotify/fsevents v0.2.0
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/hashicorp/go-version v1.7.0
|
||||
github.com/jonboulle/clockwork v0.5.0
|
||||
github.com/mattn/go-shellwords v1.0.12
|
||||
github.com/mitchellh/go-ps v1.0.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/moby/buildkit v0.23.0
|
||||
github.com/moby/buildkit v0.25.1
|
||||
github.com/moby/go-archive v0.1.0
|
||||
github.com/moby/patternmatcher v0.6.0
|
||||
github.com/moby/sys/atomicwriter v0.1.0
|
||||
@@ -40,31 +40,30 @@ require (
|
||||
github.com/otiai10/copy v1.14.1
|
||||
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/stretchr/testify v1.10.0
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/stretchr/testify v1.11.1
|
||||
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.15.0
|
||||
golang.org/x/sys v0.33.0
|
||||
google.golang.org/grpc v1.73.0
|
||||
go.uber.org/mock v0.6.0
|
||||
golang.org/x/sync v0.17.0
|
||||
golang.org/x/sys v0.37.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
|
||||
)
|
||||
|
||||
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
|
||||
@@ -89,7 +88,7 @@ require (
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/ttrpc v1.2.7 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.2.3 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.3 // indirect
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
|
||||
@@ -98,12 +97,11 @@ 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
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
|
||||
github.com/gofrs/flock v0.12.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||
@@ -111,12 +109,12 @@ require (
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/in-toto/in-toto-golang v0.9.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf // indirect
|
||||
@@ -132,6 +130,7 @@ require (
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/spdystream v0.5.0 // indirect
|
||||
@@ -168,28 +167,26 @@ require (
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
|
||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
||||
github.com/zclconf/go-cty v1.16.2 // indirect
|
||||
github.com/zclconf/go-cty v1.17.0 // 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/protobuf v1.36.6 // 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.9 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
@@ -205,7 +202,7 @@ require (
|
||||
)
|
||||
|
||||
exclude (
|
||||
// FIXME(thaJeztah): remoove this once kubernetes updated their dependencies to no longer need this.
|
||||
// FIXME(thaJeztah): remove this once kubernetes updated their dependencies to no longer need this.
|
||||
//
|
||||
// For additional details, see this PR and links mentioned in that PR:
|
||||
// https://github.com/kubernetes-sigs/kustomize/pull/5830#issuecomment-2569960859
|
||||
|
||||
159
go.sum
159
go.sum
@@ -1,5 +1,3 @@
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
|
||||
@@ -10,8 +8,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,16 +78,16 @@ github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e
|
||||
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||
github.com/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.5 h1:H7xP5OMKdkN2p0brx01slxIU6dE/q6ybbG+jozPtIqk=
|
||||
github.com/compose-spec/compose-go/v2 v2.6.5/go.mod h1:TmjkIB9W73fwVxkYY+u2uhMbMUakjiif79DlYgXsyvU=
|
||||
github.com/compose-spec/compose-go/v2 v2.9.0 h1:UHSv/QHlo6QJtrT4igF1rdORgIUhDo1gWuyJUoiNNIM=
|
||||
github.com/compose-spec/compose-go/v2 v2.9.0/go.mod h1:Oky9AZGTRB4E+0VbTPZTUu4Kp+oEMMuwZXZtPPVT1iE=
|
||||
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=
|
||||
github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||
github.com/containerd/containerd/api v1.9.0 h1:HZ/licowTRazus+wt9fM6r/9BQO7S0vD5lMcWspGIg0=
|
||||
github.com/containerd/containerd/api v1.9.0/go.mod h1:GhghKFmTR3hNtyznBoQ0EMWr9ju5AqHjcZPsSpTKutI=
|
||||
github.com/containerd/containerd/v2 v2.1.2 h1:4ZQxB+FVYmwXZgpBcKfar6ieppm3KC5C6FRKvtJ6DRU=
|
||||
github.com/containerd/containerd/v2 v2.1.2/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM=
|
||||
github.com/containerd/containerd/v2 v2.1.4 h1:/hXWjiSFd6ftrBOBGfAZ6T30LJcx1dBjdKEeI8xucKQ=
|
||||
github.com/containerd/containerd/v2 v2.1.4/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM=
|
||||
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=
|
||||
@@ -113,8 +111,9 @@ github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRq
|
||||
github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
|
||||
github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||
@@ -127,24 +126,24 @@ 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/cli v28.2.2+incompatible h1:qzx5BNUDFqlvyq4AHzdNB7gSyVTmU4cgsyN9SdInc1A=
|
||||
github.com/docker/cli v28.2.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/buildx v0.29.1 h1:58hxM5Z4mnNje3G5NKfULT9xCr8ooM8XFtlfUK9bKaA=
|
||||
github.com/docker/buildx v0.29.1/go.mod h1:J4EFv6oxlPiV1MjO0VyJx2u5tLM7ImDEl9zyB8d4wPI=
|
||||
github.com/docker/cli v28.5.1+incompatible h1:ESutzBALAD6qyCLqbQSEf1a/U8Ybms5agw59yGVc+yY=
|
||||
github.com/docker/cli v28.5.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli-docs-tool v0.10.0 h1:bOD6mKynPQgojQi3s2jgcUWGp/Ebqy1SeCr9VfKQLLU=
|
||||
github.com/docker/cli-docs-tool v0.10.0/go.mod h1:5EM5zPnT2E7yCLERZmrDA234Vwn09fzRHP4aX1qwp1U=
|
||||
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.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw=
|
||||
github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM=
|
||||
github.com/docker/docker v28.5.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=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
|
||||
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
|
||||
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
|
||||
@@ -171,8 +170,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=
|
||||
@@ -188,8 +187,8 @@ github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
|
||||
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
|
||||
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
@@ -317,8 +316,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.0 h1:HV+u7xM2IZhAjVautFR2l5FNhkxFR0jhF5ILXyc3398=
|
||||
github.com/moby/buildkit v0.23.0/go.mod h1:v5jMDvQgUyidk3wu3NvVAAd5JJo83nfet9Gf/o0+EAQ=
|
||||
github.com/moby/buildkit v0.25.1 h1:j7IlVkeNbEo+ZLoxdudYCHpmTsbwKvhgc/6UJ/mY/o8=
|
||||
github.com/moby/buildkit v0.25.1/go.mod h1:phM8sdqnvgK2y1dPDnbwI6veUCXHOZ6KFSl6E164tkc=
|
||||
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=
|
||||
@@ -445,13 +444,14 @@ github.com/spdx/tools-golang v0.5.5/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYec
|
||||
github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94 h1:JmfC365KywYwHB946TTiQWEb8kqPY+pybPLoGE9GgVk=
|
||||
github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||
github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
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.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/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=
|
||||
@@ -468,8 +468,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
|
||||
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
|
||||
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 h1:QB54BJwA6x8QU9nHY3xJSZR2kX9bgpZekRKGkLTmEXA=
|
||||
@@ -488,56 +488,51 @@ github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnn
|
||||
github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zclconf/go-cty v1.16.2 h1:LAJSwc3v81IRBZyUVQDUdZ7hs3SYs9jv0eZJDWHD/70=
|
||||
github.com/zclconf/go-cty v1.16.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
|
||||
github.com/zclconf/go-cty v1.17.0 h1:seZvECve6XX4tmnvRzWtJNHdscMtYEx5R7bnnVyd/d0=
|
||||
github.com/zclconf/go-cty v1.17.0/go.mod h1:wqFzcImaLTI6A5HfsRwB0nj5n0MRZFwmey8YoFPPs3U=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.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.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
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,13 +541,13 @@ 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=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@@ -562,10 +557,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=
|
||||
@@ -573,8 +568,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.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
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=
|
||||
@@ -596,19 +591,19 @@ 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.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
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=
|
||||
@@ -616,21 +611,21 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/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/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
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.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
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.v2 v2.2.1 h1:eJ9UAg01/HIHG987TwxvnzK2MgxXq97YY6rYDpY9aII=
|
||||
|
||||
@@ -1,62 +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"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
)
|
||||
|
||||
// engineLabelDesktopAddress is used to detect that Compose is running with a
|
||||
// Docker Desktop context. When this label is present, the value is an endpoint
|
||||
// address for an in-memory socket (AF_UNIX or named pipe).
|
||||
const engineLabelDesktopAddress = "com.docker.desktop.address"
|
||||
|
||||
// NewFromDockerClient creates a Desktop Client using the Docker CLI client to
|
||||
// auto-discover the Desktop CLI socket endpoint (if available).
|
||||
//
|
||||
// An error is returned if there is a failure communicating with Docker Desktop,
|
||||
// but even on success, a nil Client can be returned if the active Docker Engine
|
||||
// is not a Desktop instance.
|
||||
func NewFromDockerClient(ctx context.Context, dockerCli command.Cli) (*Client, error) {
|
||||
// safeguard to make sure this doesn't get stuck indefinitely
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second)
|
||||
defer cancel()
|
||||
|
||||
info, err := dockerCli.Client().Info(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("querying server info: %w", err)
|
||||
}
|
||||
for _, l := range info.Labels {
|
||||
k, v, ok := strings.Cut(l, "=")
|
||||
if !ok || k != engineLabelDesktopAddress {
|
||||
continue
|
||||
}
|
||||
|
||||
desktopCli := NewClient(v)
|
||||
_, err := desktopCli.Ping(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pinging Desktop API: %w", err)
|
||||
}
|
||||
return desktopCli, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build linux || openbsd
|
||||
//go:build linux || openbsd || freebsd
|
||||
|
||||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ocipush
|
||||
package oci
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -26,9 +26,9 @@ import (
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/remotes"
|
||||
pusherrors "github.com/containerd/containerd/v2/core/remotes/errors"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/buildx/util/imagetools"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/image-spec/specs-go"
|
||||
@@ -67,11 +67,6 @@ var clientAuthStatusCodes = []int{
|
||||
http.StatusProxyAuthRequired,
|
||||
}
|
||||
|
||||
type Pushable struct {
|
||||
Descriptor v1.Descriptor
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func DescriptorForComposeFile(path string, content []byte) v1.Descriptor {
|
||||
return v1.Descriptor{
|
||||
MediaType: ComposeYAMLMediaType,
|
||||
@@ -81,6 +76,7 @@ func DescriptorForComposeFile(path string, content []byte) v1.Descriptor {
|
||||
"com.docker.compose.version": api.ComposeVersion,
|
||||
"com.docker.compose.file": filepath.Base(path),
|
||||
},
|
||||
Data: content,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,28 +89,24 @@ func DescriptorForEnvFile(path string, content []byte) v1.Descriptor {
|
||||
"com.docker.compose.version": api.ComposeVersion,
|
||||
"com.docker.compose.envfile": filepath.Base(path),
|
||||
},
|
||||
Data: content,
|
||||
}
|
||||
}
|
||||
|
||||
func PushManifest(
|
||||
ctx context.Context,
|
||||
resolver *imagetools.Resolver,
|
||||
named reference.Named,
|
||||
layers []Pushable,
|
||||
ociVersion api.OCIVersion,
|
||||
) error {
|
||||
func PushManifest(ctx context.Context, resolver remotes.Resolver, named reference.Named, layers []v1.Descriptor, ociVersion api.OCIVersion) (v1.Descriptor, error) {
|
||||
// Check if we need an extra empty layer for the manifest config
|
||||
if ociVersion == api.OCIVersion1_1 || ociVersion == "" {
|
||||
if err := resolver.Push(ctx, named, v1.DescriptorEmptyJSON, v1.DescriptorEmptyJSON.Data); err != nil {
|
||||
return err
|
||||
err := push(ctx, resolver, named, v1.DescriptorEmptyJSON)
|
||||
if err != nil {
|
||||
return v1.Descriptor{}, err
|
||||
}
|
||||
}
|
||||
// prepare to push the manifest by pushing the layers
|
||||
layerDescriptors := make([]v1.Descriptor, len(layers))
|
||||
for i := range layers {
|
||||
layerDescriptors[i] = layers[i].Descriptor
|
||||
if err := resolver.Push(ctx, named, layers[i].Descriptor, layers[i].Data); err != nil {
|
||||
return err
|
||||
layerDescriptors[i] = layers[i]
|
||||
if err := push(ctx, resolver, named, layers[i]); err != nil {
|
||||
return v1.Descriptor{}, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,33 +118,36 @@ func PushManifest(
|
||||
// try to push in the OCI 1.1 format but fallback to OCI 1.0 on 4xx errors
|
||||
// (other than auth) since it's most likely the result of the registry not
|
||||
// having support
|
||||
err := createAndPushManifest(ctx, resolver, named, layerDescriptors, api.OCIVersion1_1)
|
||||
descriptor, err := createAndPushManifest(ctx, resolver, named, layerDescriptors, api.OCIVersion1_1)
|
||||
var pushErr pusherrors.ErrUnexpectedStatus
|
||||
if errors.As(err, &pushErr) && isNonAuthClientError(pushErr.StatusCode) {
|
||||
// TODO(milas): show a warning here (won't work with logrus)
|
||||
return createAndPushManifest(ctx, resolver, named, layerDescriptors, api.OCIVersion1_0)
|
||||
}
|
||||
return err
|
||||
return descriptor, err
|
||||
}
|
||||
|
||||
func createAndPushManifest(
|
||||
ctx context.Context,
|
||||
resolver *imagetools.Resolver,
|
||||
named reference.Named,
|
||||
layers []v1.Descriptor,
|
||||
ociVersion api.OCIVersion,
|
||||
) error {
|
||||
toPush, err := generateManifest(layers, ociVersion)
|
||||
func push(ctx context.Context, resolver remotes.Resolver, ref reference.Named, descriptor v1.Descriptor) error {
|
||||
fullRef, err := reference.WithDigest(reference.TagNameOnly(ref), descriptor.Digest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Push(ctx, resolver, fullRef, descriptor)
|
||||
}
|
||||
|
||||
func createAndPushManifest(ctx context.Context, resolver remotes.Resolver, named reference.Named, layers []v1.Descriptor, ociVersion api.OCIVersion) (v1.Descriptor, error) {
|
||||
descriptor, toPush, err := generateManifest(layers, ociVersion)
|
||||
if err != nil {
|
||||
return v1.Descriptor{}, err
|
||||
}
|
||||
for _, p := range toPush {
|
||||
err = resolver.Push(ctx, named, p.Descriptor, p.Data)
|
||||
err = push(ctx, resolver, named, p)
|
||||
if err != nil {
|
||||
return err
|
||||
return v1.Descriptor{}, err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return descriptor, nil
|
||||
}
|
||||
|
||||
func isNonAuthClientError(statusCode int) bool {
|
||||
@@ -163,8 +158,8 @@ func isNonAuthClientError(statusCode int) bool {
|
||||
return !slices.Contains(clientAuthStatusCodes, statusCode)
|
||||
}
|
||||
|
||||
func generateManifest(layers []v1.Descriptor, ociCompat api.OCIVersion) ([]Pushable, error) {
|
||||
var toPush []Pushable
|
||||
func generateManifest(layers []v1.Descriptor, ociCompat api.OCIVersion) (v1.Descriptor, []v1.Descriptor, error) {
|
||||
var toPush []v1.Descriptor
|
||||
var config v1.Descriptor
|
||||
var artifactType string
|
||||
switch ociCompat {
|
||||
@@ -184,18 +179,18 @@ func generateManifest(layers []v1.Descriptor, ociCompat api.OCIVersion) ([]Pusha
|
||||
MediaType: ComposeEmptyConfigMediaType,
|
||||
Digest: digest.FromBytes(configData),
|
||||
Size: int64(len(configData)),
|
||||
Data: configData,
|
||||
}
|
||||
// N.B. OCI 1.0 does NOT support specifying the artifact type, so it's
|
||||
// left as an empty string to omit it from the marshaled JSON
|
||||
artifactType = ""
|
||||
toPush = append(toPush, Pushable{Descriptor: config, Data: configData})
|
||||
toPush = append(toPush, config)
|
||||
case api.OCIVersion1_1:
|
||||
config = v1.DescriptorEmptyJSON
|
||||
artifactType = ComposeProjectArtifactType
|
||||
// N.B. the descriptor has the data embedded in it
|
||||
toPush = append(toPush, Pushable{Descriptor: config, Data: make([]byte, len(config.Data))})
|
||||
toPush = append(toPush, config)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported OCI version: %s", ociCompat)
|
||||
return v1.Descriptor{}, nil, fmt.Errorf("unsupported OCI version: %s", ociCompat)
|
||||
}
|
||||
|
||||
manifest, err := json.Marshal(v1.Manifest{
|
||||
@@ -209,7 +204,7 @@ func generateManifest(layers []v1.Descriptor, ociCompat api.OCIVersion) ([]Pusha
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return v1.Descriptor{}, nil, err
|
||||
}
|
||||
|
||||
manifestDescriptor := v1.Descriptor{
|
||||
@@ -220,7 +215,8 @@ func generateManifest(layers []v1.Descriptor, ociCompat api.OCIVersion) ([]Pusha
|
||||
"com.docker.compose.version": api.ComposeVersion,
|
||||
},
|
||||
ArtifactType: artifactType,
|
||||
Data: manifest,
|
||||
}
|
||||
toPush = append(toPush, Pushable{Descriptor: manifestDescriptor, Data: manifest})
|
||||
return toPush, nil
|
||||
toPush = append(toPush, manifestDescriptor)
|
||||
return manifestDescriptor, toPush, nil
|
||||
}
|
||||
137
internal/oci/resolver.go
Normal file
137
internal/oci/resolver.go
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
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 oci
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/remotes"
|
||||
"github.com/containerd/containerd/v2/core/remotes/docker"
|
||||
"github.com/containerd/containerd/v2/pkg/labels"
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/compose/v2/internal/registry"
|
||||
"github.com/moby/buildkit/util/contentutil"
|
||||
spec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// NewResolver setup an OCI Resolver based on docker/cli config to provide registry credentials
|
||||
func NewResolver(config *configfile.ConfigFile) remotes.Resolver {
|
||||
return docker.NewResolver(docker.ResolverOptions{
|
||||
Hosts: docker.ConfigureDefaultRegistries(
|
||||
docker.WithAuthorizer(docker.NewDockerAuthorizer(
|
||||
docker.WithAuthCreds(func(host string) (string, string, error) {
|
||||
host = registry.GetAuthConfigKey(host)
|
||||
auth, err := config.GetAuthConfig(host)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if auth.IdentityToken != "" {
|
||||
return "", auth.IdentityToken, nil
|
||||
}
|
||||
return auth.Username, auth.Password, nil
|
||||
}),
|
||||
)),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
// Get retrieves a Named OCI resource and returns OCI Descriptor and Manifest
|
||||
func Get(ctx context.Context, resolver remotes.Resolver, ref reference.Named) (spec.Descriptor, []byte, error) {
|
||||
_, descriptor, err := resolver.Resolve(ctx, ref.String())
|
||||
if err != nil {
|
||||
return spec.Descriptor{}, nil, err
|
||||
}
|
||||
|
||||
fetcher, err := resolver.Fetcher(ctx, ref.String())
|
||||
if err != nil {
|
||||
return spec.Descriptor{}, nil, err
|
||||
}
|
||||
fetch, err := fetcher.Fetch(ctx, descriptor)
|
||||
if err != nil {
|
||||
return spec.Descriptor{}, nil, err
|
||||
}
|
||||
content, err := io.ReadAll(fetch)
|
||||
if err != nil {
|
||||
return spec.Descriptor{}, nil, err
|
||||
}
|
||||
return descriptor, content, nil
|
||||
}
|
||||
|
||||
func Copy(ctx context.Context, resolver remotes.Resolver, image reference.Named, named reference.Named) (spec.Descriptor, error) {
|
||||
src, desc, err := resolver.Resolve(ctx, image.String())
|
||||
if err != nil {
|
||||
return spec.Descriptor{}, err
|
||||
}
|
||||
if desc.Annotations == nil {
|
||||
desc.Annotations = make(map[string]string)
|
||||
}
|
||||
// set LabelDistributionSource so push will actually use a registry mount
|
||||
refspec := reference.TrimNamed(image).String()
|
||||
u, err := url.Parse("dummy://" + refspec)
|
||||
if err != nil {
|
||||
return spec.Descriptor{}, err
|
||||
}
|
||||
source, repo := u.Hostname(), strings.TrimPrefix(u.Path, "/")
|
||||
desc.Annotations[labels.LabelDistributionSource+"."+source] = repo
|
||||
|
||||
p, err := resolver.Pusher(ctx, named.Name())
|
||||
if err != nil {
|
||||
return spec.Descriptor{}, err
|
||||
}
|
||||
f, err := resolver.Fetcher(ctx, src)
|
||||
if err != nil {
|
||||
return spec.Descriptor{}, err
|
||||
}
|
||||
|
||||
err = contentutil.CopyChain(ctx,
|
||||
contentutil.FromPusher(p),
|
||||
contentutil.FromFetcher(f), desc)
|
||||
return desc, err
|
||||
}
|
||||
|
||||
func Push(ctx context.Context, resolver remotes.Resolver, ref reference.Named, descriptor spec.Descriptor) error {
|
||||
pusher, err := resolver.Pusher(ctx, ref.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx = remotes.WithMediaTypeKeyPrefix(ctx, ComposeYAMLMediaType, "artifact-")
|
||||
ctx = remotes.WithMediaTypeKeyPrefix(ctx, ComposeEnvFileMediaType, "artifact-")
|
||||
ctx = remotes.WithMediaTypeKeyPrefix(ctx, ComposeEmptyConfigMediaType, "config-")
|
||||
ctx = remotes.WithMediaTypeKeyPrefix(ctx, spec.MediaTypeEmptyJSON, "config-")
|
||||
|
||||
push, err := pusher.Push(ctx, descriptor)
|
||||
if errdefs.IsAlreadyExists(err) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = push.Write(descriptor.Data)
|
||||
if err != nil {
|
||||
// Close the writer on error since Commit won't be called
|
||||
_ = push.Close()
|
||||
return err
|
||||
}
|
||||
// Commit will close the writer
|
||||
return push.Commit(ctx, int64(len(descriptor.Data)), descriptor.Digest)
|
||||
}
|
||||
44
internal/registry/registry.go
Normal file
44
internal/registry/registry.go
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
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 registry
|
||||
|
||||
const (
|
||||
// DefaultNamespace is the default namespace
|
||||
DefaultNamespace = "docker.io"
|
||||
// DefaultRegistryHost is the hostname for the default (Docker Hub) registry
|
||||
// used for pushing and pulling images. This hostname is hard-coded to handle
|
||||
// the conversion from image references without registry name (e.g. "ubuntu",
|
||||
// or "ubuntu:latest"), as well as references using the "docker.io" domain
|
||||
// name, which is used as canonical reference for images on Docker Hub, but
|
||||
// does not match the domain-name of Docker Hub's registry.
|
||||
DefaultRegistryHost = "registry-1.docker.io"
|
||||
// IndexHostname is the index hostname, used for authentication and image search.
|
||||
IndexHostname = "index.docker.io"
|
||||
// IndexServer is used for user auth and image search
|
||||
IndexServer = "https://" + IndexHostname + "/v1/"
|
||||
// IndexName is the name of the index
|
||||
IndexName = "docker.io"
|
||||
)
|
||||
|
||||
// GetAuthConfigKey special-cases using the full index address of the official
|
||||
// index as the AuthConfig key, and uses the (host)name[:port] for private indexes.
|
||||
func GetAuthConfigKey(indexName string) string {
|
||||
if indexName == IndexName || indexName == IndexHostname || indexName == DefaultRegistryHost {
|
||||
return IndexServer
|
||||
}
|
||||
return indexName
|
||||
}
|
||||
@@ -29,11 +29,11 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/moby/go-archive"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type archiveEntry struct {
|
||||
@@ -84,7 +84,14 @@ func (t *Tar) Sync(ctx context.Context, service string, paths []*PathMapping) er
|
||||
if len(pathsToDelete) != 0 {
|
||||
deleteCmd = append([]string{"rm", "-rf"}, pathsToDelete...)
|
||||
}
|
||||
var eg multierror.Group
|
||||
|
||||
var (
|
||||
eg errgroup.Group
|
||||
errMu sync.Mutex
|
||||
errs = make([]error, 0, len(containers)*2) // max 2 errs per container
|
||||
)
|
||||
|
||||
eg.SetLimit(16) // arbitrary limit, adjust to taste :D
|
||||
for i := range containers {
|
||||
containerID := containers[i].ID
|
||||
tarReader := tarArchive(pathsToCopy)
|
||||
@@ -92,17 +99,23 @@ func (t *Tar) Sync(ctx context.Context, service string, paths []*PathMapping) er
|
||||
eg.Go(func() error {
|
||||
if len(deleteCmd) != 0 {
|
||||
if err := t.client.Exec(ctx, containerID, deleteCmd, nil); err != nil {
|
||||
return fmt.Errorf("deleting paths in %s: %w", containerID, err)
|
||||
errMu.Lock()
|
||||
errs = append(errs, fmt.Errorf("deleting paths in %s: %w", containerID, err))
|
||||
errMu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
if err := t.client.Untar(ctx, containerID, tarReader); err != nil {
|
||||
return fmt.Errorf("copying files to %s: %w", containerID, err)
|
||||
errMu.Lock()
|
||||
errs = append(errs, fmt.Errorf("copying files to %s: %w", containerID, err))
|
||||
errMu.Unlock()
|
||||
}
|
||||
return nil
|
||||
return nil // don't fail-fast; collect all errors
|
||||
})
|
||||
}
|
||||
return eg.Wait().ErrorOrNil()
|
||||
|
||||
_ = eg.Wait()
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
type ArchiveBuilder struct {
|
||||
@@ -229,7 +242,7 @@ func (a *ArchiveBuilder) writeEntry(entry archiveEntry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// tarPath writes the given source path into tarWriter at the given dest (recursively for directories).
|
||||
// entriesForPath writes the given source path into tarWriter at the given dest (recursively for directories).
|
||||
// e.g. tarring my_dir --> dest d: d/file_a, d/file_b
|
||||
// If source path does not exist, quietly skips it and returns no err
|
||||
func (a *ArchiveBuilder) entriesForPath(localPath, containerPath string) ([]archiveEntry, error) {
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -22,15 +22,12 @@ import (
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
func KeyboardMetrics(ctx context.Context, enabled, isDockerDesktopActive, isWatchConfigured bool) {
|
||||
func KeyboardMetrics(ctx context.Context, enabled, isDockerDesktopActive bool) {
|
||||
commandAvailable := []string{}
|
||||
if isDockerDesktopActive {
|
||||
commandAvailable = append(commandAvailable, "gui")
|
||||
commandAvailable = append(commandAvailable, "gui/composeview")
|
||||
}
|
||||
if isWatchConfigured {
|
||||
commandAvailable = append(commandAvailable, "watch")
|
||||
}
|
||||
|
||||
AddAttributeToSpan(ctx,
|
||||
attribute.Bool("navmenu.enabled", enabled),
|
||||
|
||||
@@ -18,8 +18,9 @@ package tracing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
)
|
||||
|
||||
@@ -28,23 +29,45 @@ type MuxExporter struct {
|
||||
}
|
||||
|
||||
func (m MuxExporter) ExportSpans(ctx context.Context, spans []sdktrace.ReadOnlySpan) error {
|
||||
var eg multierror.Group
|
||||
for i := range m.exporters {
|
||||
exporter := m.exporters[i]
|
||||
eg.Go(func() error {
|
||||
return exporter.ExportSpans(ctx, spans)
|
||||
})
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
errMu sync.Mutex
|
||||
errs = make([]error, 0, len(m.exporters))
|
||||
)
|
||||
|
||||
for _, exporter := range m.exporters {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := exporter.ExportSpans(ctx, spans); err != nil {
|
||||
errMu.Lock()
|
||||
errs = append(errs, err)
|
||||
errMu.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
return eg.Wait()
|
||||
wg.Wait()
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func (m MuxExporter) Shutdown(ctx context.Context) error {
|
||||
var eg multierror.Group
|
||||
for i := range m.exporters {
|
||||
exporter := m.exporters[i]
|
||||
eg.Go(func() error {
|
||||
return exporter.Shutdown(ctx)
|
||||
})
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
errMu sync.Mutex
|
||||
errs = make([]error, 0, len(m.exporters))
|
||||
)
|
||||
|
||||
for _, exporter := range m.exporters {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := exporter.Shutdown(ctx); err != nil {
|
||||
errMu.Lock()
|
||||
errs = append(errs, err)
|
||||
errMu.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
return eg.Wait()
|
||||
wg.Wait()
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package api
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -26,10 +27,12 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/docker/cli/opts"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
)
|
||||
|
||||
// Service manages a compose project
|
||||
type Service interface {
|
||||
// Compose is the API interface one can use to programmatically use docker/compose in a third-party software
|
||||
// Use [compose.NewComposeService] to get an actual instance
|
||||
type Compose interface {
|
||||
// Build executes the equivalent to a `compose build`
|
||||
Build(ctx context.Context, project *types.Project, options BuildOptions) error
|
||||
// Push executes the equivalent to a `compose push`
|
||||
@@ -98,8 +101,16 @@ type Service interface {
|
||||
Commit(ctx context.Context, projectName string, options CommitOptions) error
|
||||
// Generate generates a Compose Project from existing containers
|
||||
Generate(ctx context.Context, options GenerateOptions) (*types.Project, error)
|
||||
// Volumes executes the equivalent to a `docker volume ls`
|
||||
Volumes(ctx context.Context, project string, options VolumesOptions) ([]VolumesSummary, error)
|
||||
}
|
||||
|
||||
type VolumesOptions struct {
|
||||
Services []string
|
||||
}
|
||||
|
||||
type VolumesSummary = *volume.Volume
|
||||
|
||||
type ScaleOptions struct {
|
||||
Services []string
|
||||
}
|
||||
@@ -161,8 +172,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
|
||||
@@ -389,6 +406,8 @@ type AttachOptions struct {
|
||||
type EventsOptions struct {
|
||||
Services []string
|
||||
Consumer func(event Event) error
|
||||
Since string
|
||||
Until string
|
||||
}
|
||||
|
||||
// Event is a container runtime event served by Events API
|
||||
@@ -426,9 +445,10 @@ const (
|
||||
// PublishOptions group options of the Publish API
|
||||
type PublishOptions struct {
|
||||
ResolveImageDigests bool
|
||||
Application bool
|
||||
WithEnvironment bool
|
||||
AssumeYes bool
|
||||
|
||||
AssumeYes bool
|
||||
OCIVersion OCIVersion
|
||||
}
|
||||
|
||||
@@ -540,6 +560,7 @@ type ImageSummary struct {
|
||||
Tag string
|
||||
Platform platforms.Platform
|
||||
Size int64
|
||||
Created time.Time
|
||||
LastTagTime time.Time
|
||||
}
|
||||
|
||||
@@ -638,7 +659,6 @@ type LogConsumer interface {
|
||||
Log(containerName, message string)
|
||||
Err(containerName, message string)
|
||||
Status(container, msg string)
|
||||
Register(container string)
|
||||
}
|
||||
|
||||
// ContainerEventListener is a callback to process ContainerEvent from services
|
||||
@@ -646,16 +666,18 @@ type ContainerEventListener func(event ContainerEvent)
|
||||
|
||||
// ContainerEvent notify an event has been collected on source container implementing Service
|
||||
type ContainerEvent struct {
|
||||
Type int
|
||||
// Container is the name of the container _without the project prefix_.
|
||||
Type int
|
||||
Time int64
|
||||
Container *ContainerSummary
|
||||
// Source is the name of the container _without the project prefix_.
|
||||
//
|
||||
// This is only suitable for display purposes within Compose, as it's
|
||||
// not guaranteed to be unique across services.
|
||||
Container string
|
||||
ID string
|
||||
Service string
|
||||
Line string
|
||||
// ContainerEventExit only
|
||||
Source string
|
||||
ID string
|
||||
Service string
|
||||
Line string
|
||||
// ExitCode is only set on ContainerEventExited events
|
||||
ExitCode int
|
||||
Restarting bool
|
||||
}
|
||||
@@ -665,17 +687,19 @@ const (
|
||||
ContainerEventLog = iota
|
||||
// ContainerEventErr is a ContainerEvent of type log on stderr. Line is set
|
||||
ContainerEventErr
|
||||
// ContainerEventAttach is a ContainerEvent of type attach. First event sent about a container
|
||||
ContainerEventAttach
|
||||
// ContainerEventStarted let consumer know a container has been started
|
||||
ContainerEventStarted
|
||||
// ContainerEventRestarted let consumer know a container has been restarted
|
||||
ContainerEventRestarted
|
||||
// ContainerEventStopped is a ContainerEvent of type stopped.
|
||||
ContainerEventStopped
|
||||
// ContainerEventCreated let consumer know a new container has been created
|
||||
ContainerEventCreated
|
||||
// ContainerEventRecreated let consumer know container stopped but his being replaced
|
||||
ContainerEventRecreated
|
||||
// ContainerEventExit is a ContainerEvent of type exit. ExitCode is set
|
||||
ContainerEventExit
|
||||
// ContainerEventExited is a ContainerEvent of type exit. ExitCode is set
|
||||
ContainerEventExited
|
||||
// UserCancel user cancelled compose up, we are stopping containers
|
||||
UserCancel
|
||||
// HookEventLog is a ContainerEvent of type log on stdout by service hook
|
||||
HookEventLog
|
||||
)
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ func (d *DryRunClient) ContainerInspect(ctx context.Context, container string) (
|
||||
ID: id,
|
||||
Name: container,
|
||||
State: &containerType.State{
|
||||
Status: "running", // needed for --wait option
|
||||
Status: containerType.StateRunning, // needed for --wait option
|
||||
Health: &containerType.Health{
|
||||
Status: containerType.Healthy, // needed for healthcheck control
|
||||
},
|
||||
@@ -218,8 +218,7 @@ func (d *DryRunClient) ImageBuild(ctx context.Context, reader io.Reader, options
|
||||
rc := io.NopCloser(bytes.NewReader(jsonMessage))
|
||||
|
||||
return build.ImageBuildResponse{
|
||||
Body: rc,
|
||||
OSType: "",
|
||||
Body: rc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-version"
|
||||
|
||||
"github.com/docker/compose/v2/internal"
|
||||
@@ -65,9 +63,6 @@ var ComposeVersion string
|
||||
func init() {
|
||||
v, err := version.NewVersion(internal.Version)
|
||||
if err == nil {
|
||||
segments := v.Segments()
|
||||
if len(segments) > 2 {
|
||||
ComposeVersion = fmt.Sprintf("%d.%d.%d", segments[0], segments[1], segments[2])
|
||||
}
|
||||
ComposeVersion = v.Core().String()
|
||||
}
|
||||
}
|
||||
|
||||
35
pkg/api/labels_test.go
Normal file
35
pkg/api/labels_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/compose/v2/internal"
|
||||
"github.com/hashicorp/go-version"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestComposeVersionInitialization(t *testing.T) {
|
||||
v, err := version.NewVersion(internal.Version)
|
||||
if err != nil {
|
||||
assert.Equal(t, "", ComposeVersion, "ComposeVersion should be empty for a non-semver internal version (e.g. 'devel')")
|
||||
} else {
|
||||
expected := v.Core().String()
|
||||
assert.Equal(t, expected, ComposeVersion, "ComposeVersion should be the core of internal.Version")
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -33,6 +33,8 @@ import (
|
||||
const (
|
||||
TransformerLabel = "com.docker.compose.bridge"
|
||||
DefaultTransformerImage = "docker/compose-bridge-kubernetes"
|
||||
|
||||
templatesPath = "/templates"
|
||||
)
|
||||
|
||||
type CreateTransformerOptions struct {
|
||||
@@ -73,7 +75,7 @@ func CreateTransformer(ctx context.Context, dockerCli command.Cli, options Creat
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
content, stat, err := dockerCli.Client().CopyFromContainer(ctx, created.ID, "/templates")
|
||||
content, stat, err := dockerCli.Client().CopyFromContainer(ctx, created.ID, templatesPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -82,7 +84,7 @@ func CreateTransformer(ctx context.Context, dockerCli command.Cli, options Creat
|
||||
}()
|
||||
|
||||
srcInfo := archive.CopyInfo{
|
||||
Path: "/templates",
|
||||
Path: templatesPath,
|
||||
Exists: true,
|
||||
IsDir: stat.Mode.IsDir(),
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ package compose
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
@@ -41,11 +41,9 @@ func (s *composeService) useAPISocket(project *types.Project) (*types.Project, e
|
||||
return project, nil
|
||||
}
|
||||
|
||||
socket := s.dockerCli.DockerEndpoint().Host
|
||||
if !strings.HasPrefix(socket, "unix://") {
|
||||
return nil, fmt.Errorf("use_api_socket can only be used with unix sockets: docker endpoint %s is incompatible", socket)
|
||||
if s.dockerCli.ServerInfo().OSType == "windows" {
|
||||
return nil, errors.New("use_api_socket can't be used with a Windows Docker Engine")
|
||||
}
|
||||
socket = strings.TrimPrefix(socket, "unix://") // should we confirm absolute path?
|
||||
|
||||
creds, err := s.dockerCli.ConfigFile().GetAllCredentials()
|
||||
if err != nil {
|
||||
@@ -64,9 +62,12 @@ func (s *composeService) useAPISocket(project *types.Project) (*types.Project, e
|
||||
}
|
||||
|
||||
for name, service := range project.Services {
|
||||
if !service.UseAPISocket {
|
||||
continue
|
||||
}
|
||||
service.Volumes = append(service.Volumes, types.ServiceVolumeConfig{
|
||||
Type: types.VolumeTypeBind,
|
||||
Source: socket,
|
||||
Source: "/var/run/docker.sock",
|
||||
Target: "/var/run/docker.sock",
|
||||
})
|
||||
|
||||
|
||||
@@ -61,41 +61,37 @@ func (s *composeService) attach(ctx context.Context, project *types.Project, lis
|
||||
}
|
||||
|
||||
func (s *composeService) attachContainer(ctx context.Context, container containerType.Summary, listener api.ContainerEventListener) error {
|
||||
serviceName := container.Labels[api.ServiceLabel]
|
||||
containerName := getContainerNameWithoutProject(container)
|
||||
service := container.Labels[api.ServiceLabel]
|
||||
name := getContainerNameWithoutProject(container)
|
||||
return s.doAttachContainer(ctx, service, container.ID, name, listener)
|
||||
}
|
||||
|
||||
listener(api.ContainerEvent{
|
||||
Type: api.ContainerEventAttach,
|
||||
Container: containerName,
|
||||
ID: container.ID,
|
||||
Service: serviceName,
|
||||
})
|
||||
|
||||
wOut := utils.GetWriter(func(line string) {
|
||||
listener(api.ContainerEvent{
|
||||
Type: api.ContainerEventLog,
|
||||
Container: containerName,
|
||||
ID: container.ID,
|
||||
Service: serviceName,
|
||||
Line: line,
|
||||
})
|
||||
})
|
||||
wErr := utils.GetWriter(func(line string) {
|
||||
listener(api.ContainerEvent{
|
||||
Type: api.ContainerEventErr,
|
||||
Container: containerName,
|
||||
ID: container.ID,
|
||||
Service: serviceName,
|
||||
Line: line,
|
||||
})
|
||||
})
|
||||
|
||||
inspect, err := s.apiClient().ContainerInspect(ctx, container.ID)
|
||||
func (s *composeService) doAttachContainer(ctx context.Context, service, id, name string, listener api.ContainerEventListener) error {
|
||||
inspect, err := s.apiClient().ContainerInspect(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _, err = s.attachContainerStreams(ctx, container.ID, inspect.Config.Tty, nil, wOut, wErr)
|
||||
wOut := utils.GetWriter(func(line string) {
|
||||
listener(api.ContainerEvent{
|
||||
Type: api.ContainerEventLog,
|
||||
Source: name,
|
||||
ID: id,
|
||||
Service: service,
|
||||
Line: line,
|
||||
})
|
||||
})
|
||||
wErr := utils.GetWriter(func(line string) {
|
||||
listener(api.ContainerEvent{
|
||||
Type: api.ContainerEventErr,
|
||||
Source: name,
|
||||
ID: id,
|
||||
Service: service,
|
||||
Line: line,
|
||||
})
|
||||
})
|
||||
|
||||
_, _, err = s.attachContainerStreams(ctx, id, inspect.Config.Tty, nil, wOut, wErr)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -78,16 +79,19 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
|
||||
policy = types.IncludeDependencies
|
||||
}
|
||||
|
||||
var err 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)
|
||||
// Some build dependencies we just introduced may not be enabled
|
||||
project, err = project.WithServicesEnabled(options.Services...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(options.Services) == 0 {
|
||||
options.Services = project.ServiceNames()
|
||||
}
|
||||
|
||||
// also include services used as additional_contexts with service: prefix
|
||||
options.Services = addBuildDependencies(options.Services, project)
|
||||
// Some build dependencies we just introduced may not be enabled
|
||||
var err error
|
||||
project, err = project.WithServicesEnabled(options.Services...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
project, err = project.WithSelectedServices(options.Services)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -172,7 +176,7 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
|
||||
if options.Quiet {
|
||||
options.Progress = progress.ModeQuiet
|
||||
}
|
||||
if options.Progress == "" {
|
||||
if options.Progress == progress.ModeAuto {
|
||||
options.Progress = os.Getenv("BUILDKIT_PROGRESS")
|
||||
}
|
||||
w, err = xprogress.NewPrinter(progressCtx, os.Stdout, progressui.DisplayMode(options.Progress),
|
||||
@@ -397,6 +401,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 +476,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 +518,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) {
|
||||
|
||||
@@ -20,10 +20,12 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha1"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -32,20 +34,19 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli-plugins/socket"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/image/build"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/builder/remotecontext/urlutil"
|
||||
"github.com/google/uuid"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/util/gitutil"
|
||||
gitutil "github.com/moby/buildkit/frontend/dockerfile/dfgitutil"
|
||||
"github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
@@ -59,6 +60,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
|
||||
}
|
||||
|
||||
@@ -73,7 +77,7 @@ func buildWithBake(dockerCli command.Cli) (bool, error) {
|
||||
|
||||
_, err = manager.GetPlugin("buildx", dockerCli, &cobra.Command{})
|
||||
if err != nil {
|
||||
if manager.IsNotFound(err) {
|
||||
if errdefs.IsNotFound(err) {
|
||||
logrus.Warnf("Docker Compose is configured to build using Bake, but buildx isn't installed")
|
||||
return false, nil
|
||||
}
|
||||
@@ -116,6 +120,7 @@ type bakeTarget struct {
|
||||
Entitlements []string `json:"entitlements,omitempty"`
|
||||
ExtraHosts map[string]string `json:"extra-hosts,omitempty"`
|
||||
Outputs []string `json:"output,omitempty"`
|
||||
Attest []string `json:"attest,omitempty"`
|
||||
}
|
||||
|
||||
type bakeMetadata map[string]buildStatus
|
||||
@@ -128,7 +133,18 @@ 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))
|
||||
if options.Progress == progress.ModeAuto {
|
||||
options.Progress = os.Getenv("BUILDKIT_PROGRESS")
|
||||
}
|
||||
displayMode := progressui.DisplayMode(options.Progress)
|
||||
out := options.Out
|
||||
if out == nil {
|
||||
if displayMode == progress.ModeAuto && !s.dockerCli.Out().IsTerminal() {
|
||||
displayMode = progressui.PlainMode
|
||||
}
|
||||
out = os.Stdout // should be s.dockerCli.Out(), but NewDisplay require access to the underlying *File
|
||||
}
|
||||
display, err := progressui.NewDisplay(out, displayMode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -142,10 +158,11 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
|
||||
Targets: map[string]bakeTarget{},
|
||||
}
|
||||
var (
|
||||
group bakeGroup
|
||||
privileged bool
|
||||
read []string
|
||||
targets = make(map[string]string, len(serviceToBeBuild)) // service name -> build target
|
||||
group bakeGroup
|
||||
privileged bool
|
||||
read []string
|
||||
expectedImages = make(map[string]string, len(serviceToBeBuild)) // service name -> expected image
|
||||
targets = make(map[string]string, len(serviceToBeBuild)) // service name -> build target
|
||||
)
|
||||
|
||||
// produce a unique ID for service used as bake target
|
||||
@@ -160,18 +177,17 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
|
||||
}
|
||||
}
|
||||
|
||||
var secretsEnv []string
|
||||
for serviceName, service := range project.Services {
|
||||
if service.Build == nil {
|
||||
continue
|
||||
}
|
||||
build := *service.Build
|
||||
labels := getImageBuildLabels(project, service)
|
||||
|
||||
args := types.Mapping{}
|
||||
for k, v := range resolveAndMergeBuildArgs(s.dockerCli, project, service, options) {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
args[k] = *v
|
||||
args := resolveAndMergeBuildArgs(s.dockerCli, project, service, options).ToMapping()
|
||||
for k, v := range args {
|
||||
args[k] = strings.ReplaceAll(v, "${", "$${")
|
||||
}
|
||||
|
||||
entitlements := build.Entitlements
|
||||
@@ -192,35 +208,50 @@ 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)
|
||||
for _, path := range build.AdditionalContexts {
|
||||
_, err := gitutil.ParseGitRef(path)
|
||||
_, _, err := gitutil.ParseGitRef(path)
|
||||
if !strings.Contains(path, "://") && err != nil {
|
||||
read = append(read, path)
|
||||
}
|
||||
}
|
||||
|
||||
image := api.GetImageNameOrDefault(service, project.Name)
|
||||
expectedImages[serviceName] = image
|
||||
|
||||
pull := service.Build.Pull || options.Pull
|
||||
noCache := service.Build.NoCache || options.NoCache
|
||||
|
||||
target := targets[serviceName]
|
||||
|
||||
secrets, env := toBakeSecrets(project, build.Secrets)
|
||||
secretsEnv = append(secretsEnv, env...)
|
||||
|
||||
cfg.Targets[target] = bakeTarget{
|
||||
Context: build.Context,
|
||||
Contexts: additionalContexts(build.AdditionalContexts, targets),
|
||||
Dockerfile: dockerFilePath(build.Context, build.Dockerfile),
|
||||
DockerfileInline: strings.ReplaceAll(build.DockerfileInline, "${", "$${"),
|
||||
Args: args,
|
||||
Labels: build.Labels,
|
||||
Tags: append(build.Tags, api.GetImageNameOrDefault(service, project.Name)),
|
||||
Labels: labels,
|
||||
Tags: append(build.Tags, image),
|
||||
|
||||
CacheFrom: build.CacheFrom,
|
||||
// CacheTo: TODO
|
||||
CacheFrom: build.CacheFrom,
|
||||
CacheTo: build.CacheTo,
|
||||
NetworkMode: build.Network,
|
||||
Platforms: build.Platforms,
|
||||
Target: build.Target,
|
||||
Secrets: toBakeSecrets(project, build.Secrets),
|
||||
Secrets: secrets,
|
||||
SSH: toBakeSSH(append(build.SSH, options.SSHs...)),
|
||||
Pull: options.Pull,
|
||||
NoCache: options.NoCache,
|
||||
Pull: pull,
|
||||
NoCache: noCache,
|
||||
ShmSize: build.ShmSize,
|
||||
Ulimits: toBakeUlimits(build.Ulimits),
|
||||
Entitlements: entitlements,
|
||||
@@ -228,6 +259,7 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
|
||||
|
||||
Outputs: outputs,
|
||||
Call: call,
|
||||
Attest: toBakeAttest(build),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,13 +284,20 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
|
||||
}
|
||||
logrus.Debugf("bake build config:\n%s", string(b))
|
||||
|
||||
tmpdir := os.TempDir()
|
||||
var metadataFile string
|
||||
for {
|
||||
// we don't use os.CreateTemp here as we need a temporary file name, but don't want it actually created
|
||||
// as bake relies on atomicwriter and this creates conflict during rename
|
||||
metadataFile = filepath.Join(os.TempDir(), fmt.Sprintf("compose-build-metadataFile-%d.json", rand.Int31()))
|
||||
if _, err = os.Stat(metadataFile); os.IsNotExist(err) {
|
||||
break
|
||||
metadataFile = filepath.Join(tmpdir, fmt.Sprintf("compose-build-metadataFile-%s.json", uuid.New().String()))
|
||||
if _, err = os.Stat(metadataFile); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
break
|
||||
}
|
||||
var pathError *fs.PathError
|
||||
if errors.As(err, &pathError) {
|
||||
return nil, fmt.Errorf("can't acces os.tempDir %s: %w", tmpdir, pathError.Err)
|
||||
}
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
@@ -270,16 +309,23 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if versions.LessThan(buildx.Version[1:], "0.17.0") {
|
||||
return nil, fmt.Errorf("compose build requires buildx 0.17 or later")
|
||||
}
|
||||
|
||||
args := []string{"bake", "--file", "-", "--progress", "rawjson", "--metadata-file", metadataFile}
|
||||
mustAllow := buildx.Version != "" && versions.GreaterThanOrEqualTo(buildx.Version[1:], "0.17.0")
|
||||
if mustAllow {
|
||||
// FIXME we should prompt user about this, but this is a breaking change in UX
|
||||
for _, path := range read {
|
||||
args = append(args, "--allow", "fs.read="+path)
|
||||
}
|
||||
if privileged {
|
||||
args = append(args, "--allow", "security.insecure")
|
||||
}
|
||||
// FIXME we should prompt user about this, but this is a breaking change in UX
|
||||
for _, path := range read {
|
||||
args = append(args, "--allow", "fs.read="+path)
|
||||
}
|
||||
if privileged {
|
||||
args = append(args, "--allow", "security.insecure")
|
||||
}
|
||||
if options.SBOM != "" {
|
||||
args = append(args, "--sbom="+options.SBOM)
|
||||
}
|
||||
if options.Provenance != "" {
|
||||
args = append(args, "--provenance="+options.Provenance)
|
||||
}
|
||||
|
||||
if options.Builder != "" {
|
||||
@@ -291,23 +337,22 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
|
||||
|
||||
logrus.Debugf("Executing bake with args: %v", args)
|
||||
|
||||
cmd := exec.CommandContext(ctx, buildx.Path, args...)
|
||||
// Remove DOCKER_CLI_PLUGIN... variable so buildx can detect it run standalone
|
||||
cmd.Env = filter(os.Environ(), manager.ReexecEnvvar)
|
||||
|
||||
// Use docker/cli mechanism to propagate termination signal to child process
|
||||
server, err := socket.NewPluginServer(nil)
|
||||
if err == nil {
|
||||
defer server.Close() //nolint:errcheck
|
||||
cmd.Env = replace(cmd.Env, socket.EnvKey, server.Addr().String())
|
||||
if s.dryRun {
|
||||
return dryRunBake(ctx, cfg), nil
|
||||
}
|
||||
cmd := exec.CommandContext(ctx, buildx.Path, args...)
|
||||
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("DOCKER_CONTEXT=%s", s.dockerCli.CurrentContext()))
|
||||
|
||||
// propagate opentelemetry context to child process, see https://github.com/open-telemetry/oteps/blob/main/text/0258-env-context-baggage-carriers.md
|
||||
carrier := propagation.MapCarrier{}
|
||||
otel.GetTextMapPropagator().Inject(ctx, &carrier)
|
||||
cmd.Env = append(cmd.Env, types.Mapping(carrier).Values()...)
|
||||
err = s.prepareShellOut(ctx, types.NewMapping(os.Environ()), cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
endpoint, cleanup, err := s.propagateDockerEndpoint()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd.Env = append(cmd.Env, endpoint...)
|
||||
cmd.Env = append(cmd.Env, secretsEnv...)
|
||||
defer cleanup()
|
||||
|
||||
cmd.Stdout = s.stdout()
|
||||
cmd.Stdin = bytes.NewBuffer(b)
|
||||
@@ -317,16 +362,25 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
|
||||
}
|
||||
|
||||
var errMessage []string
|
||||
scanner := bufio.NewScanner(pipe)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
reader := bufio.NewReader(pipe)
|
||||
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eg.Go(cmd.Wait)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
for {
|
||||
line, readErr := reader.ReadString('\n')
|
||||
if readErr != nil {
|
||||
if readErr == io.EOF {
|
||||
break
|
||||
}
|
||||
if errors.Is(readErr, os.ErrClosed) {
|
||||
logrus.Debugf("bake stopped")
|
||||
break
|
||||
}
|
||||
return nil, fmt.Errorf("failed to execute bake: %w", readErr)
|
||||
}
|
||||
decoder := json.NewDecoder(strings.NewReader(line))
|
||||
var status client.SolveStatus
|
||||
err := decoder.Decode(&status)
|
||||
@@ -364,13 +418,14 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
|
||||
cw := progress.ContextWriter(ctx)
|
||||
results := map[string]string{}
|
||||
for name := range serviceToBeBuild {
|
||||
image := expectedImages[name]
|
||||
target := targets[name]
|
||||
built, ok := md[target]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("build result not found in Bake metadata for service %s", name)
|
||||
}
|
||||
results[name] = built.Digest
|
||||
cw.Event(progress.BuiltEvent(name))
|
||||
results[image] = built.Digest
|
||||
cw.Event(progress.BuiltEvent(image))
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
@@ -414,8 +469,9 @@ func toBakeSSH(ssh types.SSHConfig) []string {
|
||||
return s
|
||||
}
|
||||
|
||||
func toBakeSecrets(project *types.Project, secrets []types.ServiceSecretConfig) []string {
|
||||
func toBakeSecrets(project *types.Project, secrets []types.ServiceSecretConfig) ([]string, []string) {
|
||||
var s []string
|
||||
var env []string
|
||||
for _, ref := range secrets {
|
||||
def := project.Secrets[ref.Source]
|
||||
target := ref.Target
|
||||
@@ -424,43 +480,80 @@ func toBakeSecrets(project *types.Project, secrets []types.ServiceSecretConfig)
|
||||
}
|
||||
switch {
|
||||
case def.Environment != "":
|
||||
env = append(env, fmt.Sprintf("%s=%s", def.Environment, project.Environment[def.Environment]))
|
||||
s = append(s, fmt.Sprintf("id=%s,type=env,env=%s", target, def.Environment))
|
||||
case def.File != "":
|
||||
s = append(s, fmt.Sprintf("id=%s,type=file,src=%s", target, def.File))
|
||||
}
|
||||
}
|
||||
return s
|
||||
return s, env
|
||||
}
|
||||
|
||||
func filter(environ []string, variable string) []string {
|
||||
prefix := variable + "="
|
||||
filtered := make([]string, 0, len(environ))
|
||||
for _, val := range environ {
|
||||
if !strings.HasPrefix(val, prefix) {
|
||||
filtered = append(filtered, val)
|
||||
func toBakeAttest(build types.BuildConfig) []string {
|
||||
var attests []string
|
||||
|
||||
// Handle per-service provenance configuration (only from build config, not global options)
|
||||
if build.Provenance != "" {
|
||||
if build.Provenance == "true" {
|
||||
attests = append(attests, "type=provenance")
|
||||
} else if build.Provenance != "false" {
|
||||
attests = append(attests, fmt.Sprintf("type=provenance,%s", build.Provenance))
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
func replace(environ []string, variable, value string) []string {
|
||||
filtered := filter(environ, variable)
|
||||
return append(filtered, fmt.Sprintf("%s=%s", variable, value))
|
||||
// Handle per-service SBOM configuration (only from build config, not global options)
|
||||
if build.SBOM != "" {
|
||||
if build.SBOM == "true" {
|
||||
attests = append(attests, "type=sbom")
|
||||
} else if build.SBOM != "false" {
|
||||
attests = append(attests, fmt.Sprintf("type=sbom,%s", build.SBOM))
|
||||
}
|
||||
}
|
||||
|
||||
return attests
|
||||
}
|
||||
|
||||
func dockerFilePath(ctxName string, dockerfile string) string {
|
||||
if dockerfile == "" {
|
||||
return ""
|
||||
}
|
||||
if urlutil.IsGitURL(ctxName) {
|
||||
if contextType, _ := build.DetectContextType(ctxName); contextType == build.ContextTypeGit {
|
||||
return dockerfile
|
||||
}
|
||||
if !filepath.IsAbs(dockerfile) {
|
||||
dockerfile = filepath.Join(ctxName, dockerfile)
|
||||
}
|
||||
symlinks, err := filepath.EvalSymlinks(dockerfile)
|
||||
dir := filepath.Dir(dockerfile)
|
||||
symlinks, err := filepath.EvalSymlinks(dir)
|
||||
if err == nil {
|
||||
return symlinks
|
||||
return filepath.Join(symlinks, filepath.Base(dockerfile))
|
||||
}
|
||||
return dockerfile
|
||||
}
|
||||
|
||||
func dryRunBake(ctx context.Context, cfg bakeConfig) map[string]string {
|
||||
w := progress.ContextWriter(ctx)
|
||||
bakeResponse := map[string]string{}
|
||||
for name, target := range cfg.Targets {
|
||||
dryRunUUID := fmt.Sprintf("dryRun-%x", sha1.Sum([]byte(name)))
|
||||
displayDryRunBuildEvent(w, name, dryRunUUID, target.Tags[0])
|
||||
bakeResponse[name] = dryRunUUID
|
||||
}
|
||||
for name := range bakeResponse {
|
||||
w.Event(progress.BuiltEvent(name))
|
||||
}
|
||||
return bakeResponse
|
||||
}
|
||||
|
||||
func displayDryRunBuildEvent(w progress.Writer, name string, dryRunUUID, tag string) {
|
||||
w.Event(progress.Event{
|
||||
ID: name + " ==>",
|
||||
Status: progress.Done,
|
||||
Text: fmt.Sprintf("==> writing image %s", dryRunUUID),
|
||||
})
|
||||
w.Event(progress.Event{
|
||||
ID: name + " ==> ==>",
|
||||
Status: progress.Done,
|
||||
Text: fmt.Sprintf(`naming to %s`, tag),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -70,16 +70,7 @@ func (s composeService) dryRunBuildResponse(ctx context.Context, name string, op
|
||||
w := progress.ContextWriter(ctx)
|
||||
buildResponse := map[string]*client.SolveResponse{}
|
||||
dryRunUUID := fmt.Sprintf("dryRun-%x", sha1.Sum([]byte(name)))
|
||||
w.Event(progress.Event{
|
||||
ID: "==>",
|
||||
Status: progress.Done,
|
||||
Text: fmt.Sprintf("==> writing image %s", dryRunUUID),
|
||||
})
|
||||
w.Event(progress.Event{
|
||||
ID: "==> ==>",
|
||||
Status: progress.Done,
|
||||
Text: fmt.Sprintf(`naming to %s`, options.Tags[0]),
|
||||
})
|
||||
displayDryRunBuildEvent(w, name, dryRunUUID, options.Tags[0])
|
||||
buildResponse[name] = &client.SolveResponse{ExporterResponse: map[string]string{
|
||||
"containerimage.digest": dryRunUUID,
|
||||
}}
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
@@ -35,7 +34,6 @@ import (
|
||||
buildtypes "github.com/docker/docker/api/types/build"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/builder/remotecontext/urlutil"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
@@ -49,17 +47,9 @@ func (s *composeService) doBuildClassic(ctx context.Context, project *types.Proj
|
||||
buildCtx io.ReadCloser
|
||||
dockerfileCtx io.ReadCloser
|
||||
contextDir string
|
||||
tempDir string
|
||||
relDockerfile string
|
||||
|
||||
err error
|
||||
)
|
||||
|
||||
dockerfileName := dockerFilePath(service.Build.Context, service.Build.Dockerfile)
|
||||
specifiedContext := service.Build.Context
|
||||
progBuff := s.stdout()
|
||||
buildBuff := s.stdout()
|
||||
|
||||
if len(service.Build.Platforms) > 1 {
|
||||
return "", fmt.Errorf("the classic builder doesn't support multi-arch build, set DOCKER_BUILDKIT=1 to use BuildKit")
|
||||
}
|
||||
@@ -81,34 +71,51 @@ func (s *composeService) doBuildClassic(ctx context.Context, project *types.Proj
|
||||
}
|
||||
service.Build.Labels[api.ImageBuilderLabel] = "classic"
|
||||
|
||||
switch {
|
||||
case isLocalDir(specifiedContext):
|
||||
dockerfileName := dockerFilePath(service.Build.Context, service.Build.Dockerfile)
|
||||
specifiedContext := service.Build.Context
|
||||
progBuff := s.stdout()
|
||||
buildBuff := s.stdout()
|
||||
|
||||
contextType, err := build.DetectContextType(specifiedContext)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
switch contextType {
|
||||
case build.ContextTypeStdin:
|
||||
return "", fmt.Errorf("building from STDIN is not supported")
|
||||
case build.ContextTypeLocal:
|
||||
contextDir, relDockerfile, err = build.GetContextFromLocalDir(specifiedContext, dockerfileName)
|
||||
if err == nil && strings.HasPrefix(relDockerfile, ".."+string(filepath.Separator)) {
|
||||
// Dockerfile is outside of build-context; read the Dockerfile and pass it as dockerfileCtx
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to prepare context: %w", err)
|
||||
}
|
||||
if strings.HasPrefix(relDockerfile, ".."+string(filepath.Separator)) {
|
||||
// Dockerfile is outside build-context; read the Dockerfile and pass it as dockerfileCtx
|
||||
dockerfileCtx, err = os.Open(dockerfileName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to open Dockerfile: %w", err)
|
||||
}
|
||||
defer dockerfileCtx.Close() //nolint:errcheck
|
||||
}
|
||||
case urlutil.IsGitURL(specifiedContext):
|
||||
case build.ContextTypeGit:
|
||||
var tempDir string
|
||||
tempDir, relDockerfile, err = build.GetContextFromGitURL(specifiedContext, dockerfileName)
|
||||
case urlutil.IsURL(specifiedContext):
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to prepare context: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = os.RemoveAll(tempDir)
|
||||
}()
|
||||
contextDir = tempDir
|
||||
case build.ContextTypeRemote:
|
||||
buildCtx, relDockerfile, err = build.GetContextFromURL(progBuff, specifiedContext, dockerfileName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to prepare context: %w", err)
|
||||
}
|
||||
default:
|
||||
return "", fmt.Errorf("unable to prepare context: path %q not found", specifiedContext)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to prepare context: %w", err)
|
||||
}
|
||||
|
||||
if tempDir != "" {
|
||||
defer os.RemoveAll(tempDir) //nolint:errcheck
|
||||
contextDir = tempDir
|
||||
}
|
||||
|
||||
// read from a directory into tar archive
|
||||
if buildCtx == nil {
|
||||
excludes, err := build.ReadDockerignore(contextDir)
|
||||
@@ -126,7 +133,7 @@ func (s *composeService) doBuildClassic(ctx context.Context, project *types.Proj
|
||||
excludes = build.TrimBuildFilesFromExcludes(excludes, relDockerfile, false)
|
||||
buildCtx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
|
||||
ExcludePatterns: excludes,
|
||||
ChownOpts: &archive.ChownOpts{},
|
||||
ChownOpts: &archive.ChownOpts{UID: 0, GID: 0},
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -146,6 +153,7 @@ func (s *composeService) doBuildClassic(ctx context.Context, project *types.Proj
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Setup an upload progress bar
|
||||
progressOutput := streamformatter.NewProgressOutput(progBuff)
|
||||
body := progress.NewProgressReader(buildCtx, progressOutput, 0, "", "Sending build context to Docker daemon")
|
||||
|
||||
@@ -155,19 +163,28 @@ func (s *composeService) doBuildClassic(ctx context.Context, project *types.Proj
|
||||
return "", err
|
||||
}
|
||||
authConfigs := make(map[string]registry.AuthConfig, len(creds))
|
||||
for k, auth := range creds {
|
||||
authConfigs[k] = registry.AuthConfig(auth)
|
||||
for k, authConfig := range creds {
|
||||
authConfigs[k] = registry.AuthConfig{
|
||||
Username: authConfig.Username,
|
||||
Password: authConfig.Password,
|
||||
ServerAddress: authConfig.ServerAddress,
|
||||
|
||||
// TODO(thaJeztah): Are these expected to be included? See https://github.com/docker/cli/pull/6516#discussion_r2387586472
|
||||
Auth: authConfig.Auth,
|
||||
IdentityToken: authConfig.IdentityToken,
|
||||
RegistryToken: authConfig.RegistryToken,
|
||||
}
|
||||
}
|
||||
buildOptions := imageBuildOptions(s.dockerCli, project, service, options)
|
||||
buildOpts := imageBuildOptions(s.dockerCli, project, service, options)
|
||||
imageName := api.GetImageNameOrDefault(service, project.Name)
|
||||
buildOptions.Tags = append(buildOptions.Tags, imageName)
|
||||
buildOptions.Dockerfile = relDockerfile
|
||||
buildOptions.AuthConfigs = authConfigs
|
||||
buildOptions.Memory = options.Memory
|
||||
buildOpts.Tags = append(buildOpts.Tags, imageName)
|
||||
buildOpts.Dockerfile = relDockerfile
|
||||
buildOpts.AuthConfigs = authConfigs
|
||||
buildOpts.Memory = options.Memory
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
response, err := s.apiClient().ImageBuild(ctx, body, buildOptions)
|
||||
response, err := s.apiClient().ImageBuild(ctx, body, buildOpts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -195,26 +212,9 @@ func (s *composeService) doBuildClassic(ctx context.Context, project *types.Proj
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Windows: show error message about modified file permissions if the
|
||||
// daemon isn't running Windows.
|
||||
if response.OSType != "windows" && runtime.GOOS == "windows" {
|
||||
// if response.OSType != "windows" && runtime.GOOS == "windows" && !options.quiet {
|
||||
_, _ = fmt.Fprintln(s.stdout(), "SECURITY WARNING: You are building a Docker "+
|
||||
"image from Windows against a non-Windows Docker host. All files and "+
|
||||
"directories added to build context will have '-rwxr-xr-x' permissions. "+
|
||||
"It is recommended to double check and reset permissions for sensitive "+
|
||||
"files and directories.")
|
||||
}
|
||||
|
||||
return imageID, nil
|
||||
}
|
||||
|
||||
func isLocalDir(c string) bool {
|
||||
_, err := os.Stat(c)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func imageBuildOptions(dockerCli command.Cli, project *types.Project, service types.ServiceConfig, options api.BuildOptions) buildtypes.ImageBuildOptions {
|
||||
config := service.Build
|
||||
return buildtypes.ImageBuildOptions{
|
||||
|
||||
@@ -38,8 +38,6 @@ import (
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/docker/compose/v2/internal/desktop"
|
||||
"github.com/docker/compose/v2/internal/experimental"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
)
|
||||
|
||||
@@ -52,20 +50,35 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
// NewComposeService create a local implementation of the compose.Service API
|
||||
func NewComposeService(dockerCli command.Cli) api.Service {
|
||||
return &composeService{
|
||||
type Option func(service *composeService)
|
||||
|
||||
// NewComposeService create a local implementation of the compose.Compose API
|
||||
func NewComposeService(dockerCli command.Cli, options ...Option) api.Compose {
|
||||
s := &composeService{
|
||||
dockerCli: dockerCli,
|
||||
clock: clockwork.NewRealClock(),
|
||||
maxConcurrency: -1,
|
||||
dryRun: false,
|
||||
}
|
||||
for _, option := range options {
|
||||
option(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// WithPrompt configure a UI component for Compose service to interact with user and confirm actions
|
||||
func WithPrompt(prompt Prompt) Option {
|
||||
return func(s *composeService) {
|
||||
s.prompt = prompt
|
||||
}
|
||||
}
|
||||
|
||||
type Prompt func(message string, defaultValue bool) (bool, error)
|
||||
|
||||
type composeService struct {
|
||||
dockerCli command.Cli
|
||||
desktopCli *desktop.Client
|
||||
experiments *experimental.State
|
||||
dockerCli command.Cli
|
||||
// prompt is used to interact with user and confirm actions
|
||||
prompt Prompt
|
||||
|
||||
clock clockwork.Clock
|
||||
maxConcurrency int
|
||||
@@ -81,9 +94,6 @@ func (s *composeService) Close() error {
|
||||
if s.dockerCli != nil {
|
||||
errs = append(errs, s.dockerCli.Client().Close())
|
||||
}
|
||||
if s.isDesktopIntegrationActive() {
|
||||
errs = append(errs, s.desktopCli.Close())
|
||||
}
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
@@ -321,7 +331,3 @@ func (s *composeService) RuntimeVersion(ctx context.Context) (string, error) {
|
||||
})
|
||||
return runtimeVersion.val, runtimeVersion.err
|
||||
}
|
||||
|
||||
func (s *composeService) isDesktopIntegrationActive() bool {
|
||||
return s.desktopCli != nil
|
||||
}
|
||||
|
||||
@@ -22,23 +22,6 @@ import (
|
||||
moby "github.com/docker/docker/api/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// ContainerCreated created status
|
||||
ContainerCreated = "created"
|
||||
// ContainerRestarting restarting status
|
||||
ContainerRestarting = "restarting"
|
||||
// ContainerRunning running status
|
||||
ContainerRunning = "running"
|
||||
// ContainerRemoving removing status
|
||||
ContainerRemoving = "removing"
|
||||
// ContainerPaused paused status
|
||||
ContainerPaused = "paused"
|
||||
// ContainerExited exited status
|
||||
ContainerExited = "exited"
|
||||
// ContainerDead dead status
|
||||
ContainerDead = "dead"
|
||||
)
|
||||
|
||||
var _ io.ReadCloser = ContainerStdout{}
|
||||
|
||||
// ContainerStdout implement ReadCloser for moby.HijackedResponse
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user