Compare commits

..
265 Commits
Author SHA1 Message Date
Paweł GronowskiandGitHub 98f1464960 Merge pull request #6988 from thaJeztah/make_shell
README: simplify instructions for using dev container
2026-05-14 16:33:29 +02:00
Sebastiaan van Stijn 50712c9326 README: simplify instructions for using dev container
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-14 15:44:01 +02:00
Paweł GronowskiandGitHub 653dc8f03d Merge pull request #6485 from paulchen5/6484-update-pull-request-template
PR template: remove outdated contributing guide link
2026-05-14 14:15:44 +02:00
Paweł GronowskiandGitHub 13945822d4 Merge pull request #6987 from thaJeztah/contributing_links
docs: fix stale links in CONTRIBUTING.md
2026-05-14 14:15:25 +02:00
f99747b9e0 docs: fix stale links in CONTRIBUTING.md
Also made some minor touch-ups (more can be done).

Co-authored-by: Philemon Ukane <ukanephilemon@gmail.com>
Co-authored-by: Andrea Grillo <andrea.grillo96@live.com>
Co-authored-by: Mahitha Adapa <mahitha.ada@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-14 13:45:02 +02:00
PaulchenandSebastiaan van Stijn ddac061db7 PR template: remove outdated contributing guide link
update contributing guide link and improve PR template formatting in `.github/PULL_REQUEST_TEMPLATE.md`

Signed-off-by: Paulchen <lukas.23022005@gmail.com>
Signed-off-by: Lukas Michael <lukas.23022005@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-14 13:15:58 +02:00
Paweł GronowskiandGitHub bd55370d2f Merge pull request #6984 from thaJeztah/cleanup_experimental
experimental: sync with actual features gated by experimental
2026-05-14 13:08:32 +02:00
Paweł GronowskiandGitHub f907b27599 Merge pull request #6985 from thaJeztah/rm_builder_stub
docs: remove stub for builder
2026-05-14 13:07:52 +02:00
Paweł GronowskiandGitHub 5201f5894e Merge pull request #6971 from matte1782/docs-authz-64kib-buffer-2026-05
docs: clarify 64 KiB response-body buffer in authz plugin docs
2026-05-14 13:02:31 +02:00
Paweł GronowskiandGitHub 5d48774148 Merge pull request #6986 from thaJeztah/cleanup_docs_readme
docs: remove outdated README
2026-05-14 13:00:30 +02:00
Sebastiaan van Stijn 10b1e87d09 docs: remove outdated README
The instructions in the file were outdated, and after removing them,
nothing really substantial was remaining, so let's just remove the
file for now.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-14 12:36:49 +02:00
Sebastiaan van Stijn e3802b8a0e experimental: sync with actual features gated by experimental
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-14 12:25:35 +02:00
Paweł GronowskiandGitHub 24f630cbe6 Merge pull request #2783 from pmorch/zsh-completion-gitlab-url
Reflect the new github URL for completion/zsh/_docker
2026-05-14 12:20:45 +02:00
Paweł GronowskiandGitHub 6f3c1ad752 Merge pull request #2943 from jimlinntu/add_test_commands_in_README
add commands of how to run the test
2026-05-14 12:05:03 +02:00
Paweł GronowskiandGitHub 33b32585cb Merge pull request #3728 from maxmorozoff/issue-3727
docs: Fix template error in cli example (#3727)
2026-05-14 12:04:13 +02:00
Paweł GronowskiandGitHub 333c580fd3 Merge pull request #4290 from 38tter/fix-tiny-nits
Fix nits
2026-05-14 12:03:43 +02:00
Paweł GronowskiandGitHub d77461c153 Merge pull request #4188 from finalchild/autoremove
Explain the auto-removal of anonymous volumes at the description of 'create --rm' and 'run --rm'
2026-05-14 12:03:24 +02:00
Paweł GronowskiandGitHub 666e4d5100 Merge pull request #6980 from thaJeztah/grammar_fixes
docs: minor grammar fixes
2026-05-14 12:02:45 +02:00
Paweł GronowskiandGitHub d090f98cb7 Merge pull request #4391 from u1735067/patch-1
metrics-addr is not experimental anymore since 20.10
2026-05-14 12:02:11 +02:00
Alexandre LEVAVASSEURandSebastiaan van Stijn 18bd1e7ce0 metrics-addr is not experimental since 20.10
See https://github.com/moby/moby/commit/f337a8d21d9902772a766f57475a5512405c86c5

Signed-off-by: Alexandre LEVAVASSEUR <alexandre+oss@13x.fr>
2026-05-14 11:52:32 +02:00
Sebastiaan van StijnandGitHub c5139f4905 Merge pull request #6982 from docker/dependabot/github_actions/github/codeql-action-4.35.4
build(deps): bump github/codeql-action from 4.35.3 to 4.35.4
2026-05-14 11:48:45 +02:00
Sebastiaan van Stijn 8aa8342502 docs: remove stub for builder
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-14 11:45:02 +02:00
dependabot[bot]andGitHub a6dc278db3 build(deps): bump github/codeql-action from 4.35.3 to 4.35.4
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.35.3 to 4.35.4.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/e46ed2cbd01164d986452f91f178727624ae40d7...68bde559dea0fdcac2102bfdf6230c5f70eb485e)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.35.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-14 08:44:18 +00:00
Max MorozovandSebastiaan van Stijn cc962d598a docs: Fix template error in cli example
Signed-off-by: Max Morozov <max@morozov.page>
Signed-off-by: Max Morozov <gtmax.yo@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-14 03:58:34 +02:00
Sebastiaan van StijnandVicente Jimenez Aguilar e4d651d792 docs: fix config, secret examples
Co-authored-by: Vicente Jimenez Aguilar <googuy@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-14 03:34:23 +02:00
Park JaeonandSebastiaan van Stijn fb09d828e3 document --rm also removing anonymous volumes
Explain the auto-removal of anonymous volumes
at the description of 'create --rm' and 'run --rm'

Signed-off-by: Park Jaeon <me@finalchild.dev>
2026-05-14 03:22:40 +02:00
Sebastiaan van Stijn 88e93954a6 docs, completion: use symlinks instead of symbol(ic) links
Symbolic links is the official term, but more commonly we refer to
them as symlinkx.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-14 03:07:19 +02:00
f550901e65 docs: minor grammar fixes
Co-authored-by: David Schmitt <118179693+DavidS-om@users.noreply.github.com>
Co-authored-by: Callis Ezenwaka <callisezenwaka@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-14 03:06:23 +02:00
Seiya MiyataandSebastiaan van Stijn a6d65ea31d Fix nits
Signed-off-by: Seiya Miyata <odradek38@gmail.com>
2026-05-14 03:00:12 +02:00
Jim LinandSebastiaan van Stijn 80ad53b064 add commands of how to run the test
Signed-off-by: Jim Lin <b04705003@ntu.edu.tw>
2026-05-14 02:20:47 +02:00
Peter Valdemar MørchandSebastiaan van Stijn fe78dc0be6 Reflect the new github URL for completion/zsh/_docker
Signed-off-by: Peter Valdemar Mørch <peter@morch.com>
2026-05-14 01:54:56 +02:00
Paweł GronowskiandGitHub 2ea4dc14aa Merge pull request #6977 from thaJeztah/receiver_name
cli/config/configfile: use more idiomatic receiver name
2026-05-13 14:01:42 +02:00
Paweł GronowskiandGitHub 39e188c5dc Merge pull request #6913 from Mohammed-Thaha/6203-add-healthcheck-format
container/ps: add HealthStatus formatter field
2026-05-13 14:01:16 +02:00
Sebastiaan van Stijn f12fc152a7 cli/config/configfile: use more idiomatic receiver name
Use a shorter name, which is more idiomatic, and prevents accidental
shadowing of types or arguments.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-12 16:44:00 +02:00
Mohammed ThahaandSebastiaan van Stijn 8d8d405dc7 container/ps: add HealthStatus formatter field
Signed-off-by: Mohammed Thaha <mohammedthahacse@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-12 16:14:21 +02:00
Sebastiaan van Stijn 517ca50506 docs: add more space in ps format table
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-12 16:09:46 +02:00
Paweł GronowskiandGitHub bcf36a5083 Merge pull request #6975 from thaJeztah/rm_redundant_test
cli/command/image: rm redundant TestPrintImageTreeNoWarningWhenRedirected
2026-05-11 18:36:08 +02:00
Sebastiaan van Stijn 7cbcd2f720 cli/command/image: rm redundant TestPrintImageTreeNoWarningWhenRedirected
This is already covered by various tests using .golden files.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-11 17:27:19 +02:00
Paweł GronowskiandGitHub ba93f0d1cc Merge pull request #6972 from thaJeztah/decorate_env_file_error
decorate --env-file, --label-file errors
2026-05-11 13:59:56 +02:00
Paweł GronowskiandGitHub ed8c9d7113 Merge pull request #6974 from thaJeztah/bump_x_deps
vendor: update golang.org/x/* dependencies
2026-05-11 13:57:56 +02:00
Paweł GronowskiandGitHub aa1be189b4 Merge pull request #4535 from thaJeztah/restart_policy_more_validate
refactor parsing restart-policies
2026-05-11 13:57:33 +02:00
Sebastiaan van Stijn 970afd5cc4 vendor: golang.org/x/net v0.54.0
full diff: https://github.com/golang/net/compare/v0.53.0...v0.54.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-11 12:22:37 +02:00
Sebastiaan van Stijn 7e07bf127c vendor: golang.org/x/mod v0.36.0
full diff: https://github.com/golang/mod/compare/v0.34.0...v0.36.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-11 12:21:34 +02:00
Sebastiaan van Stijn 5aeb52681b vendor: golang.org/x/text v0.37.0
full diff: https://github.com/golang/time/compare/v0.36.0...v0.37.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-11 12:20:20 +02:00
Sebastiaan van Stijn 644d046721 vendor: golang.org/x/term v0.43.0
full diff: https://github.com/golang/term/compare/v0.42.0...v0.43.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-11 12:18:58 +02:00
Sebastiaan van Stijn a853f40a30 vendor: golang.org/x/sys v0.44.0
full diff: https://github.com/golang/sys/compare/v0.43.0...v0.44.0

- unix: add CPUSetDynamic for systems with more than 1024 CPUs
- unix: avoid nil pointer dereference in Utime
- unix: automatically remove container created by mkall.sh
- cpu: use IsProcessorFeaturePresent to calculate ARM64 on windows
- windows: add GetIfTable2Ex, GetIpInterface{Entry,Table}, GetUnicastIpAddressTable
- windows: avoid uint16 overflow in NewNTUnicodeString

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-11 12:18:14 +02:00
Sebastiaan van Stijn 87f06ec70a refactor parsing restart-policies
- opts.ParseRestartPolicy: use a struct-literal and clarify that this
  function only parses, but does not validate invalid combinations.
- cli/compose/convert: convertRestartPolicy: update doc to be more
  clear on the order of preference and intent.
- cli/compose/convert: convertRestartPolicy: use a switch based on
  known values to allow the exhaustive linter to catch missing options.
- cli/compose/convert: convertRestartPolicy: add validation for negative
  values when converting the legacy service.restart policy.
- cli/compose/convert: convertRestartPolicy: always set MaxAttempts
  and leave validation to the daemon.

opts: ParseRestartPolicy: improve validation of max restart-counts

Use the new container.ValidateRestartPolicy utility to verify if a max-restart-count
is allowed for the given restart-policy.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-11 10:41:13 +02:00
Sebastiaan van Stijn 033f8a1fd2 cli/compose/convert: use test-table for restart-policy tests
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-11 10:40:01 +02:00
Sebastiaan van Stijn 27bee792a0 decorate --env-file, --label-file errors
Before:

    docker run --rm --env-file=./no-such-file alpine
    docker: open ./no-such-file: no such file or directory

    Run 'docker run --help' for more information

After:

    docker run --rm --env-file=./no-such-file alpine
    docker: --env-file: open ./no-such-file: no such file or directory

    Run 'docker run --help' for more information

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-11 10:18:53 +02:00
Matteo Panzeri 6d38b7a71a docs: clarify 64 KiB response-body buffer in authz plugin docs
Adds a "Response body size and partial buffering" subsection to
docs/extend/plugins_authorization.md documenting the 64 KiB
maxBufferSize constant in the daemon's internal responseModifier
(pkg/authorization/response.go in moby/moby) and the practical
implications for plugins that use ResponseBody inspection.

The existing docs (lines 81-87) say streaming endpoints such as
logs and events send only the HTTP request to plugins, but don't
explain the underlying mechanism. Plugin authors building
response-body redaction or content-filtering can be surprised when
the same effect happens on non-listed endpoints whose response is
produced through multiple writes exceeding the buffer or via an
io.WriteFlusher.

The 64 KiB buffer is observable from the public moby source, so
this PR is documentation catching up to existing behavior — not
a contract change.

Signed-off-by: Matteo Panzeri <matteo1782@gmail.com>
2026-05-09 23:00:51 +02:00
Sebastiaan van StijnandGitHub a6d013f4c9 Merge pull request #6840 from vibhuanand/3064-context-use-docs
docs: clarify docker context use affects all terminal sessions
2026-05-08 19:35:37 +02:00
Sebastiaan van StijnandGitHub 247839fb41 Merge pull request #6968 from docker/dependabot/github_actions/github/codeql-action-4.35.3
build(deps): bump github/codeql-action from 4.35.2 to 4.35.3
2026-05-08 16:13:43 +02:00
Paweł GronowskiandGitHub 3d408ee95c Merge pull request #6969 from thaJeztah/bump_creds_helper
vendor: github.com/docker/docker-credential-helpers v0.9.7
2026-05-08 15:46:42 +02:00
Paweł GronowskiandGitHub f66e796147 Merge pull request #6846 from thaJeztah/normalize_authconfig
cli/config/configfile: normalize hostname when resolving auth
2026-05-08 15:46:13 +02:00
Sebastiaan van Stijn 1e1384fae2 vendor: github.com/docker/docker-credential-helpers v0.9.7
no code-changes; only updates go version

full diff: https://github.com/docker/docker-credential-helpers/compare/v0.9.6...v0.9.7

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-08 14:51:12 +02:00
Paweł GronowskiandGitHub e76954974a Merge pull request #6737 from thaJeztah/rm_deprecated_buildutils
cli/command/image/build: remove deprecated utilities and consts
2026-05-08 13:54:28 +02:00
Paweł GronowskiandGitHub f285746704 Merge pull request #6947 from thaJeztah/bump_platforms
vendor: github.com/containerd/platforms v1.0.0-rc.4
2026-05-08 13:26:50 +02:00
Paweł GronowskiandGitHub a3554d6830 Merge pull request #6950 from thaJeztah/plugin_limit_messages
cli-plugins/hooks: limit maximum number of lines / messages
2026-05-08 13:26:33 +02:00
Paweł GronowskiandGitHub ed8f23bffa Merge pull request #6965 from thaJeztah/test_subtests
cli/compose/schema: TestValidatePorts: use subtests
2026-05-08 13:26:00 +02:00
Paweł GronowskiandGitHub 96131c8159 Merge pull request #6966 from thaJeztah/DetectDefaultStore_update_godoc
cli/config/credentials: DetectDefaultStore: update GoDoc
2026-05-08 13:25:47 +02:00
dependabot[bot]andGitHub 976d97dd23 build(deps): bump github/codeql-action from 4.35.2 to 4.35.3
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.35.2 to 4.35.3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/95e58e9a2cdfd71adc6e0353d5c52f41a045d225...e46ed2cbd01164d986452f91f178727624ae40d7)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.35.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-08 08:44:18 +00:00
Sebastiaan van StijnandGitHub a9031f4c84 Merge pull request #6967 from vvoland/update-go
update to go1.26.3
2026-05-08 00:22:25 +02:00
Paweł Gronowski 77435c59ec update to go1.26.3
This release include 11 security fixes:

- cmd/go: malicious module proxy can bypass checksum database

    A malicious module proxy could exploit a flaw in the go command's
    validation of module checksums to bypass checksum database validation.

    This vulnerability affects any user using an untrusted module proxy
    (GOMODPROXY) or checksum database (GOSUMDB).

    A malicious module proxy can serve altered versions of the Go toolchain.
    When selecting a different version of the Go toolchain than the
    currently installed toolchain (due to the GOTOOLCHAIN environment variable,
    or a go.work or go.mod with a toolchain line), the go command will download
    and execute a toolchain provided by the module proxy. A malicious module
    proxy can bypass checksum database validation for this downloaded
    toolchain.

    Since this vulnerability affects the security of toolchain downloads,
    setting GOTOOLCHAIN to a fixed version is not sufficient. You must upgrade
    your base Go toolchain.

    The go tool always validates the hash of a toolchain before executing it,
    so fixed versions will refuse to execute any cached, altered versions of the
    toolchain.

    The go tool trusts go.sum files to contain accurate hashes of the current
    module's dependencies. A malicious proxy exploiting this vulnerability to
    serve an altered module will have caused an incorrect hash to be recorded
    in the go.sum. Users who have configured a non-trusted GOPROXY can determine
    if they have been affected by running "rm go.sum ; go mod tidy ; go mod verify",
    which will revalidate all dependencies of the current module.

    The specific flaw in more detail:

    The go command consults the checksum database to validate downloaded modules,
    when a module is not listed in the go.sum file. It verifies that the module hash
    reported by the checksum database matches the hash of the downloaded module.
    If, however, the checksum database returns a successful response that contains
    no entry for the module, the go command incorrectly permitted validation to succeed.

    A module proxy may mirror or proxy the checksum database, in which case the go
    command will not connect to the checksum database directly. Checksums reported
    by the checksum database are cryptographically signed, so a malicious proxy
    cannot alter the reported checksum for a module. However, a proxy which returns
    an empty checksum response, or a checksum response for an unrelated module,
    could cause the go command to proceed as if a downloaded module has been validated.

    The go command now properly checks checksum database responses to ensure
    that the expected module signature is present, not just that if a signature is
    present it matches the expectation.

    Thanks to Mundur (https://github.com/M0nd0R) for reporting this issue.

    This is CVE-2026-42501 and Go issue https://go.dev/issue/79070.

- net/http/httputil: ReverseProxy forwards queries with more than urlmaxqueryparams parameters

    When used with a Rewrite function, or a Director function which parses query parameters,
    ReverseProxy sanitizes the forwarded request to remove query parameters which are not
    parsed by url.ParseQuery. ReverseProxy did not take ParseQuery's limit on the total number
    of query parameters (controlled by GODEBUG=urlmaxqueryparams=N) into account.
    This could permit ReverseProxy to forward a request containing a query parameter
    that was not visible to the Rewrite function.

    For example, the query "a1=x&a2=x&...&a10000=x&hidden=y" could forward the parameter
    "hidden=y" while hiding it from the proxy's Rewrite function.

    ReverseProxy now avoids forwarding parameters that exceed the ParseQuery limit.

    This is CVE-2026-39825 and Go issue https://go.dev/issue/78948.

- net: panic in Dial and LookupPort when handling NUL byte on Windows

    The Dial and LookupPort functions would panic on Windows when provided
    with an input containing a NUL (0). These functions now return an error
    rather than panicking.

    This is CVE-2026-39836 and Go issue https://go.dev/issue/79006.

- net/mail: quadratic string concatenation in consumePhrase

    Pathological inputs could cause DoS through consumePhrase
    when parsing an email address according to RFC 5322.

    This is CVE-2026-42499 and Go issue https://go.dev/issue/78987.

- net/mail: quadratic string concatentation in consumeComment

    Well-crafted inputs reaching ParseAddress, ParseAddressList,
    and ParseDate were able to trigger excessive CPU exhaustion
    and memory allocations.

    This is CVE-2026-39820 and Go issue https://go.dev/issue/78566.

- cmd/go: "go bug" follows symlinks in predictable temporary filenames

    The "go bug" command wrote to two files with predictable names in
    the system temporary directory (for example, "/tmp").

    An attacker with access to the temporary directory could create a
    symlink in one of these names, causing "go bug" to overwrite the
    target of the symlink.

    The "go bug" command now uses os.MkdirTemp to create a safe
    working directory.

    Thanks to Harshit Gupta (Mr HAX) for reporting this issue.

    This is CVE-2026-39819 and Go issue https://go.dev/issue/78584.

- cmd/go: "go tool pack" does not sanitize output paths

    The "go tool pack" subcommand is a minimal version of the Unix ar utility.
    It is used by the compiler as an internal tool with known-good inputs.

    The "pack" subcommand did not sanitize output filenames.
    When invoked to extract a malicious archive file, it could write
    files to arbitrary locations on the filesystem.

    The "pack" subcommand now refuses to extract files with names
    containing any directory components.

    Thanks to Harshit Gupta (Mr HAX) for reporting this issue.

    This is CVE-2026-39817 and Go issue https://go.dev/issue/78778.

- net/http: infinite loop in HTTP/2 transport when given bad SETTINGS_MAX_FRAME_SIZE

    When processing HTTP/2 SETTINGS frames, transport will enter an infinite loop of
    writing CONTINUATION frames if it receives a SETTINGS_MAX_FRAME_SIZE with a
    value of 0.

    This allows potential DoS against a client by a malicious server. HTTP/2
    transport now properly checks that the received SETTINGS_MAX_FRAME_SIZE is
    valid.

    Thanks to Marwan Atia (marwansamir688@gmail.com) for reporting this issue.

    This is CVE-2026-33814 and Go issue https://go.dev/issue/78476.

- html/template: escaper bypass leads to XSS

    If a trusted template author were to write a
    tag containing an empty type attribute or a type
    attribute with an ASCII whitespace, the execution of
    the template would incorrectly escape any data passed
    into the block.

    Thanks to Mundur (https://github.com/M0nd0R) for reporting this issue.

    This is CVE-2026-39826 and Go issue https://go.dev/issue/78981.

- net: crash when handling long CNAME response

    When using LookupCNAME with the cgo DNS resolver,
    a very long CNAME response could trigger a double-free of C memory
    and a crash. The double-free has been fixed.

    Thanks to hamayanhamayan for reporting this issue.

    This is CVE-2026-33811 and Go issue https://go.dev/issue/78803.

- html/template: bypass of meta content URL escaping causes XSS

    CVE-2026-27142 fixed a vulnerability in which URLs were not
    correctly escaped inside of a tag's attribute.
    If the URL content were to insert ASCII whitespaces around the
    = rune inside of the attribute, the escaper would
    fail to similarly escape it, leading to XSS.

    Dynamic inputs to a tag's attribute are now
    whitespace sanitized prior to escaping.

    Thanks to Samy Ghannad for reporting this issue.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-05-07 19:26:33 +02:00
Sebastiaan van Stijn e93fe9083b cli/config/credentials: DetectDefaultStore: update GoDoc
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-07 17:20:48 +02:00
Sebastiaan van Stijn b7ab63387a cli-plugins/hooks: limit maximum number of lines / messages
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-07 17:09:52 +02:00
VibhuandSebastiaan van Stijn e03b8373c7 docs: clarify docker context use is sticky
Signed-off-by: Vibhu <vibhuanand@outlook.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-07 17:06:02 +02:00
Sebastiaan van Stijn 6f144c6d70 cli/command/image/build: remove deprecated ResolveAndValidateContextPath util
It was deprecated in 0f2f9e9c41

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-07 16:28:34 +02:00
Sebastiaan van Stijn b124c707f0 cli/command/image/build: remove deprecated WriteTempDockerfile util
It was deprecated in 6e1ff0bec1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-07 16:28:34 +02:00
Sebastiaan van Stijn 3dd0c846cd cli/command/image/build: remove deprecated DetectArchiveReader util
It was deprecated in c52fa073cd

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-07 16:28:34 +02:00
Sebastiaan van Stijn 403bebeea2 cli/command/image/build: remove deprecated DefaultDockerfileName const
It was deprecated in f24bb4bc76

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-07 16:28:33 +02:00
Sebastiaan van Stijn 8e7ec44f9f cli/command/image/build: remove deprecated IsArchive utility
It was deprecated in 64be664e85

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-07 16:28:31 +02:00
Sebastiaan van StijnandGitHub 4001288d95 Merge pull request #6754 from MaheshThakur9152/fix/remove-images-redirect-warning
Fix: Remove inconsistent human readability warning from docker images
2026-05-07 14:01:21 +02:00
Sebastiaan van Stijn 55fcffe743 cli/config/configfile: normalize hostname when resolving auth
Previously, normalization was done before calling these functions,
which required implementations to normalize before using.

Move the normalization into the GetAuthConfig, GetCredentialsStore,
so that non-normalized hostnames will be able to resolve the correct
auth.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-07 13:58:11 +02:00
Sebastiaan van Stijn e8a0beb909 cli/compose/schema: TestValidatePorts: use subtests
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-07 13:55:59 +02:00
Paweł GronowskiandGitHub 1fe8d42912 Merge pull request #6964 from thaJeztah/bump_version
bump VERSION to v29.5.0
2026-05-07 13:29:25 +02:00
Sebastiaan van Stijn 9013c33372 bump VERSION to v29.5.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-07 13:09:47 +02:00
Sebastiaan van StijnandGitHub 0b459a4b76 Merge pull request #6954 from vvoland/stable-labels
formatter: Sort labels for stable output
2026-04-24 17:27:20 +02:00
Paweł Gronowski 7059ef4c9c formatter: Sort labels for stable output
Several Labels() methods iterated over maps without sorting, producing
non-deterministic output.

In early versions of Go, map iteration order happened to be stable in
practice, so the original code appeared to work correctly.
Since Go 1.12, the runtime intentionally randomizes map iteration order,
making the output unpredictable between runs.

The API response produces sorted labels and the container formatter
already sorted its labels (changed in 5ee17eef).

Apply the same fix to the volume, network, config, and secret
formatters, and update tests to assert exact ordering instead of using
order-independent comparison.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-04-24 16:47:11 +02:00
Paweł GronowskiandGitHub 3928571dac Merge pull request #6953 from thaJeztah/bump_swarmkit
vendor: github.com/moby/swarmkit/v2 v2.1.2
2026-04-24 11:40:39 +02:00
Sebastiaan van Stijn 9bbe02848d vendor: github.com/moby/swarmkit/v2 v2.1.2
full diff: https://github.com/moby/swarmkit/compare/v2.1.1...v2.1.2

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-23 21:39:53 +02:00
Sebastiaan van StijnandGitHub 977ee838e0 Merge pull request #6951 from docker/dependabot/github_actions/github/codeql-action-4.35.2
build(deps): bump github/codeql-action from 4.35.1 to 4.35.2
2026-04-22 17:24:55 +02:00
dependabot[bot]andGitHub b03176ddff build(deps): bump github/codeql-action from 4.35.1 to 4.35.2
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.35.1 to 4.35.2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/c10b8064de6f491fea524254123dbe5e09572f13...95e58e9a2cdfd71adc6e0353d5c52f41a045d225)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.35.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-22 13:35:46 +00:00
Paweł GronowskiandGitHub e874fdbe83 Merge pull request #6952 from thaJeztah/bump_version
bump VERSION to v29.4.2
2026-04-22 15:22:15 +02:00
Sebastiaan van Stijn 9d8c1e4b70 bump VERSION to v29.4.2
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-22 15:14:35 +02:00
Paweł GronowskiandGitHub 055a478ea9 Merge pull request #6945 from thaJeztah/bump_moby
vendor: github.com/moby/moby/client v0.4.1, moby/api v1.54.2
2026-04-20 16:57:44 +02:00
Sebastiaan van Stijn d0f5b279e9 cmd/docker-trust: bump moby/client v0.4.1, moby/api v1.54.2
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-20 16:50:53 +02:00
Sebastiaan van Stijn b7f37e86da vendor: github.com/moby/moby/client v0.4.1, moby/api v1.54.2
- https://github.com/moby/moby/compare/api/v1.54.1...api/v1.54.2
- https://github.com/moby/moby/compare/client/v0.4.0...client/v0.4.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-20 16:49:58 +02:00
Paweł GronowskiandGitHub c93d892f0e Merge pull request #6949 from thaJeztah/bump_utils
Dockerfile: update buildx to v0.33.0, compose v5.1.3
2026-04-20 16:26:38 +02:00
Paweł GronowskiandGitHub 3553cafa13 Merge pull request #6948 from thaJeztah/bump_trust_deps
cmd/docker-trust: bump dependencies
2026-04-20 16:26:17 +02:00
Sebastiaan van Stijn 266f039bb5 Dockerfile: update compose to v5.1.3
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-20 16:13:56 +02:00
Sebastiaan van Stijn d74d3c3b16 Dockerfile: update buildx to v0.33.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-20 16:12:56 +02:00
Sebastiaan van StijnandGitHub 134c2a0ed6 Merge pull request #6826 from thaJeztah/bump_golangci_lint2
Dockerfile: update golangci-lint to v2.10.1
2026-04-20 16:00:14 +02:00
Paweł GronowskiandSebastiaan van Stijn 58a7c3155b golangci-lint: fix lint failures from v2.10.1 upgrade
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-04-20 15:49:35 +02:00
Sebastiaan van Stijn f37a9e663f Dockerfile: update golangci-lint to v2.10.1
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-20 15:49:32 +02:00
Paweł GronowskiandGitHub 4e25e5cfa9 Merge pull request #6941 from vvoland/milestone-validate
gha: Add milestone validation workflow
2026-04-20 15:32:29 +02:00
Sebastiaan van Stijn ee56098b07 cmd/docker-trust: bump dependencies
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-20 14:59:01 +02:00
Sebastiaan van StijnandGitHub f31b8b2697 Merge pull request #6946 from thaJeztah/bump_runewidth
vendor: github.com/mattn/go-runewidth v0.0.23
2026-04-20 14:52:27 +02:00
Paweł Gronowski efbbc0c68c gha: Add milestone validation workflow
Ensure PRs have a milestone set and that it matches the next release
version declared in the root VERSION file.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-04-20 14:51:37 +02:00
Paweł GronowskiandGitHub 1546665853 Merge pull request #6942 from thaJeztah/rm_kernel_memory
docs, completion: remove deprecated "--kernel-memory" flags
2026-04-20 14:39:40 +02:00
Paweł GronowskiandGitHub 4e4e8f940b Merge pull request #6943 from thaJeztah/bump_creds_helper
vendor: github.com/docker/docker-credential-helpers v0.9.6
2026-04-20 14:38:45 +02:00
Paweł GronowskiandGitHub 2b65d5943f Merge pull request #6944 from thaJeztah/bump_x_deps
vendor: update golang.org/x/* dependencies
2026-04-20 14:38:27 +02:00
Sebastiaan van Stijn beac1144a6 vendor: github.com/containerd/platforms v1.0.0-rc.4
full diff: https://github.com/containerd/platforms/compare/v1.0.0-rc.2...v1.0.0-rc.4

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-20 14:37:14 +02:00
Sebastiaan van Stijn a75ab98e43 vendor: github.com/mattn/go-runewidth v0.0.23
full diff: https://github.com/mattn/go-runewidth/compare/v0.0.22...v0.0.23

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-20 14:34:25 +02:00
Sebastiaan van Stijn b9549d3ab5 vendor: golang.org/x/net v0.53.0
full diff: https://cs.opensource.google/go/x/net/+/refs/tags/v0.52.0...refs/tags/v0.53.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-20 14:22:26 +02:00
Sebastiaan van Stijn 2edf815a80 vendor: golang.org/x/term v0.42.0
full diff: https://cs.opensource.google/go/x/term/+/refs/tags/v0.41.0...refs/tags/v0.42.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-20 14:21:03 +02:00
Sebastiaan van Stijn 258010c788 vendor: golang.org/x/text v0.36.0
full diff: https://cs.opensource.google/go/x/text/+/refs/tags/v0.35.0...refs/tags/v0.36.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-20 14:20:06 +02:00
Sebastiaan van Stijn cfa80d8644 vendor: golang.org/x/mod v0.35.0
full diff: https://cs.opensource.google/go/x/mod/+/refs/tags/v0.34.0...refs/tags/v0.35.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-20 14:18:58 +02:00
Sebastiaan van Stijn c20b2479e0 vendor: golang.org/x/sys v0.43.0
full diff: https://cs.opensource.google/go/x/sys/+/refs/tags/v0.42.0...refs/tags/v0.43.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-20 14:17:33 +02:00
Sebastiaan van Stijn 573dd31bf7 vendor: github.com/docker/docker-credential-helpers v0.9.6
full diff: https://github.com/docker/docker-credential-helpers/compare/v0.9.5...v0.9.6

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-20 14:14:22 +02:00
Sebastiaan van Stijn 2988338b99 docs/reference: remove deprecated "--kernel-memory" flag
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-20 14:06:16 +02:00
Sebastiaan van Stijn 58c7328df6 man: remove deprecated "--kernel-memory" flag
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-20 14:05:48 +02:00
Sebastiaan van Stijn 5468871a02 contrib/completion: remove deprecated "--kernel-memory" flags
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-20 14:05:10 +02:00
Paweł GronowskiandGitHub 10f16704a3 Merge pull request #6931 from docker/dependabot/github_actions/docker/bake-action-7.1.0
build(deps): bump docker/bake-action from 7.0.0 to 7.1.0
2026-04-20 12:12:09 +02:00
Sebastiaan van StijnandGitHub 89958b190c Merge pull request #6927 from Varun5711/docs/legacy-plugin-links-6880
docs: refresh legacy plugin references
2026-04-17 20:24:32 +02:00
Varun5711 acbbeb3c3a docs: refresh legacy plugin references
Signed-off-by: Varun Hotani <varunhotani@gmail.com>
2026-04-17 21:14:16 +05:30
Paweł GronowskiandGitHub dea3ebaa87 Merge pull request #6936 from thaJeztah/bump_otels
vendor: google.golang.org/grpc v1.80.0, go.opentelemetry.io/otel v1.43.0, go.opentelemetry.io/contrib v0.68.0
2026-04-17 15:41:57 +02:00
Sebastiaan van Stijn 3362ae31d0 vendor: go.opentelemetry.io/contrib v0.68.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-17 14:09:30 +02:00
Sebastiaan van Stijn a1edb22832 vendor: go.opentelemetry.io/otel v1.43.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-17 14:07:46 +02:00
Sebastiaan van Stijn 0a24da3382 vendor: google.golang.org/grpc v1.80.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-17 14:07:39 +02:00
Sebastiaan van Stijn f24c779887 vendor: google.golang.org/genproto/* 9d38bb4040a9
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-17 14:04:40 +02:00
Paweł GronowskiandGitHub 17633b7742 Merge pull request #6935 from docker/dependabot/github_actions/actions/upload-artifact-7.0.1
build(deps): bump actions/upload-artifact from 7.0.0 to 7.0.1
2026-04-17 10:52:20 +02:00
dependabot[bot]andGitHub 99aaff1807 build(deps): bump actions/upload-artifact from 7.0.0 to 7.0.1
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/bbbca2ddaa5d8feaa63e36b76fdaad77386f024f...043fb46d1a93c77aae656e7c1c64a875d1fc6a0a)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-17 08:43:30 +00:00
dependabot[bot]andSebastiaan van Stijn f3c060c382 build(deps): bump docker/bake-action from 7.0.0 to 7.1.0
Bumps [docker/bake-action](https://github.com/docker/bake-action) from 7.0.0 to 7.1.0.
- [Release notes](https://github.com/docker/bake-action/releases)
- [Commits](https://github.com/docker/bake-action/compare/82490499d2e5613fcead7e128237ef0b0ea210f7...a66e1c87e2eca0503c343edf1d208c716d54b8a8)

---
updated-dependencies:
- dependency-name: docker/bake-action
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-17 00:41:48 +02:00
Paweł GronowskiandGitHub 792293cc2f Merge pull request #6923 from docker/dependabot/github_actions/docker/login-action-4.1.0
build(deps): bump docker/login-action from 4.0.0 to 4.1.0
2026-04-16 13:10:41 +02:00
Paweł GronowskiandGitHub 2660a496bf Merge pull request #6930 from thaJeztah/bump_go_connections
vendor: github.com/docker/go-connections v0.7.0
2026-04-16 13:10:09 +02:00
Sebastiaan van Stijn b9a052a987 vendor: github.com/docker/go-connections v0.7.0
Changes:

- raise minimum supported Go version to go1.23
- sockets: `ConfigureTransport`: prevent idle connections leaking FDs.
- sockets: implement `WithAdditionalUsersAndGroups` for windows.
- tlsconfig: add ChaCha20-Poly1305 cipher suites to align closer with stdlib defaults.
- nat: SortPortMap: accept `map[Port][]PortBinding` as argument.
- nat: add benchmarks, optimize, and improve errors.
- proxy: check for `net.ErrClosed` instead of string-matching "use of closed network connection".

Breaking changes:

- tlsconfig: deprecate `tlsconfig.SystemCertPool` in favor of stdlib `x509.SystemCertPool`.
- sockets: remove deprecated `DialPipe`, `GetProxyEnv`, `DialerFromEnvironment`

Dependency updates:

- update github.com/Microsoft/go-winio to go v0.6.2
- update golang.org/x/sys to v0.10.0

full diff: https://github.com/docker/go-connections/compare/v0.6.0...v0.7.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-16 00:20:20 +02:00
Sebastiaan van StijnandGitHub 84b357f6b0 Merge pull request #6929 from thaJeztah/stdlib_systempools
use stdlib's x509.SystemCertPool on Windows
2026-04-15 16:32:00 +02:00
Sebastiaan van Stijn 0d32fde8e3 internal/registry: use stdlib's x509.SystemCertPool on Windows
The `tlsconfig.SystemCertPool` utility in go-connections was added in
[docker/go-connections@55aadc3], at which time Go stdlib didn't support
system-pools ([x509.SystemCertPool]) on Windows, so an empty pool was
constructed.

Support for system pools on Windows originally added in Go 1.8 (through
[golang/go@05471e9]), but reverted, and re-implemented in Go 1.18 (through
[golang/go@3544082]).

Go 1.18 and up now implement this, but, unlike Linux, which uses a pure-Go
implementation, certificate validation is handled by the system:

> On macOS and Windows, certificate verification is handled by system APIs,
> but the package aims to apply consistent validation rules across operating
> systems.

On macOS and Windows, x509.SystemCertPool returns an empty Pool, with the
`systemPool` set to `true` (see [loadSystemRoots]). This must be considered
an implementation detail; custom CAs can be appended to this pool, and handled
as usual.

This patch removes the special handling on Windows, removing the dependency
on go-connections for this part.

[docker/go-connections@55aadc3]: https://github.com/docker/go-connections/commit/55aadc3cc561684699edcdd0921b9293c3ee6b49
[golang/go@05471e9]: https://github.com/golang/go/commit/05471e9ee64a300bd2dcc4582ee1043c055893bb
[golang/go@3544082]: https://github.com/golang/go/commit/3544082f75fd3d2df7af237ed9aef3ddd499ab9c
[x509.SystemCertPool]: https://pkg.go.dev/crypto/x509#SystemCertPool
[loadSystemRoots]: https://cs.opensource.google/go/go/+/refs/tags/go1.26.1:src/crypto/x509/root_windows.go;l=15-17

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-15 13:04:15 +02:00
Sebastiaan van Stijn ad641e5d61 cmd/docker-trust: use stdlib's x509.SystemCertPool on Windows
The `tlsconfig.SystemCertPool` utility in go-connections was added in
[docker/go-connections@55aadc3], at which time Go stdlib didn't support
system-pools ([x509.SystemCertPool]) on Windows, so an empty pool was
constructed.

Support for system pools on Windows originally added in Go 1.8 (through
[golang/go@05471e9]), but reverted, and re-implemented in Go 1.18 (through
[golang/go@3544082]).

Go 1.18 and up now implement this, but, unlike Linux, which uses a pure-Go
implementation, certificate validation is handled by the system:

> On macOS and Windows, certificate verification is handled by system APIs,
> but the package aims to apply consistent validation rules across operating
> systems.

On macOS and Windows, x509.SystemCertPool returns an empty Pool, with the
`systemPool` set to `true` (see [loadSystemRoots]). This must be considered
an implementation detail; custom CAs can be appended to this pool, and handled
as usual.

This patch removes the special handling on Windows, removing the dependency
on go-connections for this part.

[docker/go-connections@55aadc3]: https://github.com/docker/go-connections/commit/55aadc3cc561684699edcdd0921b9293c3ee6b49
[golang/go@05471e9]: https://github.com/golang/go/commit/05471e9ee64a300bd2dcc4582ee1043c055893bb
[golang/go@3544082]: https://github.com/golang/go/commit/3544082f75fd3d2df7af237ed9aef3ddd499ab9c
[x509.SystemCertPool]: https://pkg.go.dev/crypto/x509#SystemCertPool
[loadSystemRoots]: https://cs.opensource.google/go/go/+/refs/tags/go1.26.1:src/crypto/x509/root_windows.go;l=15-17

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-15 13:03:39 +02:00
dependabot[bot]andSebastiaan van Stijn 14aa781767 build(deps): bump docker/login-action from 4.0.0 to 4.1.0
Bumps [docker/login-action](https://github.com/docker/login-action) from 4.0.0 to 4.1.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/b45d80f862d83dbcd57f89517bcf500b2ab88fb2...4907a6ddec9925e35a0a9e82d7399ccc52663121)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 4.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-15 11:40:10 +02:00
Paweł GronowskiandGitHub 950401cf07 Merge pull request #6920 from thaJeztah/bump_go1.26.2
update to Go 1.26.2
2026-04-08 12:49:00 +02:00
Sebastiaan van StijnandGitHub 3ba1094cb1 Merge pull request #6915 from docker/dependabot/github_actions/github/codeql-action-4.35.1
build(deps): bump github/codeql-action from 4.34.1 to 4.35.1
2026-04-08 01:03:53 +02:00
Sebastiaan van Stijn d1920f0b2c update to Go 1.26.2
go1.26.2 (released 2026-04-07) includes security fixes to the go command,
the compiler, and the archive/tar, crypto/tls, crypto/x509, html/template,
and os packages, as well as bug fixes to the go command, the go fix command,
the compiler, the linker, the runtime, and the net, net/http, and net/url
packages. See the Go 1.26.2 milestone on our issue tracker for details;

- https://github.com/golang/go/issues?q=milestone%3AGo1.26.2+label%3ACherryPickApproved
- full diff: https://github.com/golang/go/compare/go1.26.1...go1.26.2

From the security announce:

We have just released Go versions 1.26.2 and 1.25.9, minor point releases.

These releases include 10 security fixes following the security policy:

- os: Root.Chmod can follow symlinks out of the root on Linux

  On Linux, if the target of Root.Chmod is replaced with a symlink while
  the chmod operation is in progress, Chmod could operate on the target
  of the symlink, even when the target lies outside the root.

  The Linux fchmodat syscall silently ignores the AT_SYMLINK_NOFOLLOW flag,
  which Root.Chmod uses to avoid symlink traversal. Root.Chmod checks its
  target before acting and returns an error if the target is a symlink
  lying outside the root, so the impact is limited to cases where the
  target is replaced with a symlink between the check and operation.

  On Linux, Root.Chmod now uses the fchmodat2 syscall when available, and
  an workaround using /proc/self/fd otherwise.

  Thanks to Uuganbayar Lkhamsuren for reporting this issue.

  This is CVE-2026-32282 and Go issue https://go.dev/issue/78293.

- html/template: JS template literal context incorrectly tracked

  Context was not properly tracked across template branches for JS template
  literals, leading to possibly incorrect escaping of content when branches were
  used.

  Additionally template actions within JS template literals did not properly
  track
  the brace depth, leading to incorrect escaping being applied.

  These issues could cause actions within JS template literals to be incorrectly
  or improperly escaped, leading to XSS vulnerabilities.

  This only affects templates that use template actions within JS template
  literals.

  This is CVE-2026-32289 and Go issue https://go.dev/issue/78331.

- crypto/x509: excluded DNS constraints not properly applied to wildcard domains

  When verifying a certificate chain containing excluded DNS constraints, these
  constraints are not correctly applied to wildcard DNS SANs which use a
  different
  case than the constraint.

  For example, if a certificate contains the DNS name "*.example.com" and the
  excluded DNS name "EXAMPLE.COM", the constraint will not be applied.

  This only affects validation of otherwise trusted certificate chains, issued
  by
  a root CA in the VerifyOptions.Roots CertPool, or in the system certificate
  pool.

  This issue only affects Go 1.26.

  Thank you to Riyas from Saintgits College of Engineering, k1rnt, @1seal for
  reporting this issue.

  This is CVE-2026-33810 and Go issue https://go.dev/issue/78332.

- cmd/compile: no-op interface conversion bypasses overlap checking

  Previously, the compiler failed to unwrap pointers contained within
  a no-op interface conversion leading to an incorrect determination
  of a non-overlapping move.

  To prevent unsafe move operations, the compiler will now unwrap all
  such conversions before considering a move non-overlapping.

  Thank you to Jakub Ciolek - https://ciolek.dev/ for reporting this issue.

  This is CVE-2026-27144 and Go issue https://go.dev/issue/78371.

- cmd/compile: possible memory corruption after bound check elimination

  Previously, slices and arrays accessed using induction variables
  were sometimes incorrectly proved in-bound. If the induction variable
  used for indexing were to overflow or underflow, it could allow access
  to memory beyond the scope of the original slice or array.

  To prevent this behavior, the compiler ensures that any mutated induction
  variable that overflows/underflows with respect to its loop condition
  is not used for bound check elimination.

  Thank you to Jakub Ciolek - https://ciolek.dev/ for reporting this issue.

  This is CVE-2026-27143 and Go issue https://go.dev/issue/78333.

- archive/tar: unbounded allocation when parsing old format GNU sparse map

  tar.Reader could allocate an unbounded amount of memory when reading
  a maliciously-crafted archive containing a large number of sparse
  regions encoded in the "old GNU sparse map" format.

  We now limit both the number of old GNU sparse map extension blocks,
  and the total number of sparse file entries, regardless of encoding.

  Thanks to Colin Walters (wal...@verbum.org) who initially reported this issue.
  Thanks also to Uuganbayar Lkhamsuren (https://github.com/uug4na) and Jakub
  Ciolek
  who additionally reported this issue.

  This is CVE-2026-32288 and Go issue https://go.dev/issue/78301.

- crypto/tls: multiple key update handshake messages can cause connection to
  deadlock

  If one side of the TLS connection sends multiple key update messages
  post-handshake in a single record, the connection can deadlock, causing
  uncontrolled consumption of resources. This can lead to a denial of service.

  This only affects TLS 1.3.

  Thank you to Jakub Ciolek - https://ciolek.dev/ for reporting this issue.

  This is CVE-2026-32283 and Go issue https://go.dev/issue/78334.

- cmd/go: trust layer bypass when using cgo and SWIG

  A well-crafted SWIG source file could take advantage
  of a file-naming convention used inside the trust
  boundary of the cgo compiler. Doing so could result
  in arbitrary code execution during build time.

  SWIG files are disallowed from using this convention.

  Thank you to Juho Forsén of Mattermost for reporting this issue.

  This is CVE-2026-27140 and Go issue https://go.dev/issue/78335.

- crypto/x509: unexpected work during chain building

  During chain building, the amount of work that is done is not correctly
  limited
  when a large number of intermediate certificates are passed in
  VerifyOptions.Intermediates, which can lead to a denial of service. This
  affects
  both direct users of crypto/x509 and users of crypto/tls.

  Thank you to Jakub Ciolek - https://ciolek.dev/ for reporting this issue.

  This is CVE-2026-32280 and Go issue https://go.dev/issue/78282.

- crypto/x509: inefficient policy validation

  Validating certificate chains which use policies is unexpectedly inefficient
  when certificates in the chain contain a very large number of policy mappings,
  possibly causing denial of service.

  This only affects validation of otherwise trusted certificate chains, issued
  by
  a root CA in the VerifyOptions.Roots CertPool, or in the system certificate
  pool.

  Thank you to Jakub Ciolek - https://ciolek.dev/ for reporting this issue.

  This is CVE-2026-32281 and Go issue https://go.dev/issue/78281.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-08 00:45:45 +02:00
dependabot[bot]andGitHub b23c1a2d76 build(deps): bump github/codeql-action from 4.34.1 to 4.35.1
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.34.1 to 4.35.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/38697555549f1db7851b81482ff19f1fa5c4fedc...c10b8064de6f491fea524254123dbe5e09572f13)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.35.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-06 08:45:04 +00:00
Sebastiaan van StijnandGitHub 9d7ad9ff18 Merge pull request #6911 from thaJeztah/bump_modules
vendor: moby/api v1.54.1, moby/client v0.4.0
2026-04-03 16:24:17 +02:00
Sebastiaan van Stijn c88681f8d8 vendor: moby/api v1.54.1, moby/client v0.4.0
full diffs:

- https://github.com/moby/moby/compare/ef0a1e449505...api/v1.54.1
- https://github.com/moby/moby/compare/ef0a1e449505...client/v0.4.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-03 15:54:11 +02:00
Paweł GronowskiandGitHub 84b884f383 Merge pull request #6909 from thaJeztah/update_authors_mailmap
update AUTHORS and .mailmap
2026-04-03 13:53:21 +02:00
Paweł GronowskiandGitHub d6169a5ea9 Merge pull request #6910 from thaJeztah/update_version
bump version to v29.4.0-dev
2026-04-03 13:46:49 +02:00
Sebastiaan van Stijn 5ddc1553ae bump version to v29.4.0-dev
This file is only used as default if no version is specified. We
should probably get rid of this, but let's update it to better
reflect the version that developer builds are building.

https://github.com/docker/cli/blob/d48fb9f9f7bdb6e0ef37dbde68612a1704cad46e/docker.Makefile#L22

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-03 13:28:17 +02:00
Sebastiaan van Stijn a347d9e103 update AUTHORS and .mailmap
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-03 13:26:29 +02:00
Sebastiaan van StijnandGitHub 699b029b57 Merge pull request #6908 from thaJeztah/bump_runewidth
vendor: github.com/mattn/go-runewidth v0.0.22
2026-04-03 12:58:59 +02:00
Paweł GronowskiandGitHub 512607a396 Merge pull request #6889 from YoanWai/docs/prune-filter-behavior
docs: clarify multiple --filter behavior in prune commands
2026-04-03 12:53:43 +02:00
Sebastiaan van Stijn 5fca671ef4 vendor: github.com/mattn/go-runewidth v0.0.22
full diff: https://github.com/mattn/go-runewidth/compare/v0.0.21...v0.0.22

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-03 12:53:08 +02:00
Paweł GronowskiandGitHub 753b10228f Merge pull request #6893 from thaJeztah/bump_moby
vendor: moby/client and moby/api master
2026-04-03 12:49:36 +02:00
Sebastiaan van Stijn 42da40a605 vendor: moby/client and moby/api master
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-03 12:44:53 +02:00
Paweł GronowskiandGitHub 7d4f9bd581 Merge pull request #6872 from thaJeztah/link_completions
shell completions: add shell completion for `docker rm --link` and exclude legacy links for container names
2026-04-03 12:37:11 +02:00
Paweł GronowskiandGitHub 2daa2c31e8 Merge pull request #6876 from thaJeztah/stats_optimize
docker stats: assorted fixes and optimizations in rendering
2026-04-03 12:34:04 +02:00
Paweł GronowskiandGitHub d8a85fba27 Merge pull request #6875 from thaJeztah/optimize_formatter
cli/command/formatter: assorted fixes and cleanups
2026-04-03 12:32:39 +02:00
Sebastiaan van StijnandGitHub efddff6549 Merge pull request #6907 from docker/dependabot/github_actions/codecov/codecov-action-6.0.0
build(deps): bump codecov/codecov-action from 5.5.3 to 6.0.0
2026-04-03 12:32:27 +02:00
Paweł GronowskiandGitHub 72beec9840 Merge pull request #6906 from thaJeztah/stream_preserve_file
cli/streams: Out, In: preserve original os.File when available
2026-04-03 12:29:30 +02:00
dependabot[bot]andGitHub 0029d5936a build(deps): bump codecov/codecov-action from 5.5.3 to 6.0.0
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.5.3 to 6.0.0.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/1af58845a975a7985b0beb0cbe6fbbb71a41dbad...57e3a136b779b570ffcdbf80b3bdc90e7fab3de2)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-03 08:44:33 +00:00
Sebastiaan van Stijn e7cbaafa9d cli/command/container: statsFormatWrite: inline render func
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 22:00:04 +02:00
Sebastiaan van Stijn c44a4d9758 cli/command/container: RunStats: avoid bytes to strings conversions
This code is using a `bytes.Buffer` to render the stats, before writing
the results to the CLI's output. Let's try to use bytes where possible
instead of converting to a string;

- Use the buffer's `Write` (and `Out().Write`) to write directly to the
  buffer/writer where possible.
- Use `io.WriteString` instead of `fmt.Printf`
- Use `bytes.SplitSeq` instead of `strings.SplitSeq`

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 21:59:49 +02:00
Sebastiaan van Stijn d92d1187fc cli/command/container: RunStats: rename buffer var for brevity
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 21:59:10 +02:00
Sebastiaan van Stijn ee88c60a5e cli/command/container: stats: add snapshot method
Move logic to capture a snapshot of the current stats to the stats struct.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 21:59:05 +02:00
Sebastiaan van Stijn 4c5efd61ea cli/command/container: fix buffer reuse when printing stats
Don't write lines back into the same buffer that's being read from when
clearing lines; add a separate output buffer to construct the output,
then write it to the CLI's output at once (to prevent terminal flicker).

Relates to / introduced in cb2f95ceee.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 21:50:31 +02:00
Sebastiaan van Stijn b309524f60 cli/command/formatter: NewStats: update GoDoc and add TODO
Update the GoDoc to better align with the actual implementation. The
"idOrName" is used for fuzzy-matching the container, which can result
in multiple stats for the same container:

    docker ps --format 'table {{.ID}}\t{{.Names}}'
    CONTAINER ID   NAMES
    b49e6c21d12e   quizzical_maxwell

    docker stats --no-stream quizzical_maxwell b49e6c21d12e b49e6
    CONTAINER ID   NAME                CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O        PIDS
    b49e6c21d12e   quizzical_maxwell   0.10%     140.8MiB / 7.653GiB   1.80%     3.11MB / 13.4kB   115MB / 1.12MB   28
    b49e6c21d12e   quizzical_maxwell   0.10%     140.8MiB / 7.653GiB   1.80%     3.11MB / 13.4kB   115MB / 1.12MB   28
    b49e6c21d12e   quizzical_maxwell   0.10%     140.8MiB / 7.653GiB   1.80%     3.11MB / 13.4kB   115MB / 1.12MB   28

We should resolve the canonical ID once, then use that as reference
to prevent duplicates. Various  parts in the code compare Container
against "ID" only (not considering "name" or "ID-prefix").

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 21:11:48 +02:00
Sebastiaan van Stijn abd2e211b9 cli/command/formatter: add Format.templateString, remove Context.preFormat
The `Context.preFormat` method normalizes the Format as given by the user,
and handles (e.g.) stripping the "table" prefix and replacing the "json"
format for the actual format (`{{json .}}`).

The method used a `finalFormat` field on the Context as intermediate,
and was required to be called before executing the format.

This patch adds a `Format.templateString()` method that returns the
parsed format instead of storing it on the Context. It is currently
not exported, but something we could consider in future.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 21:11:40 +02:00
Sebastiaan van Stijn cb615a9772 cli/command/formatter: Context.postFormat: remove redundant buffer
A tabwriter is backed by a buffer already, because it needs to re-flow columns
based on content written to it. This buffer was added in [moby@ea61dac9e6] as
part of a new feature to allow for custom delimiters; neither the patch, nor
code-review on the PR mention the extra buffer, so it likely was just overlooked.

This patch;

- removes the redundant buffer
- adds an early return for cases where no tabwriter is used.

[moby@ea61dac9e6]: https://github.com/moby/moby/commit/ea61dac9e6d04879445f9c34729055ac1bb15050

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 20:59:30 +02:00
Sebastiaan van Stijn f7a909d56b cli/command/formatter: optimize ContainerContext.Names
Optimize formatting of container name(s);

- Inline `StripNamePrefix` in the loop, so that we don't have to
  construct a new slice with names (in most cases only to pick
  the first one).
- Don't use `strings.Split`, as it allocates a new slice and we only
  used it to check if the container-name was a legacy-link (contained
  slashes).
- Use a string-builder to concatenate names when not truncating instead
  of using an intermediate slice (and `strings.Join`).

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 20:59:29 +02:00
Sebastiaan van Stijn 94d4929a04 cli/streams: Out, In: preserve original os.File when available
Preserve the original *os.File, if available, and add a File() method
to return it.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 20:52:30 +02:00
Sebastiaan van StijnandGitHub 2cc9fe1438 Merge pull request #6900 from thaJeztah/cli_stream_cleanups
cli/streams: assorted cleanups
2026-04-02 20:52:06 +02:00
Paweł GronowskiandGitHub 38e44e4125 Merge pull request #6905 from thaJeztah/bump_trust_deps
cmd/docker-trust: bump dependencies
2026-04-02 18:15:34 +02:00
Sebastiaan van Stijn 526dfffc26 cli/streams: simplify CheckTty
This function is very specific to attaching to containers, and probably
helps clarity to inline it where used.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 18:11:50 +02:00
Sebastiaan van Stijn 48721c2340 cli/streams: don't depend on embedding
Define explicit wrapper methods instead of depending on the embedded
commonStreams struct.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 17:29:55 +02:00
Sebastiaan van Stijn 39e82e6524 cli/streams: move constructors to the start
It's more idiomatic to define the constructor before methods.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 17:29:55 +02:00
Sebastiaan van Stijn 34805dd013 cli/streams: (In|Out).SetRawTerminal: dry
Move the code to the commonStream type, which is where the actual
state field is kept.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 17:29:55 +02:00
Sebastiaan van Stijn 6e1f03c2e0 cmd/docker-trust: bump dependencies
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 17:06:57 +02:00
Sebastiaan van StijnandGitHub 7639343b30 Merge pull request #6867 from docker/update-swarm-docs
Updated example tokens in swarm docs
2026-04-02 15:42:59 +02:00
87a222158d Updated example tokens in swarm docs
Prevent security scanners from detecting them as eaked secrets.

Co-authored-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
Signed-off-by: Alexandre Vallières-Lagacé <alexandre.valliereslagace@docker.com>
Signed-off-by: Alexandre Vallières-Lagacé <alexandre@vallier.es>
2026-04-02 15:25:16 +02:00
Paweł GronowskiandGitHub fbfb69f7e9 Merge pull request #6873 from thaJeztah/simplify_chips
cli/command/image: getPossibleChips: simplify
2026-04-02 13:51:17 +02:00
Paweł GronowskiandGitHub 424955ddf7 Merge pull request #6904 from thaJeztah/bump_otels
vendor: go.opentelemetry.io/otel v1.42.0, otel/contrib v1.67.0
2026-04-02 13:43:10 +02:00
Paweł GronowskiandGitHub bbb6311c09 Merge pull request #6871 from thaJeztah/completions_no_dups
cli/command/completion: don't provide duplicate completions
2026-04-02 13:41:27 +02:00
Sebastiaan van StijnandGitHub 48afa03bba Merge pull request #6878 from zampani-docker/zampani/fix-plugin-force-exit-race
fix(cmd/docker): prevent race between force-exit goroutine and plugin wait
2026-04-02 12:54:38 +02:00
Sebastiaan van Stijn 98d978df6b vendor: go.opentelemetry.io/otel v1.42.0, otel/contrib v1.67.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 12:51:19 +02:00
Sebastiaan van Stijn a48ff6b591 cli/command/completion: don't provide duplicate completions
When completing for commands that accept multiple arguments, we did
not remove suggestions that were already consumed. This could be
confusing if there was only 1 suggestion, in which case every `<tab>`
would automatically suggest the same name again:

docker rm -fv magical_lumiere magical_lumiere  magical_lumiere

This patch adds a "Unique" helper to wrap a completion func to remove
completion results that are already consumed (i.e., appear in "args").

For example:

    # initial completion: args is empty, so all results are shown
    command <tab>
    one two three

    # "one" is already used so omitted
    command one <tab>
    two three

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 12:46:41 +02:00
Paweł GronowskiandGitHub 12b8ca4406 Merge pull request #6901 from thaJeztah/modernize
cli/command/formatter: modernize
2026-04-02 12:34:14 +02:00
Paweł GronowskiandGitHub dc9b6e553e Merge pull request #6874 from thaJeztah/future_proof_prefix
formatting: only strip "/" prefixes
2026-04-02 11:42:31 +02:00
Paweł GronowskiandGitHub f4d2906b56 Merge pull request #6894 from Rohan5commit/docs/fix-run-following-typo-20260331
docs: fix typo in run reference examples
2026-04-02 11:40:50 +02:00
Paweł GronowskiandGitHub 6998987257 Merge pull request #6866 from thaJeztah/bump_x_deps
vendor: update golang.org/x/* dependencies
2026-04-02 11:39:38 +02:00
Paweł GronowskiandGitHub 5e856302bf Merge pull request #6899 from thaJeztah/bump_compress
vendor: github.com/klauspost/compress v1.18.5
2026-04-02 11:37:51 +02:00
Paweł GronowskiandGitHub 253dc62658 Merge pull request #6903 from thaJeztah/bump_jose
vendor: github.com/go-jose/go-jose/v4 v4.1.4
2026-04-02 11:36:52 +02:00
Paweł GronowskiandGitHub a9ea8b23fa Merge pull request #6825 from thaJeztah/bump_go1.26
update to go1.26.1
2026-04-02 11:32:46 +02:00
Sebastiaan van Stijn 091afa4957 vendor: github.com/go-jose/go-jose/v4 v4.1.4
Fixes CVE-2026-34986 / GHSA-78h2-9frx-2jm8

full diff: https://github.com/go-jose/go-jose/compare/v4.1.3...v4.1.4

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 02:39:43 +02:00
Sebastiaan van Stijn 968ad0ea6c vendor: golang.org/x/net v0.52.0
full diff: https://cs.opensource.google/go/x/net/+/refs/tags/v0.50.0...refs/tags/v0.52.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 02:31:42 +02:00
Sebastiaan van Stijn 78fb018754 vendor: golang.org/x/time v0.15.0
full diff: https://cs.opensource.google/go/x/time/+/refs/tags/v0.14.0...refs/tags/v0.15.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 02:29:46 +02:00
Sebastiaan van Stijn 18739a5ef6 vendor: golang.org/x/term v0.41.0
full diff: https://cs.opensource.google/go/x/term/+/refs/tags/v0.40.0...refs/tags/v0.41.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 02:29:45 +02:00
Sebastiaan van Stijn 9b21846cde vendor: golang.org/x/text v0.35.0
full diff: https://cs.opensource.google/go/x/text/+/refs/tags/v0.34.0...refs/tags/v0.35.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 02:29:45 +02:00
Sebastiaan van Stijn c22bf3c77e vendor: golang.org/x/mod v0.34.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 02:29:32 +02:00
Sebastiaan van Stijn d792fc53b7 vendor: golang.org/x/sync v0.20.0
full diff: https://cs.opensource.google/go/x/sync/+/refs/tags/v0.19.0...refs/tags/v0.20.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 02:17:17 +02:00
Sebastiaan van Stijn e9664a72ea vendor: golang.org/x/sys v0.42.0
full diff: https://cs.opensource.google/go/x/sys/+/refs/tags/v0.41.0...refs/tags/v0.42.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 02:17:17 +02:00
Sebastiaan van Stijn fb776458cb update to go1.26.1
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 02:12:08 +02:00
Sebastiaan van Stijn 62d80156e1 ci: pin remaining actions
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-02 02:11:26 +02:00
Sebastiaan van StijnandGitHub a3c4d64755 Merge pull request #6897 from thaJeztah/bump_minimum_go
update minimum go version to go1.25
2026-04-01 20:12:45 +02:00
Paweł GronowskiandGitHub eecf81316d Merge pull request #6898 from thaJeztah/bump_patternmatcher
vendor: github.com/moby/patternmatcher v0.6.1
2026-04-01 18:39:33 +02:00
Paweł GronowskiandGitHub e67dba1189 Merge pull request #6896 from thaJeztah/bump_grpc
vendor: google.golang.org/grpc v1.79.3
2026-04-01 18:39:30 +02:00
Paweł GronowskiandGitHub fea2465d65 Merge pull request #4723 from thaJeztah/govalidator
ci: add module compatibility check
2026-04-01 16:37:45 +02:00
Sebastiaan van Stijn ea74248e8e cli/command/formatter: modernize
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-01 15:43:34 +02:00
Sebastiaan van Stijn 5efed5fa30 vendor: github.com/klauspost/compress v1.18.5
- zstd: Fix crash when changing encoder dictionary with same ID

full diff: https://github.com/klauspost/compress/compare/v1.18.4...v1.18.5

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-01 00:38:50 +02:00
Sebastiaan van Stijn cd9e5ae84f vendor: github.com/moby/patternmatcher v0.6.1
- fix panic / nil pointer dereference on invalid patterns

full diff: https://github.com/moby/patternmatcher/compare/v0.6.0...v0.6.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-01 00:24:02 +02:00
Sebastiaan van Stijn bf6a1e1fcf cli-plugins/socket: modernize
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-01 00:19:32 +02:00
Sebastiaan van Stijn 8f7dc04070 update minimum go version to go1.25
- drop support for go1.24
- update vendor.mod to go1.25.0
- update //go:build tags to go1.25

The golang.org/x/ dependencies now require go1.25 as a minimum,
so updating our build tags accordingly.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-04-01 00:19:04 +02:00
Sebastiaan van Stijn a14db81c9c vendor: google.golang.org/grpc v1.79.3
fixes [CVE-2026-33186] / [GHSA-p77j-4mvh-x3m3]

full diff: https://github.com/grpc/grpc-go/compare/v1.78.0...v1.79.3

[CVE-2026-33186]: https://www.cve.org/CVERecord?id=CVE-2026-33186
[GHSA-p77j-4mvh-x3m3]: https://github.com/grpc/grpc-go/security/advisories/GHSA-p77j-4mvh-x3m3

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-31 09:22:30 +02:00
Codex e660030f3a docs: fix typo in run reference
Signed-off-by: Codex <codex@openai.com>
2026-03-31 10:31:44 +08:00
Sebastiaan van Stijn 58c1585b49 gha: validate gocompat
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-26 17:53:31 +01:00
Sebastiaan van Stijn ea42337d01 implement module compatibility check
This package imports all "importable" packages, i.e., packages that:

- are not applications ("main")
- are not internal
- and that have non-test go-files

We do this to verify that our code can be consumed as a dependency
in "module mode". When using a dependency that does not have a go.mod
(i.e.; is not a "module"), go implicitly generates a go.mod. Lacking
information from the dependency itself, it assumes "go1.16" language
(see [DefaultGoModVersion]). Starting with Go1.21, go downgrades the
language version used for such dependencies, which means that any
language feature used that is not supported by go1.16 results in a
compile error;

    # github.com/docker/cli/cli/context/store
    /go/pkg/mod/github.com/docker/cli@v25.0.0-beta.2+incompatible/cli/context/store/storeconfig.go:6:24: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    /go/pkg/mod/github.com/docker/cli@v25.0.0-beta.2+incompatible/cli/context/store/store.go:74:12: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)

These errors do NOT occur when using GOPATH mode, nor do they occur
when using "pseudo module mode" (the "-mod=mod -modfile=vendor.mod"
approach used in this repository).

As a workaround for this situation, we must include "//go:build" comments
in any file that uses newer go-language features (such as the "any" type
or the "min()", "max()" builtins).

From the go toolchain docs (https://go.dev/doc/toolchain):

> The go line for each module sets the language version the compiler enforces
> when compiling packages in that module. The language version can be changed
> on a per-file basis by using a build constraint.
>
> For example, a module containing code that uses the Go 1.21 language version
> should have a go.mod file with a go line such as go 1.21 or go 1.21.3.
> If a specific source file should be compiled only when using a newer Go
> toolchain, adding //go:build go1.22 to that source file both ensures that
> only Go 1.22 and newer toolchains will compile the file and also changes
> the language version in that file to Go 1.22.

This file is a generated module that imports all packages provided in
the repository, which replicates an external consumer using our code
as a dependency in go-module mode, and verifies all files in those
packages have the correct "//go:build <go language version>" set.

To test this package:

    make shell
    make -C ./internal/gocompat/
    make: Entering directory '/go/src/github.com/docker/cli/internal/gocompat'
    GO111MODULE=off go generate .
    GO111MODULE=on go mod tidy
    GO111MODULE=on go test -v
    # github.com/docker/cli/templates
    ../../templates/templates.go:13:17: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    # github.com/docker/cli/cli/compose/template
    ../../cli/compose/template/template.go:98:45: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/compose/template/template.go:105:27: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/compose/template/template.go:141:28: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    # github.com/docker/cli/cli/compose/types
    ../../cli/compose/types/types.go:53:22: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/compose/types/types.go:86:34: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/compose/types/types.go:105:22: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/compose/types/types.go:137:34: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/compose/types/types.go:211:20: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/compose/types/types.go:343:35: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/compose/types/types.go:442:40: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/compose/types/types.go:469:24: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/compose/types/types.go:490:24: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/compose/types/types.go:587:28: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/compose/types/types.go:442:40: too many errors
    # github.com/docker/cli/cli/context/store
    ../../cli/context/store/storeconfig.go:6:24: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/context/store/store.go:74:12: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/context/store/store.go:75:23: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/context/store/metadatastore.go:43:58: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/context/store/metadatastore.go:48:22: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/context/store/metadatastore.go:80:30: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    # github.com/docker/cli/cli/command/idresolver
    ../../cli/command/idresolver/idresolver.go:6:2: "github.com/docker/docker/api/types" imported and not used
    ../../cli/command/idresolver/idresolver.go:7:2: "github.com/docker/docker/api/types/swarm" imported and not used
    ../../cli/command/idresolver/idresolver.go:9:2: "github.com/pkg/errors" imported and not used
    ../../cli/command/idresolver/idresolver.go:28:49: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/command/idresolver/idresolver.go:58:53: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    # github.com/docker/cli/cli/compose/schema
    ../../cli/compose/schema/schema.go:20:46: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/compose/schema/schema.go:27:53: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/compose/schema/schema.go:45:32: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/compose/schema/schema.go:66:33: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    FAIL	gocompat [build failed]
    make: *** [Makefile:3: verify] Error 1
    make: Leaving directory '/go/src/github.com/docker/cli/internal/gocompat'

[DefaultGoModVersion]: https://github.com/golang/go/blob/58c28ba286dd0e98fe4cca80f5d64bbcb824a685/src/cmd/go/internal/gover/version.go#L15-L24
[2]: https://go.dev/doc/toolchain

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-26 17:53:31 +01:00
Paweł GronowskiandGitHub 538ee85f39 Merge pull request #6888 from thaJeztah/add_build_tags
cli-plugins/hooks: add missing "go:build" comments
2026-03-26 16:32:44 +01:00
Yoan Wainmann d573c171fe docs: clarify multiple --filter behavior in prune commands
Document how multiple --filter flags interact in prune commands:
different filter keys are ANDed (all conditions must match), while
multiple values for the same key are ORed (any value can match).

This addresses a gap in the documentation where users could not
determine whether multiple filters were combined with AND or OR
logic, which is especially important for prune commands where
the wrong assumption could lead to unintended data removal.

Closes #5899

Signed-off-by: Yoan Wainmann <yoan@mreshet.co.il>
2026-03-25 18:13:49 +02:00
Sebastiaan van Stijn 07bb479a45 cli-plugins/hooks: add missing "go:build" comments
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-25 16:32:18 +01:00
Paweł GronowskiandGitHub 9637f1b364 Merge pull request #6886 from thaJeztah/pin_actions
ci: pin actions to digests
2026-03-25 15:22:16 +01:00
Sebastiaan van Stijn 97b9e04a94 ci: pin actions to digests
As a follow-up, we should use the full version (major.minor.patch).

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-25 14:58:57 +01:00
Michael ZampaniandClaude Sonnet 4.6 2bc4307816 fix(cmd/docker): prevent race between force-exit goroutine and plugin wait
When a plugin ignores context cancellation and the user sends 3 SIGINTs,
the CLI kills the plugin with SIGKILL. Previously the signal goroutine
called os.Exit(1) directly; a race existed where plugincmd.Run() could
return first (plugin was SIGKILL'd, so ws.ExitStatus() = -1) and the
main goroutine would call os.Exit(-1) = exit code 255 before the
goroutine reached os.Exit(1).

Fix by moving exit-code ownership to the main goroutine. The signal
goroutine closes forceExitCh before calling Kill(), guaranteeing the
channel is closed before plugincmd.Run() returns (the plugin can only
die after Kill() delivers SIGKILL; Run() only returns after the process
is reaped). The main goroutine checks forceExitCh after Run() returns
and performs the print + os.Exit(1) itself.

Also return from the signal goroutine after the force-kill to prevent
further loop iterations from calling close(forceExitCh) a second time
(which would panic), in case additional signals arrive while the kill
is in flight.

Fixes a flaky failure in TestPluginSocketCommunication/detached/
the_main_CLI_exits_after_3_signals where exit code 255 was observed
instead of 1 on loaded CI runners (RC Docker on Alpine).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Michael Zampani <michael.zampani@docker.com>
2026-03-21 15:31:12 -07:00
Sebastiaan van Stijn 9c117d3c5d cli/command/container: add shell completion for docker rm --link
When linking containers through legacy links, a container can get multiple
names; its own name, and a name for each link it's providing:

    # create two containers with links between them
    docker run -d --name one nginx:alpine
    docker run -d --name two --link one:link1 --link one:link2 --link one:link3 nginx:alpine

    docker rm --link <tab>
    two/link1 two/link2 two/link3

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-20 18:24:19 +01:00
Sebastiaan van Stijn df57ff7201 cli/command/completion: ContainerNames: skip legacy link names
Inline StripNamePrefix and skip legacy links for completion.
Legacy links can be removed from a container, but only when
using `docker [container] rm --link <link-name>`.

When linking containers through legacy links, a container can get multiple
names; its own name, and a name for each link it's providing:

    # create two containers with links between them

    docker run -d --name one nginx:alpine
    docker run -d --name two --link one:link1 --link one:link2 --link one:link3 nginx:alpine

    # container "one" now has multiple names
    docker ps --no-trunc --format '{{.Names}}'
    two
    one,two/link1,two/link2,two/link3

    # running `docker rm --link` with a link-name removes a link:

    docker rm --link two/link3
    docker ps --no-trunc --format '{{.Names}}'
    two
    one,two/link1,two/link2

    # but without `--link`, it resolves the linked container and removes it:
    docker rm -fv two/link2
    two/link2
    docker ps --no-trunc --format '{{.Names}}'
    two

Legacy links are deprecated, and this can be confusing, so let's not provide
completion for secondary names.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-20 18:17:43 +01:00
Sebastiaan van Stijn dfda342e51 cli/command/formatter: StripNamePrefix only strip "/" prefix
This code was assuming the API always returns container names with
a "/" prefix. While this is currently correct, we may at some point
stop doing so.

This patch changes the code to only trim "/" as prefix and not any
other character.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-20 17:43:37 +01:00
Sebastiaan van Stijn b5efd66ba6 cli/command/container: stats: make stripping "/" prefix deterministic
This code was assuming the API always returns container names with
a "/" prefix. While this is currently correct, we may at some point
stop doing so.

This patch changes the code to only trim "/" as prefix and not any
other character.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-20 17:43:36 +01:00
Sebastiaan van Stijn 64c8d68045 cli/command/image: getPossibleChips: simplify
Extract the check to a closure that filters in-place, and
run the check in a single loop.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-20 17:33:14 +01:00
Sebastiaan van StijnandGitHub 7922984193 Merge pull request #6863 from thaJeztah/fix_stats
fix: docker stats --all: remove containers when removed
2026-03-19 15:44:15 +01:00
Sebastiaan van StijnandGitHub bcfb717a31 Merge pull request #6859 from thaJeztah/plugin_metadata
cli-plugins: separate hook types from manager and refactor
2026-03-19 14:47:29 +01:00
Sebastiaan van Stijn 4bf4d567bd cli-plugins/hooks: PrintNextSteps: slight cleanup
- skip aec to construct the formatting and use a const instead
- skip fmt.Println and write directly to the writer
- move newlines outside of the "bold" formatting

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-19 12:24:36 +01:00
Sebastiaan van Stijn dd1f7f5856 cli-plugins/hooks: simplify templating formats
This allows for slighly cleaner / more natural placeholders, as it
doesn't require the context (`.`) to be specified;

- `{{command}}` instead of `{{.Command}}` or `{{command .}}`
- `{{flagValue "my-flag"}}` instead of `{{.FlagValue "my-flag"}} or `{{flagValue . "my-flag"}}`

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-19 12:24:35 +01:00
Sebastiaan van Stijn 9243240346 cli-plugins/hooks: add commandInfo type for templating
Define a local type for methods to expose to the template, instead of
passing the cobra.Cmd. This avoids templates depending on features
exposed by Cobra that are not part of the contract, and slightly
decouples the templat from the Cobra implementation.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-19 12:24:35 +01:00
Sebastiaan van Stijn 4a1b2ef2c5 cli-plugins/hooks: update godoc
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-19 12:24:35 +01:00
Sebastiaan van Stijn 4142d4026e cli-plugins/hooks: detect if templating is needed
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-19 12:24:35 +01:00
Sebastiaan van Stijn cd053606a6 cli-plugins/hooks: slight tweaks in templates
- use `%q` instead of manually quoting the string
- use `%d` instead of manually converting the number to a string

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-19 12:24:35 +01:00
Sebastiaan van Stijn aadfe6214f cli-plugins/hooks: update tests
- add basic unit-test for the template utilities
- make sure the template parsing tests test both the current
  template produced by the utilities, as well as a fixture
- rewrite the printer test to use fixtures
- use blackbox testing ("hooks_test")

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-19 12:24:34 +01:00
Sebastiaan van Stijn dce201d6ee cli-plugins/hooks: move template utils separate from render code
These utilities are used by CLI-plugins; separate them from the render
code, which is used by teh CLI-plugin manager.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-19 12:24:34 +01:00
Sebastiaan van Stijn e26f94d823 cli-plugins/hooks: add JSON labels, omitzero
Add labels to define the expected casing and don't serialize
empty fields.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-19 12:24:27 +01:00
Sebastiaan van Stijn 0431e4d23c cli-plugins/hooks: rename HookType to ResponseType
Rename the type to match the struct it's used for. Also;

- Fix the type of the NextSteps const
- Don't use iota for values; the ResponseType is used as
  part of the "wire" format, which means that plugins using
  the value can use a different version of the module code;
  using iota increases the risk of (accidentally) changing
  values, which would break the wire format.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-18 12:20:48 +01:00
Sebastiaan van Stijn 607ebfca5d cli-plugins/hooks: rename HookMessage to Response
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-18 12:20:48 +01:00
Sebastiaan van Stijn 60180924e3 cli-plugins/manager: move HookPluginData to hooks.Request
Separate types used by plugins from the manager code.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-18 12:20:48 +01:00
Sebastiaan van Stijn dd91ed3f2d cli-plugins/manager: refactor for easier debugging
Extract the code inside the loop to a closure, so that we can more
easily set up debug-logging.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-18 12:20:42 +01:00
Sebastiaan van Stijn 0501cf8293 cli-plugins/manager: simplify ctx-cancel check
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-18 12:18:46 +01:00
Sebastiaan van Stijn 5343bdc792 cli-plugins/manager: Plugin.RunHook: improve error message
Currently, the error was a plain "exit status 1"; make the error
message more informative if we need it :)

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-18 12:18:41 +01:00
Sebastiaan van Stijn 5fb5e0b0da docker stats --all: remove containers when removed
Before this patch, running `docker stats --all` would continue showing
all containers once observed. For example;

    CONTAINER ID   NAME                 CPU %     MEM USAGE / LIMIT     MEM %     NET I/O         BLOCK I/O        PIDS
    f2a785b0cd5f   foo1                 0.00%     8.535MiB / 7.653GiB   0.11%     1.12kB / 126B   0B / 12.3kB      11
    fc191b27517f   foo2                 0.00%     8.531MiB / 7.653GiB   0.11%     998B / 126B     0B / 12.3kB      11
    5040185fba53   foo3                 0.00%     8.578MiB / 7.653GiB   0.11%     872B / 126B     0B / 8.19kB      11

WHen removing `foo2`, the container would continue to be listed:

    CONTAINER ID   NAME                 CPU %     MEM USAGE / LIMIT     MEM %     NET I/O         BLOCK I/O        PIDS
    f2a785b0cd5f   foo1                 0.00%     8.535MiB / 7.653GiB   0.11%     1.12kB / 126B   0B / 12.3kB      11
    fc191b27517f   foo2                 --        -- / --               --        --              --               --
    5040185fba53   foo3                 0.00%     8.578MiB / 7.653GiB   0.11%     872B / 126B     0B / 12.3kB      11

Starting a new `foo2` container would now produce multiple entries:

    CONTAINER ID   NAME                 CPU %     MEM USAGE / LIMIT     MEM %     NET I/O         BLOCK I/O        PIDS
    f2a785b0cd5f   foo1                 0.00%     8.535MiB / 7.653GiB   0.11%     1.25kB / 126B   0B / 12.3kB      11
    fc191b27517f   foo2                 --        -- / --               --        --              --               --
    5040185fba53   foo3                 0.00%     8.578MiB / 7.653GiB   0.11%     998B / 126B     0B / 12.3kB      11
    dba11b9e1ba9   foo2                 0.00%     8.578MiB / 7.653GiB   0.11%     872B / 126B     0B / 8.19kB      11

Repeat that, and the list would continue to grow;

    CONTAINER ID   NAME                 CPU %     MEM USAGE / LIMIT     MEM %     NET I/O         BLOCK I/O        PIDS
    f2a785b0cd5f   foo1                 0.00%     8.535MiB / 7.653GiB   0.11%     1.25kB / 126B   0B / 12.3kB      11
    fc191b27517f   foo2                 --        -- / --               --        --              --               --
    5040185fba53   foo3                 0.00%     8.578MiB / 7.653GiB   0.11%     998B / 126B     0B / 12.3kB      11
    dba11b9e1ba9   foo2                 --        -- / --               --        --              --               --
    193a6dcfaa2d   foo2                 --        -- / --               --        --              --               --
    bf50e58085c6   foo2                 0.00%     8.539MiB / 7.653GiB   0.11%     872B / 126B     0B / 8.19kB      11

After this patch, containers are removed when we observe a `destroy` event;

    CONTAINER ID   NAME                 CPU %     MEM USAGE / LIMIT     MEM %     NET I/O         BLOCK I/O        PIDS
    f2a785b0cd5f   foo1                 0.00%     8.535MiB / 7.653GiB   0.11%     1.5kB / 126B    0B / 12.3kB      11
    5040185fba53   foo3                 0.00%     8.578MiB / 7.653GiB   0.11%     1.25kB / 126B   0B / 12.3kB      11
    bf50e58085c6   foo2                 0.00%     8.539MiB / 7.653GiB   0.11%     872B / 126B     0B / 12.3kB      11

Containers are added when created, so in the example above, the new `foo2`
is added at the end:

    CONTAINER ID   NAME                 CPU %     MEM USAGE / LIMIT     MEM %     NET I/O         BLOCK I/O        PIDS
    f2a785b0cd5f   foo1                 0.00%     8.535MiB / 7.653GiB   0.11%     1.5kB / 126B    0B / 12.3kB      11
    5040185fba53   foo3                 0.00%     8.578MiB / 7.653GiB   0.11%     1.25kB / 126B   0B / 12.3kB      11
    bf50e58085c6   foo2                 0.00%     8.539MiB / 7.653GiB   0.11%     872B / 126B     0B / 12.3kB      11

If a container dies, and `--all` is set, we continue listing it, but stats
are not updated while the container is stopped (we should consider resetting
the stats and show `-- / --` to be more clear that we don't have the container
running).

Here's with `foo3` stopped:

    CONTAINER ID   NAME                 CPU %     MEM USAGE / LIMIT     MEM %     NET I/O         BLOCK I/O        PIDS
    f2a785b0cd5f   foo1                 0.00%     8.535MiB / 7.653GiB   0.11%     1.5kB / 126B    0B / 12.3kB      11
    5040185fba53   foo3                 0.00%     0B / 0B               0.00%     0B / 0B         0B / 0B          0
    bf50e58085c6   foo2                 0.00%     8.539MiB / 7.653GiB   0.11%     872B / 126B     0B / 12.3kB      11

Starting the container continues updating its stats:

    CONTAINER ID   NAME                 CPU %     MEM USAGE / LIMIT     MEM %     NET I/O         BLOCK I/O        PIDS
    f2a785b0cd5f   foo1                 0.00%     8.535MiB / 7.653GiB   0.11%     1.63kB / 126B   0B / 12.3kB      11
    5040185fba53   foo3                 0.00%     8.496MiB / 7.653GiB   0.11%     872B / 126B     0B / 0B          11
    bf50e58085c6   foo2                 0.00%     8.539MiB / 7.653GiB   0.11%     998B / 126B     0B / 12.3kB      11

When running without `--all`, we continue to remove containers as soon as
possible (`die` events), but with `--all`, those events are ignored with
the expectation that the container might come back.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-17 21:05:44 +01:00
Sebastiaan van StijnandGitHub 743c385f78 Merge pull request #6862 from thaJeztah/update_golangci_lint_config
fix linting and update golangci-lint config
2026-03-17 21:05:01 +01:00
Sebastiaan van StijnandGitHub 434193ff78 Merge pull request #6865 from thaJeztah/cleanup_stats
cli/command/container: RunStats: pass ctx to stats event handlers and refractor to DRY
2026-03-17 21:03:38 +01:00
Sebastiaan van StijnandGitHub adc5466cba Merge pull request #6864 from thaJeztah/bump_runewidth
vendor: github.com/mattn/go-runewidth v0.0.21
2026-03-17 21:02:53 +01:00
Sebastiaan van Stijn 560db7d451 vendor: github.com/mattn/go-runewidth v0.0.21
full diff: https://github.com/mattn/go-runewidth/compare/v0.0.20...v0.0.21

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-17 18:36:02 +01:00
Sebastiaan van Stijn dc4abf8b99 golangci-lint: gocheckcompilerdirectives: ignore "//go:fix"
The linter has not been updated yet to recognize this directive.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-17 18:14:57 +01:00
Sebastiaan van Stijn 7f781688ed golangci-lint: remove outdated exclusion
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-17 18:14:54 +01:00
Sebastiaan van Stijn 21293265b1 cli/command/image/build: use t.Chdir() in tests
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-17 18:14:48 +01:00
Sebastiaan van Stijn 3f51d0a9d2 cli/command/container: RunStats: refactor to DRY
- update setHandler to accept multiple event-types
- pass a logger to the event-handlers with the common fields
  already set.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-17 18:05:33 +01:00
Sebastiaan van Stijn 9645db767a cli/command/container: RunStats: pass ctx to stats event handlers
Wire up the context explicitly instead of capturing it in the closures.
Also pass through the context to `watch` to replace the context.TODO()

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-17 18:05:33 +01:00
Sebastiaan van StijnandGitHub 499a4c50bd Merge pull request #6800 from 4RH1T3CT0R7/master
docker cp: report both content size and transferred size
2026-03-16 17:41:05 +01:00
Paweł GronowskiandGitHub bb0f76343a Merge pull request #6804 from icemc/update-docs-flocker-plugin
Removed EOL Flocker plugin reference from plugin documentations.
2026-03-16 09:40:12 +00:00
4RH1T3CT0R7 2bc66ecbc7 docker cp: report both content size and transferred size
When copying files with `docker cp`, the success message now shows both
the actual content size and the transferred (tar stream) size when they
differ, making it easier to understand compression and overhead:

    Successfully copied 2.01MB (transferred 2.53MB) to ctr:/dir

Extract copySummary helper to keep copyToContainer under the gocyclo
complexity threshold. Add unit tests for copySummary and stdin path.

Signed-off-by: 4RH1T3CT0R7 <iprintercanon@gmail.com>
2026-03-13 21:32:58 +03:00
Sebastiaan van StijnandGitHub 26d4525d46 Merge pull request #6817 from luojiyin1987/fix-plugin-cobra
fix: restore os.Args after plugin completion and fix error return
2026-03-13 15:28:03 +01:00
Ludovic Temgoua AbandaandSebastiaan van Stijn 33790e88d0 docs: use generic myplugin example for plugin documentation
- Removed EOL Flocker plugin reference from plugin documentations.
- docs: use generic myplugin example instead of VolumeDriver

Co-authored-by: Ludovic Temgoua Abanda <abandaludovic500@gmail.com>
Signed-off-by: Ludovic Temgoua Abanda <abandaludovic500@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-13 15:27:02 +01:00
luojiyinandSebastiaan van Stijn 6b1ba1ad84 fix: restore os.Args after plugin completion and fix error return
Signed-off-by: luojiyin <luojiyin@hotmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-13 14:48:45 +01:00
Sebastiaan van StijnandGitHub c3a17b9def Merge pull request #6839 from literally-user/6838-fix-font-representation
scripts/warn-outside-container: fix font representation
2026-03-13 14:36:20 +01:00
Sebastiaan van StijnandGitHub 9ca766c489 Merge pull request #6858 from gounthar/feat/add-riscv64-to-bin-image-cross
Add linux/riscv64 to bin-image-cross release target
2026-03-13 13:13:37 +01:00
Bruno Verachten 300d8231da feat: add linux/riscv64 to bin-image-cross release target
Add linux/riscv64 to the bin-image-cross platforms list so that
official release images include riscv64 binaries.

riscv64 is already present in the _platforms variable (line 38) and
used by the cross, dynbinary-cross, and plugins-cross targets. CI
already builds riscv64 binaries, but they are excluded from the
release image because bin-image-cross has its own platform list.

Closes #6857

Signed-off-by: Bruno Verachten <gounthar@gmail.com>
2026-03-12 10:53:47 +01:00
Sebastiaan van StijnandGitHub 5d8cc2c987 Merge pull request #6854 from thaJeztah/missing_buildtags
cli/command: add missing "go:build" comments
2026-03-10 17:31:48 +01:00
Sebastiaan van StijnandGitHub d0442edbfe Merge pull request #6845 from thaJeztah/cleanup_godoc
cli/config/credentials: ConvertToHostname: update godoc
2026-03-10 17:31:14 +01:00
Sebastiaan van StijnandGitHub 3897d9cc09 Merge pull request #6850 from thaJeztah/update_go1.25.8
update to go1.25.8
2026-03-10 17:30:40 +01:00
Sebastiaan van Stijn 9a471180cb cli/command: add missing "go:build" comments
- commit e8dc2fce32 modernized loops to
  range over int, which requires go1.22 or later.
- commit 85ebca52fd modernized code to
  use stdlib min/max, which requires go1.21 or later.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-10 11:18:58 +01:00
Sebastiaan van StijnandGitHub e2f38c4947 Merge pull request #6848 from docker/dependabot/github_actions/docker/metadata-action-6
build(deps): bump docker/metadata-action from 5 to 6
2026-03-06 16:30:00 +01:00
Sebastiaan van StijnandGitHub 13ec581924 Merge pull request #6847 from docker/dependabot/github_actions/docker/bake-action-7
build(deps): bump docker/bake-action from 6 to 7
2026-03-06 16:29:20 +01:00
Sebastiaan van Stijn f7d83cbae8 update to go1.25.8
go1.25.8 (released 2026-03-05) includes security fixes to the html/template,
net/url, and os packages, as well as bug fixes to the go command, the compiler,
and the os package. See the Go 1.25.8 milestone on our issue tracker for details.

- 1.25.8 https://github.com/golang/go/issues?q=milestone%3AGo1.25.8+label%3ACherryPickApproved
- diff: https://github.com/golang/go/compare/go1.25.7...go1.25.8
- 1.26.1 https://github.com/golang/go/issues?q=milestone%3AGo1.26.1+label%3ACherryPickApproved
- diff: https://github.com/golang/go/compare/go1.26.0...go1.26.1

---

We have just released Go versions 1.26.1 and 1.25.8, minor point releases.

These releases include 5 security fixes following the security policy:

crypto/x509: incorrect enforcement of email constraints

- When verifying a certificate chain which contains a certificate containing
  multiple email address constraints (composed of the full email address) which
  share common local portions (the portion of the address before the '@'
  character) but different domain portions (the portion of the address after the
  '@' character), these constraints will not be properly applied, and only the
  last constraint will be considered.

  This can allow certificates in the chain containing email addresses which are
  either not permitted or excluded by the relevant constraints to be returned by
  calls to Certificate.Verify. Since the name constraint checks happen after chain
  building is complete, this only applies to certificate chains which chain to
  trusted roots (root certificates either in VerifyOptions.Roots or in the system
  root certificate pool), requiring a trusted CA to issue certificates containing
  either not permitted or excluded email addresses.

  This issue only affects Go 1.26.

  Thanks to Jakub Ciolek for reporting this issue.

  This is CVE-2026-27137 and Go issue https://go.dev/issue/77952.

- crypto/x509: panic in name constraint checking for malformed certificates

  Certificate verification can panic when a certificate in the chain has an empty
  DNS name and another certificate in the chain has excluded name constraints.
  This can crash programs that are either directly verifying X.509 certificate
  chains, or those that use TLS.

  Since the name constraint checks happen after chain building is complete, this
  only applies to certificate chains which chain to trusted roots (root
  certificates either in VerifyOptions.Roots or in the system root certificate
  pool), requiring a trusted CA to issue certificates containing malformed DNS
  names.

  This issue only affects Go 1.26.

  Thanks to Jakub Ciolek for reporting this issue.

  This is CVE-2026-27138 and Go issue https://go.dev/issue/77953.

- html/template: URLs in meta content attribute actions are not escaped

  Actions which insert URLs into the content attribute of HTML meta tags are not
  escaped. This can allow XSS if the meta tag also has an http-equiv attribute
  with the value "refresh".

  A new GODEBUG setting has been added, htmlmetacontenturlescape, which can be
  used to disable escaping URLs in actions in the meta content attribute which
  follow "url=" by setting htmlmetacontenturlescape=0.

  This is CVE-2026-27142 and Go issue https://go.dev/issue/77954.

- net/url: reject IPv6 literal not at start of host

  The Go standard library function net/url.Parse insufficiently
  validated the host/authority component and accepted some invalid URLs
  by effectively treating garbage before an IP-literal as ignorable.
  The function should have rejected this as invalid.

  To prevent this behavior, net/url.Parse now rejects IPv6 literals
  that do not appear at the start of the host subcomponent of a URL.

  Thanks to Masaki Hara (https://github.com/qnighy) of Wantedly.

  This is CVE-2026-25679 and Go issue https://go.dev/issue/77578.

- os: FileInfo can escape from a Root

  On Unix platforms, when listing the contents of a directory using
  File.ReadDir or File.Readdir the returned FileInfo could reference
  a file outside of the Root in which the File was opened.

  The contents of the FileInfo were populated using the lstat system
  call, which takes the path to the file as a parameter. If a component
  of the full path of the file described by the FileInfo is replaced with
  a symbolic link, the target of the lstat can be directed to another
  location on the filesystem.

  The impact of this escape is limited to reading metadata provided by
  lstat from arbitrary locations on the filesystem. This could be used
  to probe for the presence or absence of files as well as gleaning
  metadata like file sizes, but does not permit reading or writing files
  outside the root.

  The FileInfo is now populated using fstatat.

  Thank you to Miloslav Trmač of Red Hat for reporting this issue.

  This is CVE-2026-27139 and Go issue https://go.dev/issue/77827.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-06 13:52:18 +01:00
dependabot[bot]andGitHub 39d676c72d build(deps): bump docker/metadata-action from 5 to 6
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5 to 6.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](https://github.com/docker/metadata-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-06 08:42:45 +00:00
dependabot[bot]andGitHub 6453c4c3a7 build(deps): bump docker/bake-action from 6 to 7
Bumps [docker/bake-action](https://github.com/docker/bake-action) from 6 to 7.
- [Release notes](https://github.com/docker/bake-action/releases)
- [Commits](https://github.com/docker/bake-action/compare/v6...v7)

---
updated-dependencies:
- dependency-name: docker/bake-action
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-06 08:42:41 +00:00
Sebastiaan van Stijn eef3c957be cli/config/credentials: ConvertToHostname: update godoc
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-05 19:02:03 +01:00
Davlat Davydov 71db1520de scripts/warn-outside-container: fix font representation
Signed-off-by: Davlat Davydov <literally_user@hotmail.com>

fix CI

review changes
2026-03-05 19:34:01 +03:00
Mahesh Thakur 02dee5c2d2 Fix: Remove inconsistent human readability warning from docker images
Signed-off-by: Mahesh Thakur <maheshthakur9152@gmail.com>
2026-01-23 08:12:21 +05:30
485 changed files with 20166 additions and 28724 deletions
+2 -5
View File
@@ -4,9 +4,6 @@ https://github.com/docker/cli/blob/master/CONTRIBUTING.md
** Make sure all your commits include a signature generated with `git commit -s` **
For additional information on our contributing process, read our contributing
guide https://docs.docker.com/opensource/code/
If this is a bug fix, make sure your description includes "fixes #xxxx", or
"closes #xxxx"
@@ -20,6 +17,7 @@ Provide the following information:
**- How to verify it**
**- Human readable description for the release notes**
<!--
Write a short (one line) summary that describes the changes in this
pull request for inclusion in the changelog.
@@ -28,10 +26,9 @@ It must be placed inside the below triple backticks section.
NOTE: Only fill this section if changes introduced in this PR are user-facing.
The PR must have a relevant impact/ label.
-->
```markdown changelog
```markdown changelog
```
**- A picture of a cute animal (not mandatory but encouraged)**
+5 -5
View File
@@ -66,7 +66,7 @@ jobs:
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
-
name: Build
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
uses: docker/bake-action@a66e1c87e2eca0503c343edf1d208c716d54b8a8 # v7.1.0
with:
targets: ${{ matrix.target }}
set: |
@@ -88,7 +88,7 @@ jobs:
fi
-
name: Upload artifacts
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: ${{ env.ARTIFACT_NAME }}
path: /tmp/out/*
@@ -101,7 +101,7 @@ jobs:
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
username: ${{ secrets.DOCKERHUB_CLIBIN_USERNAME }}
password: ${{ secrets.DOCKERHUB_CLIBIN_TOKEN }}
@@ -125,7 +125,7 @@ jobs:
type=semver,pattern={{major}}.{{minor}}
-
name: Build and push image
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
uses: docker/bake-action@a66e1c87e2eca0503c343edf1d208c716d54b8a8 # v7.1.0
with:
files: |
./docker-bake.hcl
@@ -168,7 +168,7 @@ jobs:
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
-
name: Build
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
uses: docker/bake-action@a66e1c87e2eca0503c343edf1d208c716d54b8a8 # v7.1.0
with:
targets: plugins-cross
set: |
+4 -4
View File
@@ -63,18 +63,18 @@ jobs:
name: Update Go
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
with:
go-version: "1.25.8"
go-version: "1.26.3"
cache: false
-
name: Initialize CodeQL
uses: github/codeql-action/init@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
with:
languages: go
-
name: Autobuild
uses: github/codeql-action/autobuild@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
uses: github/codeql-action/autobuild@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
-
name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
with:
category: "/language:go"
+1 -1
View File
@@ -74,7 +74,7 @@ jobs:
TESTFLAGS: -coverprofile=/tmp/coverage/coverage.txt
-
name: Send to Codecov
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v5
with:
files: ./build/coverage/coverage.txt
token: ${{ secrets.CODECOV_TOKEN }}
+4 -4
View File
@@ -33,12 +33,12 @@ jobs:
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
-
name: Test
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
uses: docker/bake-action@a66e1c87e2eca0503c343edf1d208c716d54b8a8 # v7.1.0
with:
targets: test-coverage
-
name: Send to Codecov
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v5
with:
files: ./build/coverage/coverage.txt
token: ${{ secrets.CODECOV_TOKEN }}
@@ -67,7 +67,7 @@ jobs:
name: Set up Go
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
with:
go-version: "1.25.8"
go-version: "1.26.3"
cache: false
-
name: Test
@@ -81,7 +81,7 @@ jobs:
shell: bash
-
name: Send to Codecov
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v5
with:
files: /tmp/coverage.txt
working-directory: ${{ env.GOPATH }}/src/github.com/docker/cli
+35
View File
@@ -0,0 +1,35 @@
name: validate-milestone
permissions:
contents: read
on:
pull_request:
types: [opened, synchronize, milestoned, demilestoned, edited]
jobs:
validate-milestone:
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
sparse-checkout: VERSION
- name: Validate milestone matches VERSION
run: |
expected=$(cat VERSION)
milestone="${{ github.event.pull_request.milestone.title }}"
if [[ -z "$milestone" ]]; then
echo "::error::PR must have a milestone set (expected: $expected)"
exit 1
fi
if [[ "$milestone" != "$expected" ]]; then
echo "::error::Milestone '$milestone' does not match VERSION '$expected'"
exit 1
fi
echo "Milestone: $milestone ✓"
+25 -1
View File
@@ -38,7 +38,7 @@ jobs:
steps:
-
name: Run
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
uses: docker/bake-action@a66e1c87e2eca0503c343edf1d208c716d54b8a8 # v7.1.0
with:
targets: ${{ matrix.target }}
@@ -80,3 +80,27 @@ jobs:
shell: 'script --return --quiet --command "bash {0}"'
run: |
make -f docker.Makefile ${{ matrix.target }}
validate-gocompat:
runs-on: ubuntu-24.04
env:
GOPATH: ${{ github.workspace }}
GO111MODULE: off
steps:
-
name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
path: src/github.com/docker/cli
-
name: Set up Go
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
with:
go-version: "1.26.3"
cache: false
-
name: Run gocompat check
shell: 'script --return --quiet --command "bash {0}"'
working-directory: ${{ github.workspace }}/src/github.com/docker/cli
run: |
make -C ./internal/gocompat verify
+11 -12
View File
@@ -5,7 +5,7 @@ run:
# which causes it to fallback to go1.17 semantics.
#
# TODO(thaJeztah): update "usetesting" settings to enable go1.24 features once our minimum version is go1.24
go: "1.25.8"
go: "1.26.3"
timeout: 5m
@@ -110,8 +110,15 @@ linters:
excludes:
- G104 # G104: Errors unhandled; (TODO: reduce unhandled errors, or explicitly ignore)
- G115 # G115: integer overflow conversion; (TODO: verify these: https://github.com/docker/cli/issues/5584)
- G117 # G117: Exported struct field matches secret pattern (false positives for legitimate field names)
- G118 # G118: Goroutine uses context.Background/TODO while request-scoped context is available (TODO: evaluate these)
- G122 # G122: Filesystem operation in filepath.Walk/WalkDir callback uses race-prone path (TODO: evaluate these)
- G306 # G306: Expect WriteFile permissions to be 0600 or less (too restrictive; also flags "0o644" permissions)
- G307 # G307: Deferring unsafe method "*os.File" on type "Close" (also EXC0008); (TODO: evaluate these and fix where needed: G307: Deferring unsafe method "*os.File" on type "Close")
- G702 # G702: Command injection via taint analysis (TODO: evaluate these)
- G703 # G703: Path traversal via taint analysis (TODO: evaluate these)
- G704 # G704: SSRF via taint analysis (TODO: evaluate these)
- G705 # G705: XSS via taint analysis (TODO: evaluate these)
govet:
enable:
@@ -158,11 +165,6 @@ linters:
- name: use-any # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#use-any
- name: use-errors-new # https://github.com/mgechev/revive/blob/HEAD/RULES_DESCRIPTIONS.md#use-errors-new
usetesting:
os-chdir: false # FIXME(thaJeztah): Disable `os.Chdir()` detections; should be automatically disabled on Go < 1.24; see https://github.com/docker/cli/pull/5835#issuecomment-2665302478
context-background: false # FIXME(thaJeztah): Disable `context.Background()` detections; should be automatically disabled on Go < 1.24; see https://github.com/docker/cli/pull/5835#issuecomment-2665302478
context-todo: false # FIXME(thaJeztah): Disable `context.TODO()` detections; should be automatically disabled on Go < 1.24; see https://github.com/docker/cli/pull/5835#issuecomment-2665302478
exclusions:
# We prefer to use an "linters.exclusions.rules" so that new "default" exclusions are not
# automatically inherited. We can decide whether or not to follow upstream
@@ -225,13 +227,10 @@ linters:
linters:
- staticcheck
# Ignore deprecation linting for cli/command/stack/*.
#
# FIXME(thaJeztah): remove exception once these functions are un-exported or internal; see https://github.com/docker/cli/pull/6389
- text: '^(SA1019): '
path: "cli/command/stack"
# TODO(thaJeztah): remove once https://github.com/leighmcculloch/gocheckcompilerdirectives/issues/7 is fixed.
- text: "compiler directive unrecognized: //go:fix"
linters:
- staticcheck
- gocheckcompilerdirectives
# Log a warning if an exclusion rule is unused.
# Default: false
+1
View File
@@ -354,6 +354,7 @@ Lorenzo Fontana <lo@linux.com> <fontanalorenzo@me.com>
Louis Opter <kalessin@kalessin.fr>
Louis Opter <kalessin@kalessin.fr> <louis@dotcloud.com>
Lovekesh Kumar <lovekesh.kumar@rtcamp.com>
Luo Jiyin <luojiyin@hotmail.com>
Luca Favatella <luca.favatella@erlang-solutions.com> <lucafavatella@users.noreply.github.com>
Luke Marsden <me@lukemarsden.net> <luke@digital-crocus.com>
Lyn <energylyn@zju.edu.cn>
+16 -1
View File
@@ -2,6 +2,7 @@
# This file lists all contributors to the repository.
# See scripts/docs/generate-authors.sh to make modifications.
4RH1T3CT0R7 <iprintercanon@gmail.com>
A. Lester Buck III <github-reg@nbolt.com>
Aanand Prasad <aanand.prasad@gmail.com>
Aaron L. Xu <liker.xu@foxmail.com>
@@ -42,6 +43,7 @@ Alexander Larsson <alexl@redhat.com>
Alexander Morozov <lk4d4math@gmail.com>
Alexander Ryabov <i@sepa.spb.ru>
Alexandre González <agonzalezro@gmail.com>
Alexandre Vallières-Lagacé <alexandre.valliereslagace@docker.com>
Alexey Igrychev <alexey.igrychev@flant.com>
Alexis Couvreur <alexiscouvreur.pro@gmail.com>
Alfred Landrum <alfred.landrum@docker.com>
@@ -64,6 +66,7 @@ Andres G. Aragoneses <knocte@gmail.com>
Andres Leon Rangel <aleon1220@gmail.com>
Andrew France <andrew@avito.co.uk>
Andrew He <he.andrew.mail@gmail.com>
Andrew Hopp <andrew.hopp@me.com>
Andrew Hsu <andrewhsu@docker.com>
Andrew Macpherson <hopscotch23@gmail.com>
Andrew McDonnell <bugs@andrewmcdonnell.net>
@@ -127,6 +130,7 @@ Brian Goff <cpuguy83@gmail.com>
Brian Tracy <brian.tracy33@gmail.com>
Brian Wieder <brian@4wieders.com>
Bruno Sousa <bruno.sousa@docker.com>
Bruno Verachten <gounthar@gmail.com>
Bryan Bess <squarejaw@bsbess.com>
Bryan Boreham <bjboreham@gmail.com>
Bryan Murphy <bmurphy1976@gmail.com>
@@ -178,6 +182,7 @@ Christopher Svensson <stoffus@stoffus.com>
Christy Norman <christy@linux.vnet.ibm.com>
Chun Chen <ramichen@tencent.com>
Clinton Kitson <clintonskitson@gmail.com>
Codex <codex@openai.com>
Coenraad Loubser <coenraad@wish.org.za>
Colin Hebert <hebert.colin@gmail.com>
Collin Guarino <collin.guarino@gmail.com>
@@ -234,6 +239,7 @@ David Sheets <dsheets@docker.com>
David Williamson <david.williamson@docker.com>
David Xia <dxia@spotify.com>
David Young <yangboh@cn.ibm.com>
Davlat Davydov <literally_user@hotmail.com>
Deng Guangxing <dengguangxing@huawei.com>
Denis Defreyne <denis@soundcloud.com>
Denis Gladkikh <denis@gladkikh.email>
@@ -241,6 +247,7 @@ Denis Ollier <larchunix@users.noreply.github.com>
Dennis Docter <dennis@d23.nl>
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Derek McGowan <derek@mcg.dev>
Derek Misler <derek.misler@docker.com>
Des Preston <despreston@gmail.com>
Deshi Xiao <dxiao@redhat.com>
Dharmit Shah <shahdharmit@gmail.com>
@@ -260,6 +267,7 @@ Dominik Braun <dominik.braun@nbsp.de>
Don Kjer <don.kjer@gmail.com>
Dong Chen <dongluo.chen@docker.com>
DongGeon Lee <secmatth1996@gmail.com>
Dorin Geman <dorin.geman@docker.com>
Doug Davis <dug@us.ibm.com>
Drew Erny <derny@mirantis.com>
Ed Costello <epc@epcostello.com>
@@ -358,7 +366,7 @@ Hugo Gabriel Eyherabide <hugogabriel.eyherabide@gmail.com>
huqun <huqun@zju.edu.cn>
Huu Nguyen <huu@prismskylabs.com>
Hyzhou Zhy <hyzhou.zhy@alibaba-inc.com>
Iain MacDonald <IJMacD@gmail.com>
Iain MacDonald <ijmacd@gmail.com>
Iain Samuel McLean Elder <iain@isme.es>
Ian Campbell <ian.campbell@docker.com>
Ian Philpot <ian.philpot@microsoft.com>
@@ -471,6 +479,7 @@ Justyn Temme <justyntemme@gmail.com>
Jyrki Puttonen <jyrkiput@gmail.com>
Jérémie Drouet <jeremie.drouet@gmail.com>
Jérôme Petazzoni <jerome.petazzoni@docker.com>
Jörg Sommer <joerg@jo-so.de>
Jörg Thalheim <joerg@higgsboson.tk>
Kai Blin <kai@samba.org>
Kai Qiang Wu (Kennan) <wkq5325@gmail.com>
@@ -539,10 +548,12 @@ Lovekesh Kumar <lovekesh.kumar@rtcamp.com>
Luca Favatella <luca.favatella@erlang-solutions.com>
Luca Marturana <lucamarturana@gmail.com>
Lucas Chan <lucas-github@lucaschan.com>
Ludovic Temgoua Abanda <abandaludovic500@gmail.com>
Luis Henrique Mulinari <luis.mulinari@gmail.com>
Luka Hartwig <mail@lukahartwig.de>
Lukas Heeren <lukas-heeren@hotmail.com>
Lukasz Zajaczkowski <Lukasz.Zajaczkowski@ts.fujitsu.com>
Luo Jiyin <luojiyin@hotmail.com>
Lydell Manganti <LydellManganti@users.noreply.github.com>
Lénaïc Huard <lhuard@amadeus.com>
Ma Shimiao <mashimiao.fnst@cn.fujitsu.com>
@@ -603,6 +614,7 @@ Michael Spetsiotis <michael_spets@hotmail.com>
Michael Steinert <mike.steinert@gmail.com>
Michael Tews <michael@tews.dev>
Michael West <mwest@mdsol.com>
Michael Zampani <michael.zampani@docker.com>
Michal Minář <miminar@redhat.com>
Michał Czeraszkiewicz <czerasz@gmail.com>
Miguel Angel Alvarez Cabrerizo <doncicuto@gmail.com>
@@ -617,6 +629,7 @@ Mike Goelzer <mike.goelzer@docker.com>
Mike MacCana <mike.maccana@gmail.com>
mikelinjie <294893458@qq.com>
Mikhail Vasin <vasin@cloud-tv.ru>
Milas Bowman <milas.bowman@docker.com>
Milind Chawre <milindchawre@gmail.com>
Mindaugas Rukas <momomg@gmail.com>
Miroslav Gula <miroslav.gula@naytrolabs.com>
@@ -887,6 +900,7 @@ Vincent Batts <vbatts@redhat.com>
Vincent Bernat <Vincent.Bernat@exoscale.ch>
Vincent Demeester <vincent.demeester@docker.com>
Vincent Woo <me@vincentwoo.com>
Vineet Kumar <vineetkumar17112004@gmail.com>
Vishnu Kannan <vishnuk@google.com>
Vivek Goyal <vgoyal@redhat.com>
Wang Jie <wangjie5@chinaskycloud.com>
@@ -916,6 +930,7 @@ Yanqiang Miao <miao.yanqiang@zte.com.cn>
Yassine Tijani <yasstij11@gmail.com>
Yi EungJun <eungjun.yi@navercorp.com>
Ying Li <ying.li@docker.com>
Yoan Wainmann <thebook90yw@gmail.com>
Yong Tang <yong.tang.github@outlook.com>
Yosef Fertel <yfertel@gmail.com>
Yu Peng <yu.peng36@zte.com.cn>
+8 -5
View File
@@ -66,7 +66,7 @@ anybody starts working on it.
We are always thrilled to receive pull requests. We do our best to process them
quickly. If your pull request is not accepted on the first try,
don't get discouraged! Our contributor's guide explains [the review process we
use for simple changes](https://github.com/docker/docker/blob/master/project/REVIEWING.md).
use for simple changes](https://github.com/moby/moby/blob/master/project/REVIEWING.md).
### Talking to other Docker users and contributors
@@ -124,8 +124,7 @@ submitting a pull request.
Update the documentation when creating or modifying features. Test your
documentation changes for clarity, concision, and correctness, as well as a
clean documentation build. See our contributors guide for [our style
guide](https://docs.docker.com/contribute/style/grammar/) and instructions on [building
the documentation](https://docs.docker.com/contribute/).
guide](https://github.com/docker/docs/blob/main/STYLE.md).
Write clean code. Universally formatted code promotes ease of writing, reading,
and maintenance. Always run `gofmt -s -w file.go` on each changed file before
@@ -145,6 +144,7 @@ not enforced. Common prefixes are `docs: <message>`, `vendor: <message>`,
or `telemetry: <message>`.
A standard commit.
```
Fix the exploding flux capacitor
@@ -153,6 +153,7 @@ the sun and the moon align.
```
Using a package as prefix.
```
pkg/foo: prevent panic in flux capacitor
@@ -160,12 +161,14 @@ Calling function A causes the flux capacitor to blow up every time
the sun and the moon align.
```
Updating a specific vendored package.
Updating a specific vendored dependency.
```
vendor: github.com/docker/docker 6ac445c42bad (master, v28.0-dev)
vendor: github.com/moby/moby/client v0.4.0
```
Fixing a broken docs link.
```
docs: fix style/lint issues in deprecated.md
```
+3 -3
View File
@@ -8,7 +8,7 @@ ARG BASE_VARIANT=alpine
ARG ALPINE_VERSION=3.23
ARG BASE_DEBIAN_DISTRO=bookworm
ARG GO_VERSION=1.25.8
ARG GO_VERSION=1.26.3
# XX_VERSION specifies the version of the xx utility to use.
# It must be a valid tag in the docker.io/tonistiigi/xx image repository.
@@ -25,12 +25,12 @@ ARG GOTESTSUM_VERSION=v1.13.0
# BUILDX_VERSION sets the version of buildx to use for the e2e tests.
# It must be a tag in the docker.io/docker/buildx-bin image repository
# on Docker Hub.
ARG BUILDX_VERSION=0.31.1
ARG BUILDX_VERSION=0.33.0
# COMPOSE_VERSION is the version of compose to install in the dev container.
# It must be a tag in the docker.io/docker/compose-bin image repository
# on Docker Hub.
ARG COMPOSE_VERSION=v5.1.0
ARG COMPOSE_VERSION=v5.1.3
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
+13 -1
View File
@@ -51,6 +51,18 @@ Run test:
docker buildx bake test
```
Run the unit test:
```
$ make -f docker.Makefile test-unit
```
Run the full test suite:
```
$ make -f docker.Makefile test
```
List all the available targets:
```shell
@@ -62,7 +74,7 @@ make help
Start an interactive development environment:
```shell
make -f docker.Makefile shell
make shell
```
## Legal
+1 -1
View File
@@ -1 +1 @@
29.0.0-dev
29.5.0
+85
View File
@@ -0,0 +1,85 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.25
// Package hooks defines the contract between the Docker CLI and CLI plugin hook
// implementations.
//
// # Audience
//
// This package is intended to be imported by CLI plugin implementations that
// implement a "hooks" subcommand, and by the Docker CLI when invoking those
// hooks.
//
// # Contract and wire format
//
// Hook inputs (see [Request]) are serialized as JSON and passed to the plugin hook
// subcommand (currently as a command-line argument). Hook outputs are emitted by
// the plugin as JSON (see [Response]).
//
// # Stability
//
// The types that represent the hook contract ([Request], [Response] and related
// constants) are considered part of Docker CLI's public Go API.
// Fields and values may be extended in a backwards-compatible way (for example,
// adding new fields), but existing fields and their meaning should remain stable.
// Plugins should ignore unknown fields and unknown hook types to remain
// forwards-compatible.
package hooks
// ResponseType is the type of response from the plugin.
type ResponseType int
const (
NextSteps ResponseType = 0
)
// Request is the type representing the information
// that plugins declaring support for hooks get passed when
// being invoked following a CLI command execution.
type Request struct {
// RootCmd is a string representing the matching hook configuration
// which is currently being invoked. If a hook for "docker context"
// is configured and the user executes "docker context ls", the plugin
// is invoked with "context".
RootCmd string `json:"RootCmd,omitzero"`
// Flags contains flags that were set on the command for which the
// hook was invoked. It uses flag names as key, with leading hyphens
// removed ("--flag" and "-flag" are included as "flag" and "f").
//
// Flag values are not included and are set to an empty string,
// except for boolean flags known to the CLI itself, for which
// the value is either "true", or "false".
//
// Plugins can use this information to adjust their [Response]
// based on whether the command triggering the hook was invoked
// with.
Flags map[string]string `json:"Flags,omitzero"`
// CommandError is a string containing the error output (if any)
// of the command for which the hook was invoked.
CommandError string `json:"CommandError,omitzero"`
}
// Response represents a plugin hook response. Plugins
// declaring support for CLI hooks need to print a JSON
// representation of this type when their hook subcommand
// is invoked.
type Response struct {
Type ResponseType `json:"Type"`
Template string `json:"Template,omitzero"`
}
// HookType is the type of response from the plugin.
//
// Deprecated: use [ResponseType] instead.
//
//go:fix inline
type HookType = ResponseType
// HookMessage represents a plugin hook response.
//
// Deprecated: use [Response] instead.
//
//go:fix inline
type HookMessage = Response
+75
View File
@@ -0,0 +1,75 @@
package hooks
import (
"fmt"
)
const (
hookTemplateCommandName = `{{command}}`
hookTemplateFlagValue = `{{flagValue %q}}`
hookTemplateArg = `{{argValue %d}}`
)
// TemplateReplaceSubcommandName returns a hook template string
// that will be replaced by the CLI subcommand being executed
//
// Example:
//
// Response{
// Type: NextSteps,
// Template: "you ran the subcommand: " + TemplateReplaceSubcommandName(),
// }
//
// When being executed after the command:
//
// docker run --name "my-container" alpine
//
// It results in the message:
//
// you ran the subcommand: run
func TemplateReplaceSubcommandName() string {
return hookTemplateCommandName
}
// TemplateReplaceFlagValue returns a hook template string that will be
// replaced with the flags value when printed by the CLI.
//
// Example:
//
// Response{
// Type: NextSteps,
// Template: "you ran a container named: " + TemplateReplaceFlagValue("name"),
// }
//
// when executed after the command:
//
// docker run --name "my-container" alpine
//
// it results in the message:
//
// you ran a container named: my-container
func TemplateReplaceFlagValue(flag string) string {
return fmt.Sprintf(hookTemplateFlagValue, flag)
}
// TemplateReplaceArg takes an index i and returns a hook
// template string that the CLI will replace the template with
// the ith argument after processing the passed flags.
//
// Example:
//
// Response{
// Type: NextSteps,
// Template: "run this image with `docker run " + TemplateReplaceArg(0) + "`",
// }
//
// when being executed after the command:
//
// docker pull alpine
//
// It results in the message:
//
// Run this image with `docker run alpine`
func TemplateReplaceArg(i int) string {
return fmt.Sprintf(hookTemplateArg, i)
}
+50
View File
@@ -0,0 +1,50 @@
package hooks_test
import (
"testing"
"github.com/docker/cli/cli-plugins/hooks"
)
func TestTemplateHelpers(t *testing.T) {
tests := []struct {
doc string
got func() string
want string
}{
{
doc: "subcommand name",
got: hooks.TemplateReplaceSubcommandName,
want: `{{command}}`,
},
{
doc: "flag value",
got: func() string {
return hooks.TemplateReplaceFlagValue("name")
},
want: `{{flagValue "name"}}`,
},
{
doc: "arg",
got: func() string {
return hooks.TemplateReplaceArg(0)
},
want: `{{argValue 0}}`,
},
{
doc: "arg",
got: func() string {
return hooks.TemplateReplaceArg(3)
},
want: `{{argValue 3}}`,
},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
if got := tc.got(); got != tc.want {
t.Fatalf("expected %q, got %q", tc.want, got)
}
})
}
}
+12 -7
View File
@@ -1,18 +1,23 @@
package hooks
import (
"fmt"
"io"
import "io"
"github.com/morikuni/aec"
const (
whatsNext = "\n\033[1mWhat's next:\033[0m\n"
indent = " "
)
// PrintNextSteps renders list of [NextSteps] messages and writes them
// to out. It is a no-op if messages is empty.
func PrintNextSteps(out io.Writer, messages []string) {
if len(messages) == 0 {
return
}
_, _ = fmt.Fprintln(out, aec.Bold.Apply("\nWhat's next:"))
for _, n := range messages {
_, _ = fmt.Fprintln(out, " ", n)
_, _ = io.WriteString(out, whatsNext)
for _, msg := range messages {
_, _ = io.WriteString(out, indent)
_, _ = io.WriteString(out, msg)
_, _ = io.WriteString(out, "\n")
}
}
+19 -12
View File
@@ -1,38 +1,45 @@
package hooks
package hooks_test
import (
"bytes"
"strings"
"testing"
"github.com/morikuni/aec"
"github.com/docker/cli/cli-plugins/hooks"
"gotest.tools/v3/assert"
)
func TestPrintHookMessages(t *testing.T) {
testCases := []struct {
const header = "\n\x1b[1mWhat's next:\x1b[0m\n"
tests := []struct {
doc string
messages []string
expectedOutput string
}{
{
messages: []string{},
doc: "no messages",
messages: nil,
expectedOutput: "",
},
{
doc: "single message",
messages: []string{"Bork!"},
expectedOutput: aec.Bold.Apply("\nWhat's next:") + "\n" +
expectedOutput: header +
" Bork!\n",
},
{
doc: "multiple messages",
messages: []string{"Foo", "bar"},
expectedOutput: aec.Bold.Apply("\nWhat's next:") + "\n" +
expectedOutput: header +
" Foo\n" +
" bar\n",
},
}
for _, tc := range testCases {
w := bytes.Buffer{}
PrintNextSteps(&w, tc.messages)
assert.Equal(t, w.String(), tc.expectedOutput)
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
var w strings.Builder
hooks.PrintNextSteps(&w, tc.messages)
assert.Equal(t, w.String(), tc.expectedOutput)
})
}
}
+68 -89
View File
@@ -1,116 +1,95 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.25
package hooks
import (
"bytes"
"errors"
"fmt"
"strconv"
"strings"
"text/template"
"github.com/spf13/cobra"
)
type HookType int
const (
NextSteps = iota
)
// HookMessage represents a plugin hook response. Plugins
// declaring support for CLI hooks need to print a json
// representation of this type when their hook subcommand
// is invoked.
type HookMessage struct {
Type HookType
Template string
}
// TemplateReplaceSubcommandName returns a hook template string
// that will be replaced by the CLI subcommand being executed
//
// Example:
//
// "you ran the subcommand: " + TemplateReplaceSubcommandName()
//
// when being executed after the command:
// `docker run --name "my-container" alpine`
// will result in the message:
// `you ran the subcommand: run`
func TemplateReplaceSubcommandName() string {
return hookTemplateCommandName
}
// TemplateReplaceFlagValue returns a hook template string
// that will be replaced by the flags value.
//
// Example:
//
// "you ran a container named: " + TemplateReplaceFlagValue("name")
//
// when being executed after the command:
// `docker run --name "my-container" alpine`
// will result in the message:
// `you ran a container named: my-container`
func TemplateReplaceFlagValue(flag string) string {
return fmt.Sprintf(hookTemplateFlagValue, flag)
}
// TemplateReplaceArg takes an index i and returns a hook
// template string that the CLI will replace the template with
// the ith argument, after processing the passed flags.
//
// Example:
//
// "run this image with `docker run " + TemplateReplaceArg(0) + "`"
//
// when being executed after the command:
// `docker pull alpine`
// will result in the message:
// "Run this image with `docker run alpine`"
func TemplateReplaceArg(i int) string {
return fmt.Sprintf(hookTemplateArg, strconv.Itoa(i))
}
const maxMessages = 10
func ParseTemplate(hookTemplate string, cmd *cobra.Command) ([]string, error) {
tmpl := template.New("").Funcs(commandFunctions)
tmpl, err := tmpl.Parse(hookTemplate)
if err != nil {
return nil, err
out := hookTemplate
if strings.Contains(hookTemplate, "{{") {
// Message may be a template.
msgContext := commandInfo{cmd: cmd}
tmpl, err := template.New("").Funcs(template.FuncMap{
"command": msgContext.command,
"flagValue": msgContext.flagValue,
"argValue": msgContext.argValue,
// kept for backward-compatibility with old templates.
"flag": func(_ any, flagName string) (string, error) { return msgContext.flagValue(flagName) },
"arg": func(_ any, i int) (string, error) { return msgContext.argValue(i) },
}).Parse(hookTemplate)
if err != nil {
return nil, err
}
var b bytes.Buffer
err = tmpl.Execute(&b, msgContext)
if err != nil {
return nil, err
}
out = b.String()
}
b := bytes.Buffer{}
err = tmpl.Execute(&b, cmd)
if err != nil {
return nil, err
if n := strings.Count(out, "\n"); n > maxMessages {
return nil, fmt.Errorf("hook template contains too many messages (%d): maximum is %d", n, maxMessages)
}
return strings.Split(b.String(), "\n"), nil
return strings.SplitN(out, "\n", maxMessages), nil
}
var ErrHookTemplateParse = errors.New("failed to parse hook template")
const (
hookTemplateCommandName = "{{.Name}}"
hookTemplateFlagValue = `{{flag . "%s"}}`
hookTemplateArg = "{{arg . %s}}"
)
var commandFunctions = template.FuncMap{
"flag": getFlagValue,
"arg": getArgValue,
// commandInfo provides info about the command for which the hook was invoked.
// It is used for templated hook-messages.
type commandInfo struct {
cmd *cobra.Command
}
func getFlagValue(cmd *cobra.Command, flag string) (string, error) {
cmdFlag := cmd.Flag(flag)
if cmdFlag == nil {
return "", ErrHookTemplateParse
// Name returns the name of the (sub)command for which the hook was invoked.
//
// It's used for backward-compatibility with old templates.
func (c commandInfo) Name() string {
return c.command()
}
// command returns the name of the (sub)command for which the hook was invoked.
func (c commandInfo) command() string {
if c.cmd == nil {
return ""
}
return cmdFlag.Value.String(), nil
return c.cmd.Name()
}
func getArgValue(cmd *cobra.Command, i int) (string, error) {
flags := cmd.Flags()
if flags == nil {
return "", ErrHookTemplateParse
// flagValue returns the value that was set for the given flag when the hook was invoked.
func (c commandInfo) flagValue(flagName string) (string, error) {
if c.cmd == nil {
return "", fmt.Errorf("%w: flagValue: cmd is nil", ErrHookTemplateParse)
}
return flags.Arg(i), nil
f := c.cmd.Flag(flagName)
if f == nil {
return "", fmt.Errorf("%w: flagValue: no flags found", ErrHookTemplateParse)
}
return f.Value.String(), nil
}
// argValue returns the value of the nth argument.
func (c commandInfo) argValue(n int) (string, error) {
if c.cmd == nil {
return "", fmt.Errorf("%w: arg: cmd is nil", ErrHookTemplateParse)
}
flags := c.cmd.Flags()
v := flags.Arg(n)
if v == "" && n >= flags.NArg() {
return "", fmt.Errorf("%w: arg: %dth argument not set", ErrHookTemplateParse, n)
}
return v, nil
}
+64 -25
View File
@@ -1,43 +1,67 @@
package hooks
package hooks_test
import (
"testing"
"github.com/docker/cli/cli-plugins/hooks"
"github.com/spf13/cobra"
"gotest.tools/v3/assert"
)
// TestParseTemplate tests parsing templates as returned by plugins.
//
// It uses fixed string fixtures to lock in compatibility with existing
// plugin templates, so older formats continue to work even if we add new
// template forms.
//
// For helper-backed cases, it also verifies that templates produced by the
// current TemplateReplace* helpers parse to the same output. This lets us
// evolve the emitted template format without breaking older plugins.
func TestParseTemplate(t *testing.T) {
type testFlag struct {
name string
value string
}
testCases := []struct {
template string
tests := []struct {
doc string
template string // compatibility fixture; keep even if helpers emit a newer form
templateFunc func() string
flags []testFlag
args []string
expectedOutput []string
}{
{
doc: "empty template",
template: "",
expectedOutput: []string{""},
},
{
doc: "plain message",
template: "a plain template message",
expectedOutput: []string{"a plain template message"},
},
{
template: TemplateReplaceFlagValue("tag"),
doc: "subcommand name",
template: "hello {{.Name}}", // NOTE: fixture; do not modify without considering plugin compatibility
templateFunc: func() string { return "hello " + hooks.TemplateReplaceSubcommandName() },
expectedOutput: []string{"hello pull"},
},
{
doc: "single flag",
template: `{{flag . "tag"}}`, // NOTE: fixture; do not modify without considering plugin compatibility
templateFunc: func() string { return hooks.TemplateReplaceFlagValue("tag") },
flags: []testFlag{
{
name: "tag",
value: "my-tag",
},
{name: "tag", value: "my-tag"},
},
expectedOutput: []string{"my-tag"},
},
{
template: TemplateReplaceFlagValue("test-one") + " " + TemplateReplaceFlagValue("test2"),
doc: "multiple flags",
template: `{{flag . "test-one"}} {{flag . "test2"}}`, // NOTE: fixture; do not modify without considering plugin compatibility
templateFunc: func() string {
return hooks.TemplateReplaceFlagValue("test-one") + " " + hooks.TemplateReplaceFlagValue("test2")
},
flags: []testFlag{
{
name: "test-one",
@@ -51,36 +75,51 @@ func TestParseTemplate(t *testing.T) {
expectedOutput: []string{"value value2"},
},
{
template: TemplateReplaceArg(0) + " " + TemplateReplaceArg(1),
doc: "multiple args",
template: `{{arg . 0}} {{arg . 1}}`, // NOTE: fixture; do not modify without considering plugin compatibility
templateFunc: func() string { return hooks.TemplateReplaceArg(0) + " " + hooks.TemplateReplaceArg(1) },
args: []string{"zero", "one"},
expectedOutput: []string{"zero one"},
},
{
template: "You just pulled " + TemplateReplaceArg(0),
doc: "arg in sentence",
template: "You just pulled {{arg . 0}}", // NOTE: fixture; do not modify without considering plugin compatibility
templateFunc: func() string { return "You just pulled " + hooks.TemplateReplaceArg(0) },
args: []string{"alpine"},
expectedOutput: []string{"You just pulled alpine"},
},
{
doc: "multiline output",
template: "one line\nanother line!",
expectedOutput: []string{"one line", "another line!"},
},
}
for _, tc := range testCases {
testCmd := &cobra.Command{
Use: "pull",
Args: cobra.ExactArgs(len(tc.args)),
}
for _, f := range tc.flags {
_ = testCmd.Flags().String(f.name, "", "")
err := testCmd.Flag(f.name).Value.Set(f.value)
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
testCmd := &cobra.Command{
Use: "pull",
Args: cobra.ExactArgs(len(tc.args)),
}
for _, f := range tc.flags {
_ = testCmd.Flags().String(f.name, "", "")
err := testCmd.Flag(f.name).Value.Set(f.value)
assert.NilError(t, err)
}
err := testCmd.Flags().Parse(tc.args)
assert.NilError(t, err)
}
err := testCmd.Flags().Parse(tc.args)
assert.NilError(t, err)
out, err := ParseTemplate(tc.template, testCmd)
assert.NilError(t, err)
assert.DeepEqual(t, out, tc.expectedOutput)
// Validate using fixtures.
out, err := hooks.ParseTemplate(tc.template, testCmd)
assert.NilError(t, err)
assert.DeepEqual(t, out, tc.expectedOutput)
if tc.templateFunc != nil {
// Validate using the current template function equivalent.
out, err = hooks.ParseTemplate(tc.templateFunc(), testCmd)
assert.NilError(t, err)
assert.DeepEqual(t, out, tc.expectedOutput)
}
})
}
}
+5 -2
View File
@@ -45,8 +45,7 @@ func AddPluginCommandStubs(dockerCLI config.Provider, rootCmd *cobra.Command) (e
RunE: func(cmd *cobra.Command, args []string) error {
flags := rootCmd.PersistentFlags()
flags.SetOutput(nil)
perr := flags.Parse(args)
if perr != nil {
if err := flags.Parse(args); err != nil {
return err
}
if flags.Changed("help") {
@@ -60,7 +59,11 @@ func AddPluginCommandStubs(dockerCLI config.Provider, rootCmd *cobra.Command) (e
cargs := []string{p.Path, cobra.ShellCompRequestCmd, p.Name} //nolint:prealloc // no need to over-complicate things.
cargs = append(cargs, args...)
cargs = append(cargs, toComplete)
origArgs := os.Args
os.Args = cargs
defer func() {
os.Args = origArgs
}()
runCommand, runErr := PluginRunCommand(dockerCLI, p.Name, cmd)
if runErr != nil {
return nil, cobra.ShellCompDirectiveError
+59
View File
@@ -1,8 +1,12 @@
package manager
import (
"os"
"path/filepath"
"sync"
"testing"
"github.com/docker/cli/internal/test"
"github.com/spf13/cobra"
"gotest.tools/v3/assert"
)
@@ -24,3 +28,58 @@ func TestPluginResourceAttributesEnvvar(t *testing.T) {
env = appendPluginResourceAttributesEnvvar(nil, cmd, Plugin{Name: "compose"})
assert.DeepEqual(t, []string{"OTEL_RESOURCE_ATTRIBUTES=a.b.c=foo,docker.cli.cobra.command_path=docker%20compose"}, env)
}
func TestPluginStubRunEReturnsParseError(t *testing.T) {
cmd, err := preparePluginStubCommand(t)
assert.NilError(t, err)
err = cmd.RunE(cmd, []string{"--definitely-not-a-real-flag"})
assert.ErrorContains(t, err, "unknown flag: --definitely-not-a-real-flag")
}
func TestPluginStubCompletionRestoresOSArgs(t *testing.T) {
cmd, err := preparePluginStubCommand(t)
assert.NilError(t, err)
savedArgs := os.Args
t.Cleanup(func() { os.Args = savedArgs })
originalArgs := []string{"docker", "image", "ls"}
os.Args = append([]string(nil), originalArgs...)
_, directive := cmd.ValidArgsFunction(cmd, []string{"--all"}, "alp")
assert.Equal(t, directive, cobra.ShellCompDirectiveError)
assert.DeepEqual(t, os.Args, originalArgs)
}
func preparePluginStubCommand(t *testing.T) (*cobra.Command, error) {
t.Helper()
pluginCommandStubsOnce = sync.Once{}
tmpDir := t.TempDir()
const cliPlugin = `#!/bin/sh
printf '%s' '{"SchemaVersion":"0.1.0"}'
`
if err := os.WriteFile(filepath.Join(tmpDir, "docker-testplugin"), []byte(cliPlugin), 0o777); err != nil {
return nil, err
}
cli := test.NewFakeCli(nil)
cli.ConfigFile().CLIPluginsExtraDirs = []string{tmpDir}
root := &cobra.Command{Use: "docker"}
root.PersistentFlags().Bool("debug", false, "")
if err := AddPluginCommandStubs(cli, root); err != nil {
return nil, err
}
cmd, _, err := root.Find([]string{"testplugin"})
if err != nil {
return nil, err
}
if cmd == nil {
return nil, os.ErrNotExist
}
return cmd, nil
}
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package manager
+45 -31
View File
@@ -1,11 +1,14 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package manager
import (
"context"
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
"github.com/docker/cli/cli-plugins/hooks"
@@ -19,15 +22,11 @@ import (
// HookPluginData is the type representing the information
// that plugins declaring support for hooks get passed when
// being invoked following a CLI command execution.
type HookPluginData struct {
// RootCmd is a string representing the matching hook configuration
// which is currently being invoked. If a hook for `docker context` is
// configured and the user executes `docker context ls`, the plugin will
// be invoked with `context`.
RootCmd string
Flags map[string]string
CommandError string
}
//
// Deprecated: use [hooks.Request] instead.
//
//go:fix inline
type HookPluginData = hooks.Request
// RunCLICommandHooks is the entrypoint into the hooks execution flow after
// a main CLI command was executed. It calls the hook subcommand for all
@@ -55,11 +54,8 @@ func runHooks(ctx context.Context, cfg *configfile.ConfigFile, rootCmd, subComma
}
func invokeAndCollectHooks(ctx context.Context, cfg *configfile.ConfigFile, rootCmd, subCmd *cobra.Command, subCmdStr string, flags map[string]string, cmdErrorMessage string) []string {
// check if the context was cancelled before invoking hooks
select {
case <-ctx.Done():
if ctx.Err() != nil {
return nil
default:
}
pluginsCfg := cfg.Plugins
@@ -69,47 +65,65 @@ func invokeAndCollectHooks(ctx context.Context, cfg *configfile.ConfigFile, root
pluginDirs := getPluginDirs(cfg)
nextSteps := make([]string, 0, len(pluginsCfg))
for pluginName, pluginCfg := range pluginsCfg {
match, ok := pluginMatch(pluginCfg, subCmdStr, cmdErrorMessage)
if !ok {
continue
tryInvokeHook := func(pluginName string, pluginCfg map[string]string) (messages []string, ok bool, err error) {
match, matched := pluginMatch(pluginCfg, subCmdStr, cmdErrorMessage)
if !matched {
return nil, false, nil
}
p, err := getPlugin(pluginName, pluginDirs, rootCmd)
if err != nil {
continue
return nil, false, err
}
hookReturn, err := p.RunHook(ctx, HookPluginData{
resp, err := p.RunHook(ctx, hooks.Request{
RootCmd: match,
Flags: flags,
CommandError: cmdErrorMessage,
})
if err != nil {
// skip misbehaving plugins, but don't halt execution
continue
return nil, false, err
}
var hookMessageData hooks.HookMessage
err = json.Unmarshal(hookReturn, &hookMessageData)
if err != nil {
continue
var message hooks.Response
if err := json.Unmarshal(resp, &message); err != nil {
return nil, false, fmt.Errorf("failed to unmarshal hook response (%q): %w", string(resp), err)
}
// currently the only hook type
if hookMessageData.Type != hooks.NextSteps {
continue
if message.Type != hooks.NextSteps {
return nil, false, errors.New("unexpected hook response type: " + strconv.Itoa(int(message.Type)))
}
processedHook, err := hooks.ParseTemplate(hookMessageData.Template, subCmd)
messages, err = hooks.ParseTemplate(message.Template, subCmd)
if err != nil {
return nil, false, err
}
return messages, true, nil
}
for pluginName, pluginCfg := range pluginsCfg {
messages, ok, err := tryInvokeHook(pluginName, pluginCfg)
if err != nil {
// skip misbehaving plugins, but don't halt execution
logrus.WithFields(logrus.Fields{
"error": err,
"plugin": pluginName,
}).Debug("Plugin hook invocation failed")
continue
}
if !ok {
continue
}
var appended bool
nextSteps, appended = appendNextSteps(nextSteps, processedHook)
nextSteps, appended = appendNextSteps(nextSteps, messages)
if !appended {
logrus.Debugf("Plugin %s responded with an empty hook message %q. Ignoring.", pluginName, string(hookReturn))
logrus.WithFields(logrus.Fields{
"plugin": pluginName,
}).Debug("Plugin responded with an empty hook message; ignoring")
}
}
return nextSteps
+2 -3
View File
@@ -177,9 +177,8 @@ func TestGetPluginDirs(t *testing.T) {
pluginDirs := getPluginDirs(cli.ConfigFile())
assert.Equal(t, strings.Join(expected, ":"), strings.Join(pluginDirs, ":"))
extras := []string{
"foo", "bar", "baz",
}
extras := make([]string, 0, 3+len(expected))
extras = append(extras, "foo", "bar", "baz")
expected = append(extras, expected...)
cli.SetConfigFile(&configfile.ConfigFile{
CLIPluginsExtraDirs: extras,
+11 -6
View File
@@ -12,6 +12,7 @@ import (
"strconv"
"strings"
"github.com/docker/cli/cli-plugins/hooks"
"github.com/docker/cli/cli-plugins/metadata"
"github.com/spf13/cobra"
)
@@ -154,7 +155,7 @@ func validateSchemaVersion(version string) error {
// RunHook executes the plugin's hooks command
// and returns its unprocessed output.
func (p *Plugin) RunHook(ctx context.Context, hookData HookPluginData) ([]byte, error) {
func (p *Plugin) RunHook(ctx context.Context, hookData hooks.Request) ([]byte, error) {
hDataBytes, err := json.Marshal(hookData)
if err != nil {
return nil, wrapAsPluginError(err, "failed to marshall hook data")
@@ -163,12 +164,16 @@ func (p *Plugin) RunHook(ctx context.Context, hookData HookPluginData) ([]byte,
pCmd := exec.CommandContext(ctx, p.Path, p.Name, metadata.HookSubcommandName, string(hDataBytes)) // #nosec G204 -- ignore "Subprocess launched with a potential tainted input or cmd arguments"
pCmd.Env = os.Environ()
pCmd.Env = append(pCmd.Env, metadata.ReexecEnvvar+"="+os.Args[0])
hookCmdOutput, err := pCmd.Output()
if err != nil {
return nil, wrapAsPluginError(err, "failed to execute plugin hook subcommand")
}
return hookCmdOutput, nil
out, err := pCmd.Output()
if err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
return nil, wrapAsPluginError(err, "plugin hook subcommand exited unsuccessfully")
}
return nil, wrapAsPluginError(err, "failed to execute plugin hook subcommand: "+pCmd.String())
}
return out, nil
}
// pluginNameFormat is used as part of errors for invalid plugin-names.
+3 -3
View File
@@ -54,9 +54,9 @@ func TestPluginServer(t *testing.T) {
})
t.Run("allows reconnects", func(t *testing.T) {
var calls int32
var calls atomic.Int32
h := func(_ net.Conn) {
atomic.AddInt32(&calls, 1)
calls.Add(1)
}
srv, err := NewPluginServer(h)
@@ -70,7 +70,7 @@ func TestPluginServer(t *testing.T) {
waitForCalls := func(n int) {
poll.WaitOn(t, func(t poll.LogT) poll.Result {
if atomic.LoadInt32(&calls) == int32(n) {
if calls.Load() == int32(n) {
return poll.Success()
}
return poll.Continue("waiting for handler to be called")
+4 -3
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package command
@@ -566,11 +566,12 @@ type ServerInfo struct {
// It applies by default the standard streams, and the content trust from
// environment.
func NewDockerCli(ops ...CLIOption) (*DockerCli, error) {
defaultOps := []CLIOption{
defaultOps := make([]CLIOption, 0, 3+len(ops))
defaultOps = append(defaultOps,
WithDefaultContextStoreConfig(),
WithStandardStreams(),
WithUserAgent(UserAgent()),
}
)
ops = append(defaultOps, ops...)
cli := &DockerCli{baseCtx: context.Background()}
+55 -15
View File
@@ -5,7 +5,6 @@ import (
"strings"
"github.com/distribution/reference"
"github.com/docker/cli/cli/command/formatter"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
@@ -23,7 +22,7 @@ type APIClientProvider interface {
// ImageNames offers completion for images present within the local store
func ImageNames(dockerCLI APIClientProvider, limit int) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return Unique(func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if limit > 0 && len(args) >= limit {
return nil, cobra.ShellCompDirectiveNoFileComp
}
@@ -36,14 +35,14 @@ func ImageNames(dockerCLI APIClientProvider, limit int) cobra.CompletionFunc {
names = append(names, img.RepoTags...)
}
return names, cobra.ShellCompDirectiveNoFileComp
}
})
}
// ImageNamesWithBase offers completion for images present within the local store,
// including both full image names with tags and base image names (repository names only)
// when multiple tags exist for the same base name
func ImageNamesWithBase(dockerCLI APIClientProvider, limit int) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return Unique(func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if limit > 0 && len(args) >= limit {
return nil, cobra.ShellCompDirectiveNoFileComp
}
@@ -69,14 +68,14 @@ func ImageNamesWithBase(dockerCLI APIClientProvider, limit int) cobra.Completion
}
}
return names, cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
}
})
}
// ContainerNames offers completion for container names and IDs
// By default, only names are returned.
// Set DOCKER_COMPLETION_SHOW_CONTAINER_IDS=yes to also complete IDs.
func ContainerNames(dockerCLI APIClientProvider, all bool, filters ...func(container.Summary) bool) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return Unique(func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
res, err := dockerCLI.Client().ContainerList(cmd.Context(), client.ContainerListOptions{
All: all,
})
@@ -101,15 +100,21 @@ func ContainerNames(dockerCLI APIClientProvider, all bool, filters ...func(conta
if showContainerIDs {
names = append(names, ctr.ID)
}
names = append(names, formatter.StripNamePrefix(ctr.Names)...)
for _, n := range ctr.Names {
// Skip legacy link names: "/linked-container/link-name"
if len(n) <= 1 || strings.IndexByte(n[1:], '/') != -1 {
continue
}
names = append(names, strings.TrimPrefix(n, "/"))
}
}
return names, cobra.ShellCompDirectiveNoFileComp
}
})
}
// VolumeNames offers completion for volumes
func VolumeNames(dockerCLI APIClientProvider) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return Unique(func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
res, err := dockerCLI.Client().VolumeList(cmd.Context(), client.VolumeListOptions{})
if err != nil {
return nil, cobra.ShellCompDirectiveError
@@ -119,12 +124,12 @@ func VolumeNames(dockerCLI APIClientProvider) cobra.CompletionFunc {
names = append(names, vol.Name)
}
return names, cobra.ShellCompDirectiveNoFileComp
}
})
}
// NetworkNames offers completion for networks
func NetworkNames(dockerCLI APIClientProvider) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return Unique(func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
res, err := dockerCLI.Client().NetworkList(cmd.Context(), client.NetworkListOptions{})
if err != nil {
return nil, cobra.ShellCompDirectiveError
@@ -134,7 +139,7 @@ func NetworkNames(dockerCLI APIClientProvider) cobra.CompletionFunc {
names = append(names, nw.Name)
}
return names, cobra.ShellCompDirectiveNoFileComp
}
})
}
// EnvVarNames offers completion for environment-variable names. This
@@ -151,7 +156,7 @@ func NetworkNames(dockerCLI APIClientProvider) cobra.CompletionFunc {
// docker run --rm --env MY_VAR alpine printenv MY_VAR
// hello
func EnvVarNames() cobra.CompletionFunc {
return func(_ *cobra.Command, _ []string, _ string) (names []string, _ cobra.ShellCompDirective) {
return Unique(func(_ *cobra.Command, _ []string, _ string) (names []string, _ cobra.ShellCompDirective) {
envs := os.Environ()
names = make([]string, 0, len(envs))
for _, env := range envs {
@@ -159,12 +164,12 @@ func EnvVarNames() cobra.CompletionFunc {
names = append(names, name)
}
return names, cobra.ShellCompDirectiveNoFileComp
}
})
}
// FromList offers completion for the given list of options.
func FromList(options ...string) cobra.CompletionFunc {
return cobra.FixedCompletions(options, cobra.ShellCompDirectiveNoFileComp)
return Unique(cobra.FixedCompletions(options, cobra.ShellCompDirectiveNoFileComp))
}
// FileNames is a convenience function to use [cobra.ShellCompDirectiveDefault],
@@ -218,3 +223,38 @@ func Platforms() cobra.CompletionFunc {
return commonPlatforms, cobra.ShellCompDirectiveNoFileComp
}
}
// Unique wraps a completion func and removes completion results that are
// already consumed (i.e., appear in "args").
//
// For example:
//
// # initial completion: args is empty, so all results are shown
// command <tab>
// one two three
//
// # "one" is already used so omitted
// command one <tab>
// two three
func Unique(fn cobra.CompletionFunc) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
all, dir := fn(cmd, args, toComplete)
if len(all) == 0 || len(args) == 0 {
return all, dir
}
alreadyCompleted := make(map[string]struct{}, len(args))
for _, a := range args {
alreadyCompleted[a] = struct{}{}
}
out := make([]string, 0, len(all))
for _, c := range all {
if _, ok := alreadyCompleted[c]; !ok {
out = append(out, c)
}
}
return out, dir
}
}
+25 -5
View File
@@ -84,7 +84,7 @@ func TestCompleteContainerNames(t *testing.T) {
{ID: "id-b", State: container.StateCreated, Names: []string{"/container-b"}},
{ID: "id-a", State: container.StateExited, Names: []string{"/container-a"}},
},
expOut: []string{"container-c", "container-c/link-b", "container-b", "container-a"},
expOut: []string{"container-c", "container-b", "container-a"},
expOpts: client.ContainerListOptions{All: true},
expDirective: cobra.ShellCompDirectiveNoFileComp,
},
@@ -97,7 +97,7 @@ func TestCompleteContainerNames(t *testing.T) {
{ID: "id-b", State: container.StateCreated, Names: []string{"/container-b"}},
{ID: "id-a", State: container.StateExited, Names: []string{"/container-a"}},
},
expOut: []string{"id-c", "container-c", "container-c/link-b", "id-b", "container-b", "id-a", "container-a"},
expOut: []string{"id-c", "container-c", "id-b", "container-b", "id-a", "container-a"},
expOpts: client.ContainerListOptions{All: true},
expDirective: cobra.ShellCompDirectiveNoFileComp,
},
@@ -107,7 +107,7 @@ func TestCompleteContainerNames(t *testing.T) {
containers: []container.Summary{
{ID: "id-c", State: container.StateRunning, Names: []string{"/container-c", "/container-c/link-b"}},
},
expOut: []string{"container-c", "container-c/link-b"},
expOut: []string{"container-c"},
expDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
@@ -117,7 +117,7 @@ func TestCompleteContainerNames(t *testing.T) {
func(ctr container.Summary) bool { return ctr.State == container.StateCreated },
},
containers: []container.Summary{
{ID: "id-c", State: container.StateRunning, Names: []string{"/container-c", "/container-c/link-b"}},
{ID: "id-c", State: container.StateRunning, Names: []string{"/container-c"}},
{ID: "id-b", State: container.StateCreated, Names: []string{"/container-b"}},
{ID: "id-a", State: container.StateExited, Names: []string{"/container-a"}},
},
@@ -133,7 +133,7 @@ func TestCompleteContainerNames(t *testing.T) {
func(ctr container.Summary) bool { return ctr.State == container.StateCreated },
},
containers: []container.Summary{
{ID: "id-c", State: container.StateRunning, Names: []string{"/container-c", "/container-c/link-b"}},
{ID: "id-c", State: container.StateRunning, Names: []string{"/container-c"}},
{ID: "id-b", State: container.StateCreated, Names: []string{"/container-b"}},
{ID: "id-a", State: container.StateCreated, Names: []string{"/container-a"}},
},
@@ -351,3 +351,23 @@ func TestCompleteVolumeNames(t *testing.T) {
})
}
}
func TestUnique(t *testing.T) {
base := []string{"alpha", "beta", "gamma"}
comp := Unique(func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
return base, cobra.ShellCompDirectiveNoFileComp
})
values, directives := comp(&cobra.Command{}, []string{"beta"}, "")
assert.Check(t, is.Equal(directives&cobra.ShellCompDirectiveNoFileComp, cobra.ShellCompDirectiveNoFileComp))
assert.Check(t, is.DeepEqual(values, []string{"alpha", "gamma"}))
assert.Check(t, is.DeepEqual(base, []string{"alpha", "beta", "gamma"}))
values, directives = comp(&cobra.Command{}, []string{"gamma"}, "")
assert.Check(t, is.Equal(directives&cobra.ShellCompDirectiveNoFileComp, cobra.ShellCompDirectiveNoFileComp))
assert.Check(t, is.DeepEqual(values, []string{"alpha", "beta"}))
assert.Check(t, is.DeepEqual(base, []string{"alpha", "beta", "gamma"}))
}
+5
View File
@@ -1,7 +1,11 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.25
package config
import (
"fmt"
"slices"
"strings"
"time"
@@ -102,6 +106,7 @@ func (c *configContext) Labels() string {
for k, v := range mapLabels {
joinLabels = append(joinLabels, k+"="+v)
}
slices.Sort(joinLabels)
return strings.Join(joinLabels, ",")
}
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package config
+8
View File
@@ -36,6 +36,7 @@ type fakeClient struct {
infoFunc func() (client.SystemInfoResult, error)
containerStatPathFunc func(containerID, path string) (client.ContainerStatPathResult, error)
containerCopyFromFunc func(containerID, srcPath string) (client.CopyFromContainerResult, error)
containerCopyToFunc func(containerID string, options client.CopyToContainerOptions) (client.CopyToContainerResult, error)
logFunc func(string, client.ContainerLogsOptions) (client.ContainerLogsResult, error)
waitFunc func(string) client.ContainerWaitResult
containerListFunc func(client.ContainerListOptions) (client.ContainerListResult, error)
@@ -128,6 +129,13 @@ func (f *fakeClient) CopyFromContainer(_ context.Context, containerID string, op
return client.CopyFromContainerResult{}, nil
}
func (f *fakeClient) CopyToContainer(_ context.Context, containerID string, options client.CopyToContainerOptions) (client.CopyToContainerResult, error) {
if f.containerCopyToFunc != nil {
return f.containerCopyToFunc(containerID, options)
}
return client.CopyToContainerResult{}, nil
}
func (f *fakeClient) ContainerLogs(_ context.Context, containerID string, options client.ContainerLogsOptions) (client.ContainerLogsResult, error) {
if f.logFunc != nil {
return f.logFunc(containerID, options)
+30 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package container
@@ -182,6 +182,35 @@ func completeLink(dockerCLI completion.APIClientProvider) cobra.CompletionFunc {
}
}
// completeLinks implements shell completion for the `--link` option of `rm --link`.
//
// It contacts the API to get names of legacy links on containers.
// In case of an error, an empty list is returned.
func completeLinks(dockerCLI completion.APIClientProvider) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
res, err := dockerCLI.Client().ContainerList(cmd.Context(), client.ContainerListOptions{
All: true,
})
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
var names []string
for _, ctr := range res.Items {
if len(ctr.Names) <= 1 {
// Container has no links names.
continue
}
for _, n := range ctr.Names {
// Skip legacy link names: "/linked-container/link-name"
if len(n) > 1 && strings.IndexByte(n[1:], '/') != -1 {
names = append(names, strings.TrimPrefix(n, "/"))
}
}
}
return names, cobra.ShellCompDirectiveNoFileComp
}
}
// completeLogDriver implements shell completion for the `--log-driver` option of `run` and `create`.
// The log drivers are collected from a call to the Info endpoint with a fallback to a hard-coded list
// of the build-in log drivers.
+41
View File
@@ -135,3 +135,44 @@ func TestCompleteSignals(t *testing.T) {
assert.Check(t, len(values) > 1)
assert.Check(t, is.Len(values, len(signal.SignalMap)))
}
func TestCompleteLinks(t *testing.T) {
tests := []struct {
doc string
showAll, showIDs bool
filters []func(container.Summary) bool
containers []container.Summary
expOut []string
expDirective cobra.ShellCompDirective
}{
{
doc: "no results",
expDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "all containers",
showAll: true,
containers: []container.Summary{
{ID: "id-c", State: container.StateRunning, Names: []string{"/container-c", "/container-c/link-b", "/container-c/link-c"}},
{ID: "id-b", State: container.StateCreated, Names: []string{"/container-b", "/container-b/link-a"}},
{ID: "id-a", State: container.StateExited, Names: []string{"/container-a"}},
},
expOut: []string{"container-c/link-b", "container-c/link-c", "container-b/link-a"},
expDirective: cobra.ShellCompDirectiveNoFileComp,
},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
comp := completeLinks(test.NewFakeCli(&fakeClient{
containerListFunc: func(client.ContainerListOptions) (client.ContainerListResult, error) {
return client.ContainerListResult{Items: tc.containers}, nil
},
}))
containers, directives := comp(&cobra.Command{}, nil, "")
assert.Check(t, is.Equal(directives&tc.expDirective, tc.expDirective))
assert.Check(t, is.DeepEqual(containers, tc.expOut))
})
}
}
+61 -4
View File
@@ -158,7 +158,7 @@ container source to stdout.`,
}
flags := cmd.Flags()
flags.BoolVarP(&opts.followLink, "follow-link", "L", false, "Always follow symbol link in SRC_PATH")
flags.BoolVarP(&opts.followLink, "follow-link", "L", false, "Always follow symlinks in SRC_PATH")
flags.BoolVarP(&opts.copyUIDGID, "archive", "a", false, "Archive mode (copy all uid/gid information)")
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress progress output during copy. Progress output is automatically suppressed if no terminal is attached")
return cmd
@@ -168,6 +168,50 @@ func progressHumanSize(n int64) string {
return units.HumanSizeWithPrecision(float64(n), 3)
}
// localContentSize returns the total size of regular file content at path.
// For a regular file it returns the file size. For a directory it walks
// the tree and sums sizes of all regular files.
func localContentSize(path string) (int64, error) {
fi, err := os.Lstat(path)
if err != nil {
return -1, err
}
if !fi.IsDir() {
if fi.Mode().IsRegular() {
return fi.Size(), nil
}
return 0, nil
}
var total int64
err = filepath.WalkDir(path, func(_ string, d os.DirEntry, err error) error {
if err != nil {
return err
}
if d.Type().IsRegular() {
info, err := d.Info()
if err != nil {
return err
}
total += info.Size()
}
return nil
})
return total, err
}
// copySummary formats the "Successfully copied ..." message.
// When contentSize differs from transferredSize, both values are shown.
func copySummary(contentSize, transferredSize int64, dest string) string {
if contentSize != transferredSize {
return fmt.Sprintf("Successfully copied %s (transferred %s) to %s\n",
progressHumanSize(contentSize), progressHumanSize(transferredSize), dest,
)
}
return fmt.Sprintf("Successfully copied %s to %s\n",
progressHumanSize(contentSize), dest,
)
}
func runCopy(ctx context.Context, dockerCli command.Cli, opts copyOptions) error {
srcContainer, srcPath := splitCpArg(opts.source)
destContainer, destPath := splitCpArg(opts.destination)
@@ -227,7 +271,7 @@ func copyFromContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cp
}
apiClient := dockerCLI.Client()
// if client requests to follow symbol link, then must decide target file to be copied
// if client requests to follow symlinks, then must decide target file to be copied
var rebaseName string
if copyConfig.followLink {
src, err := apiClient.ContainerStatPath(ctx, copyConfig.container, client.ContainerStatPathOptions{
@@ -295,7 +339,11 @@ func copyFromContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cp
cancel()
<-done
restore()
_, _ = fmt.Fprintln(dockerCLI.Err(), "Successfully copied", progressHumanSize(copiedSize), "to", dstPath)
reportedSize := copiedSize
if !cpRes.Stat.Mode.IsDir() {
reportedSize = cpRes.Stat.Size
}
_, _ = fmt.Fprint(dockerCLI.Err(), copySummary(reportedSize, copiedSize, dstPath))
return res
}
@@ -354,11 +402,14 @@ func copyToContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cpCo
content io.ReadCloser
resolvedDstPath string
copiedSize int64
contentSize int64
sizeErr error
)
if srcPath == "-" {
content = os.Stdin
resolvedDstPath = dstInfo.Path
sizeErr = errors.New("content size not available for stdin")
if !dstInfo.IsDir {
return fmt.Errorf(`destination "%s:%s" must be a directory`, copyConfig.container, dstPath)
}
@@ -369,6 +420,8 @@ func copyToContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cpCo
return err
}
contentSize, sizeErr = localContentSize(srcInfo.Path)
srcArchive, err := archive.TarResource(srcInfo)
if err != nil {
return err
@@ -421,7 +474,11 @@ func copyToContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cpCo
cancel()
<-done
restore()
_, _ = fmt.Fprintln(dockerCLI.Err(), "Successfully copied", progressHumanSize(copiedSize), "to", copyConfig.container+":"+dstInfo.Path)
reportedSize := copiedSize
if sizeErr == nil {
reportedSize = contentSize
}
_, _ = fmt.Fprint(dockerCLI.Err(), copySummary(reportedSize, copiedSize, copyConfig.container+":"+dstInfo.Path))
return err
}
+235
View File
@@ -11,6 +11,7 @@ import (
"github.com/docker/cli/internal/test"
"github.com/moby/go-archive"
"github.com/moby/go-archive/compression"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
@@ -211,3 +212,237 @@ func TestRunCopyFromContainerToFilesystemIrregularDestination(t *testing.T) {
expected := `"/dev/random" must be a directory or a regular file`
assert.ErrorContains(t, err, expected)
}
func TestCopySummary(t *testing.T) {
tests := []struct {
name string
contentSize int64
transferredSize int64
dest string
wantContains string
wantNoContain string
}{
{
name: "different sizes shows both",
contentSize: 5,
transferredSize: 2048,
dest: "/dst",
wantContains: "(transferred",
},
{
name: "equal sizes shows single value",
contentSize: 100,
transferredSize: 100,
dest: "/dst",
wantNoContain: "(transferred",
},
{
name: "both zero",
contentSize: 0,
transferredSize: 0,
dest: "ctr:/dst",
wantContains: "Successfully copied 0B to ctr:/dst",
wantNoContain: "(transferred",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got := copySummary(tc.contentSize, tc.transferredSize, tc.dest)
if tc.wantContains != "" {
assert.Check(t, is.Contains(got, tc.wantContains))
}
if tc.wantNoContain != "" {
assert.Check(t, !strings.Contains(got, tc.wantNoContain), "unexpected substring %q in %q", tc.wantNoContain, got)
}
})
}
}
func TestCopyFromContainerReportsFileSize(t *testing.T) {
// The file content is "hello" (5 bytes), but the TAR archive wrapping
// it is much larger due to headers and padding. The success message
// should report the actual file size (5B), not the TAR stream size.
srcDir := fs.NewDir(t, "cp-test-from",
fs.WithFile("file1", "hello"))
destDir := fs.NewDir(t, "cp-test-from-dest")
const fileSize int64 = 5
fakeCli := test.NewFakeCli(&fakeClient{
containerCopyFromFunc: func(ctr, srcPath string) (client.CopyFromContainerResult, error) {
readCloser, err := archive.Tar(srcDir.Path(), compression.None)
return client.CopyFromContainerResult{
Content: readCloser,
Stat: container.PathStat{
Name: "file1",
Size: fileSize,
},
}, err
},
})
err := runCopy(context.TODO(), fakeCli, copyOptions{
source: "container:/file1",
destination: destDir.Path(),
})
assert.NilError(t, err)
errOut := fakeCli.ErrBuffer().String()
assert.Check(t, is.Contains(errOut, "Successfully copied 5B"))
assert.Check(t, is.Contains(errOut, "(transferred"))
}
func TestCopyToContainerReportsFileSize(t *testing.T) {
// Create a temp file with known content ("hello" = 5 bytes).
// The TAR archive sent to the container is larger, but the success
// message should report the actual content size.
srcFile := fs.NewFile(t, "cp-test-to", fs.WithContent("hello"))
fakeCli := test.NewFakeCli(&fakeClient{
containerStatPathFunc: func(containerID, path string) (client.ContainerStatPathResult, error) {
return client.ContainerStatPathResult{
Stat: container.PathStat{
Name: "tmp",
Mode: os.ModeDir | 0o755,
},
}, nil
},
containerCopyToFunc: func(containerID string, options client.CopyToContainerOptions) (client.CopyToContainerResult, error) {
_, _ = io.Copy(io.Discard, options.Content)
return client.CopyToContainerResult{}, nil
},
})
err := runCopy(context.TODO(), fakeCli, copyOptions{
source: srcFile.Path(),
destination: "container:/tmp",
})
assert.NilError(t, err)
errOut := fakeCli.ErrBuffer().String()
assert.Check(t, is.Contains(errOut, "Successfully copied 5B"))
assert.Check(t, is.Contains(errOut, "(transferred"))
}
func TestCopyToContainerReportsEmptyFileSize(t *testing.T) {
srcFile := fs.NewFile(t, "cp-test-empty", fs.WithContent(""))
fakeCli := test.NewFakeCli(&fakeClient{
containerStatPathFunc: func(containerID, path string) (client.ContainerStatPathResult, error) {
return client.ContainerStatPathResult{
Stat: container.PathStat{
Name: "tmp",
Mode: os.ModeDir | 0o755,
},
}, nil
},
containerCopyToFunc: func(containerID string, options client.CopyToContainerOptions) (client.CopyToContainerResult, error) {
_, _ = io.Copy(io.Discard, options.Content)
return client.CopyToContainerResult{}, nil
},
})
err := runCopy(context.TODO(), fakeCli, copyOptions{
source: srcFile.Path(),
destination: "container:/tmp",
})
assert.NilError(t, err)
errOut := fakeCli.ErrBuffer().String()
assert.Check(t, is.Contains(errOut, "Successfully copied 0B"))
assert.Check(t, is.Contains(errOut, "(transferred"))
}
func TestCopyToContainerReportsDirectorySize(t *testing.T) {
// Create a temp directory with files "aaa" (3 bytes) + "bbb" (3 bytes) = 6 bytes.
// The TAR archive is much larger, but the success message should report 6B.
srcDir := fs.NewDir(t, "cp-test-dir",
fs.WithFile("aaa", "aaa"),
fs.WithFile("bbb", "bbb"),
)
fakeCli := test.NewFakeCli(&fakeClient{
containerStatPathFunc: func(containerID, path string) (client.ContainerStatPathResult, error) {
return client.ContainerStatPathResult{
Stat: container.PathStat{
Name: "tmp",
Mode: os.ModeDir | 0o755,
},
}, nil
},
containerCopyToFunc: func(containerID string, options client.CopyToContainerOptions) (client.CopyToContainerResult, error) {
_, _ = io.Copy(io.Discard, options.Content)
return client.CopyToContainerResult{}, nil
},
})
err := runCopy(context.TODO(), fakeCli, copyOptions{
source: srcDir.Path() + string(os.PathSeparator),
destination: "container:/tmp",
})
assert.NilError(t, err)
errOut := fakeCli.ErrBuffer().String()
assert.Check(t, is.Contains(errOut, "Successfully copied 6B"))
assert.Check(t, is.Contains(errOut, "(transferred"))
}
func TestCopyFromContainerReportsDirectorySize(t *testing.T) {
// When copying a directory from a container, cpRes.Stat.Mode.IsDir() is true,
// so reportedSize falls back to copiedSize (the tar stream bytes).
srcDir := fs.NewDir(t, "cp-test-fromdir",
fs.WithFile("file1", "hello"))
destDir := fs.NewDir(t, "cp-test-fromdir-dest")
fakeCli := test.NewFakeCli(&fakeClient{
containerCopyFromFunc: func(ctr, srcPath string) (client.CopyFromContainerResult, error) {
readCloser, err := archive.Tar(srcDir.Path(), compression.None)
return client.CopyFromContainerResult{
Content: readCloser,
Stat: container.PathStat{
Name: "mydir",
Mode: os.ModeDir | 0o755,
},
}, err
},
})
err := runCopy(context.TODO(), fakeCli, copyOptions{
source: "container:/mydir",
destination: destDir.Path(),
})
assert.NilError(t, err)
errOut := fakeCli.ErrBuffer().String()
assert.Check(t, is.Contains(errOut, "Successfully copied"))
// For directories from container, content size is unknown so
// reportedSize == copiedSize and "(transferred ...)" is omitted.
assert.Check(t, !strings.Contains(errOut, "(transferred"))
}
func TestCopyToContainerStdinReportsTransferredSize(t *testing.T) {
// When copying from stdin, content size is unknown.
// The message should report transferred bytes without "(transferred ...)".
r, w, _ := os.Pipe()
_, _ = w.WriteString("some data from stdin")
w.Close()
oldStdin := os.Stdin
os.Stdin = r
t.Cleanup(func() { os.Stdin = oldStdin })
fakeCli := test.NewFakeCli(&fakeClient{
containerStatPathFunc: func(containerID, path string) (client.ContainerStatPathResult, error) {
return client.ContainerStatPathResult{
Stat: container.PathStat{
Name: "tmp",
Mode: os.ModeDir | 0o755,
},
}, nil
},
containerCopyToFunc: func(containerID string, options client.CopyToContainerOptions) (client.CopyToContainerResult, error) {
_, _ = io.Copy(io.Discard, options.Content)
return client.CopyToContainerResult{}, nil
},
})
err := runCopy(context.TODO(), fakeCli, copyOptions{
source: "-",
destination: "container:/tmp",
})
assert.NilError(t, err)
errOut := fakeCli.ErrBuffer().String()
assert.Check(t, is.Contains(errOut, "Successfully copied"))
// stdin has no content size, so reportedSize == copiedSize and
// "(transferred ...)" should not appear.
assert.Check(t, !strings.Contains(errOut, "(transferred"))
}
+29 -22
View File
@@ -2,6 +2,7 @@ package container
import (
"strconv"
"strings"
"sync"
"github.com/docker/cli/cli/command/formatter"
@@ -111,31 +112,27 @@ func NewStatsFormat(source, osType string) formatter.Format {
return formatter.Format(source)
}
// NewStats returns a new Stats entity and sets in it the given name
func NewStats(container string) *Stats {
return &Stats{StatsEntry: StatsEntry{Container: container}}
// NewStats returns a new Stats entity using the given ID, ID-prefix, or
// name to resolve the container.
func NewStats(idOrName string) *Stats {
// FIXME(thaJeztah): "idOrName" is used for fuzzy-matching the container, which can result in multiple stats for the same container.
// We should resolve the canonical ID once, then use that as reference
// to prevent duplicates. Various parts in the code compare Container
// against "ID" only (not considering "name" or "ID-prefix").
return &Stats{StatsEntry: StatsEntry{Container: idOrName}}
}
// statsFormatWrite renders the context for a list of containers statistics
func statsFormatWrite(ctx formatter.Context, stats []StatsEntry, osType string, trunc bool) error {
render := func(format func(subContext formatter.SubContext) error) error {
for _, cstats := range stats {
statsCtx := &statsContext{
s: cstats,
os: osType,
trunc: trunc,
}
if err := format(statsCtx); err != nil {
return err
}
}
return nil
}
// TODO(thaJeztah): this should be taken from the (first) StatsEntry instead.
// also, assuming all stats are for the same platform (and basing the
// column headers on that) won't allow aggregated results, which could
// be mixed platform.
memUsage := memUseHeader
if osType == winOSType {
memUsage = winMemUseHeader
}
statsCtx := statsContext{}
statsCtx := statsContext{os: osType}
statsCtx.Header = formatter.SubHeaderContext{
"Container": containerHeader,
"Name": formatter.NameHeader,
@@ -147,8 +144,18 @@ func statsFormatWrite(ctx formatter.Context, stats []StatsEntry, osType string,
"BlockIO": blockIOHeader,
"PIDs": pidsHeader,
}
statsCtx.os = osType
return ctx.Write(&statsCtx, render)
return ctx.Write(&statsCtx, func(format func(subContext formatter.SubContext) error) error {
for _, cstats := range stats {
if err := format(&statsContext{
s: cstats,
os: osType,
trunc: trunc,
}); err != nil {
return err
}
}
return nil
})
}
type statsContext struct {
@@ -167,9 +174,9 @@ func (c *statsContext) Container() string {
}
func (c *statsContext) Name() string {
// TODO(thaJeztah): make this explicitly trim the "/" prefix, not just any char.
if len(c.s.Name) > 1 {
return c.s.Name[1:]
// Trim the "/" prefix (if present).
if name := strings.TrimPrefix(c.s.Name, "/"); name != "" {
return name
}
return noValue
}
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package container
+4 -4
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package container
@@ -371,7 +371,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
var binds []string
volumes := copts.volumes.GetMap()
// add any bind targets to the list of container volumes
for bind := range copts.volumes.GetMap() {
for bind := range volumes {
parsed, err := volumespec.Parse(bind)
if err != nil {
return nil, err
@@ -506,13 +506,13 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
// collect all the environment variables for the container
envVariables, err := opts.ReadKVEnvStrings(copts.envFile.GetSlice(), copts.env.GetSlice())
if err != nil {
return nil, err
return nil, fmt.Errorf("--env-file: %w", err)
}
// collect all the labels for the container
labels, err := opts.ReadKVStrings(copts.labelsFile.GetSlice(), copts.labels.GetSlice())
if err != nil {
return nil, err
return nil, fmt.Errorf("--label-file: %w", err)
}
pidMode := container.PidMode(copts.pidMode)
+18 -15
View File
@@ -829,13 +829,6 @@ func TestParseRestartPolicy(t *testing.T) {
Name: container.RestartPolicyAlways,
},
},
{
input: "always:1",
expected: container.RestartPolicy{
Name: container.RestartPolicyAlways,
MaximumRetryCount: 1,
},
},
{
input: "always:2:3",
expectedErr: "invalid restart policy format: maximum retry count must be an integer",
@@ -861,6 +854,16 @@ func TestParseRestartPolicy(t *testing.T) {
input: "unless-stopped:invalid",
expectedErr: "invalid restart policy format: maximum retry count must be an integer",
},
// Unknown / invalid combinations: validation is handled by the daemon>
{
input: "anything:123",
expected: container.RestartPolicy{Name: "anything", MaximumRetryCount: 123},
},
{
input: "negative:-123",
expected: container.RestartPolicy{Name: "negative", MaximumRetryCount: -123},
},
}
for _, tc := range tests {
t.Run(tc.input, func(t *testing.T) {
@@ -937,13 +940,13 @@ func TestParseLoggingOpts(t *testing.T) {
}
func TestParseEnvfileVariables(t *testing.T) {
e := "open nonexistent: no such file or directory"
expErr := "--env-file: open nonexistent: no such file or directory"
if runtime.GOOS == "windows" {
e = "open nonexistent: The system cannot find the file specified."
expErr = "--env-file: open nonexistent: The system cannot find the file specified."
}
// env ko
if _, _, _, err := parseRun([]string{"--env-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
t.Fatalf("Expected an error with message '%s', got %v", e, err)
if _, _, _, err := parseRun([]string{"--env-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != expErr {
t.Fatalf("Expected an error with message '%s', got %v", expErr, err)
}
// env ok
config, _, _, err := parseRun([]string{"--env-file=testdata/valid.env", "img", "cmd"})
@@ -990,13 +993,13 @@ func TestParseEnvfileVariablesWithBOMUnicode(t *testing.T) {
}
func TestParseLabelfileVariables(t *testing.T) {
e := "open nonexistent: no such file or directory"
expErr := "--label-file: open nonexistent: no such file or directory"
if runtime.GOOS == "windows" {
e = "open nonexistent: The system cannot find the file specified."
expErr = "--label-file: open nonexistent: The system cannot find the file specified."
}
// label ko
if _, _, _, err := parseRun([]string{"--label-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
t.Fatalf("Expected an error with message '%s', got %v", e, err)
if _, _, _, err := parseRun([]string{"--label-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != expErr {
t.Fatalf("Expected an error with message '%s', got %v", expErr, err)
}
// label ok
config, _, _, err := parseRun([]string{"--label-file=testdata/valid.label", "img", "cmd"})
+12 -3
View File
@@ -27,6 +27,11 @@ type rmOptions struct {
func newRmCommand(dockerCLI command.Cli) *cobra.Command {
var opts rmOptions
completeLinkNames := completeLinks(dockerCLI)
completeNames := completion.ContainerNames(dockerCLI, true, func(ctr container.Summary) bool {
return opts.force || ctr.State == container.StateExited || ctr.State == container.StateCreated
})
cmd := &cobra.Command{
Use: "rm [OPTIONS] CONTAINER [CONTAINER...]",
Short: "Remove one or more containers",
@@ -38,9 +43,13 @@ func newRmCommand(dockerCLI command.Cli) *cobra.Command {
Annotations: map[string]string{
"aliases": "docker container rm, docker container remove, docker rm",
},
ValidArgsFunction: completion.ContainerNames(dockerCLI, true, func(ctr container.Summary) bool {
return opts.force || ctr.State == container.StateExited || ctr.State == container.StateCreated
}),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if opts.rmLink {
// "--link" (remove link) is set; provide link names instead of container (primary) names.
return completeLinkNames(cmd, args, toComplete)
}
return completeNames(cmd, args, toComplete)
},
DisableFlagsInUseLine: true,
}
+67 -65
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package container
@@ -7,9 +7,7 @@ import (
"bytes"
"context"
"errors"
"fmt"
"io"
"strings"
"sync"
"time"
@@ -144,39 +142,32 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
}
eh := newEventHandler()
addEvents := []events.Action{events.ActionStart}
if options.All {
eh.setHandler(events.ActionCreate, func(e events.Message) {
if s := NewStats(e.Actor.ID); cStats.add(s) {
waitFirst.Add(1)
log.G(ctx).WithFields(log.Fields{
"event": e.Action,
"container": e.Actor.ID,
}).Debug("collecting stats for container")
go collect(ctx, s, apiClient, !options.NoStream, waitFirst)
}
})
addEvents = append(addEvents, events.ActionCreate)
}
eh.setHandler(events.ActionStart, func(e events.Message) {
eh.setHandler(addEvents, func(ctx context.Context, e events.Message) {
if s := NewStats(e.Actor.ID); cStats.add(s) {
waitFirst.Add(1)
log.G(ctx).WithFields(log.Fields{
"event": e.Action,
"container": e.Actor.ID,
}).Debug("collecting stats for container")
log.G(ctx).Debug("collecting stats for container")
go collect(ctx, s, apiClient, !options.NoStream, waitFirst)
}
})
// Remove containers when they are removed ("destroyed"); containers
// do not emit [events.ActionRemove], only [events.ActionDestroy].
//
// When running with "--all" we don't remove containers when they die,
// because they may come back, but without "--all" we remove them
// on the first possible occasion (either "die" or "destroy").
rmEvents := []events.Action{events.ActionDestroy}
if !options.All {
eh.setHandler(events.ActionDie, func(e events.Message) {
log.G(ctx).WithFields(log.Fields{
"event": e.Action,
"container": e.Actor.ID,
}).Debug("stop collecting stats for container")
cStats.remove(e.Actor.ID)
})
rmEvents = append(rmEvents, events.ActionDie)
}
eh.setHandler(rmEvents, func(ctx context.Context, e events.Message) {
log.G(ctx).Debug("stop collecting stats for container")
cStats.remove(e.Actor.ID)
})
// monitorContainerEvents watches for container creation and removal (only
// used when calling `docker stats` without arguments).
@@ -216,7 +207,7 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
}
eventChan := make(chan events.Message)
go eh.watch(eventChan)
go eh.watch(ctx, eventChan)
stopped := make(chan struct{})
go monitorContainerEvents(started, eventChan, stopped)
defer close(stopped)
@@ -294,30 +285,32 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
}
}
// Buffer to store formatted stats text.
// Once formatted, it will be printed in one write to avoid screen flickering.
var statsTextBuffer bytes.Buffer
// renderBuf holds the formatted stats output produced by statsFormatWrite.
// It does not include any terminal control sequences.
var renderBuf bytes.Buffer
// frameBuf holds the final terminal frame, including cursor movement and
// line-clearing escape sequences, written in a single pass to avoid flicker.
var frameBuf bytes.Buffer
statsCtx := formatter.Context{
Output: &statsTextBuffer,
Output: &renderBuf,
Format: NewStatsFormat(format, daemonOSType),
}
if options.NoStream {
cStats.mu.RLock()
ccStats := make([]StatsEntry, 0, len(cStats.cs))
for _, c := range cStats.cs {
ccStats = append(ccStats, c.GetStatistics())
}
cStats.mu.RUnlock()
if len(ccStats) == 0 {
statsList := cStats.snapshot()
if len(statsList) == 0 {
return nil
}
ccStats := make([]StatsEntry, 0, len(statsList))
for _, c := range statsList {
ccStats = append(ccStats, c.GetStatistics())
}
if err := statsFormatWrite(statsCtx, ccStats, daemonOSType, !options.NoTrunc); err != nil {
return err
}
_, _ = fmt.Fprint(dockerCLI.Out(), statsTextBuffer.String())
_, _ = dockerCLI.Out().Write(renderBuf.Bytes())
return nil
}
@@ -326,34 +319,38 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
for {
select {
case <-ticker.C:
cStats.mu.RLock()
ccStats := make([]StatsEntry, 0, len(cStats.cs))
for _, c := range cStats.cs {
renderBuf.Reset()
frameBuf.Reset()
statsList := cStats.snapshot()
if len(statsList) == 0 && !showAll {
// Clear screen
_, _ = io.WriteString(dockerCLI.Out(), "\033[H\033[J")
return nil
}
ccStats := make([]StatsEntry, 0, len(statsList))
for _, c := range statsList {
ccStats = append(ccStats, c.GetStatistics())
}
cStats.mu.RUnlock()
// Start by moving the cursor to the top-left
_, _ = fmt.Fprint(&statsTextBuffer, "\033[H")
if err := statsFormatWrite(statsCtx, ccStats, daemonOSType, !options.NoTrunc); err != nil {
return err
}
for line := range strings.SplitSeq(statsTextBuffer.String(), "\n") {
// Start by moving the cursor to the top-left
_, _ = io.WriteString(&frameBuf, "\033[H")
// TODO(thaJeztah): consider wrapping the writer to inject ANSI (line-clearing) during formatting.
// instead of post-processing the results.
for line := range bytes.SplitSeq(renderBuf.Bytes(), []byte{'\n'}) {
// In case the new text is shorter than the one we are writing over,
// we'll append the "erase line" escape sequence to clear the remaining text.
_, _ = fmt.Fprintln(&statsTextBuffer, line, "\033[K")
_, _ = frameBuf.Write(line)
_, _ = io.WriteString(&frameBuf, "\033[K")
_ = frameBuf.WriteByte('\n')
}
// We might have fewer containers than before, so let's clear the remaining text
_, _ = fmt.Fprint(&statsTextBuffer, "\033[J")
_, _ = fmt.Fprint(dockerCLI.Out(), statsTextBuffer.String())
statsTextBuffer.Reset()
if len(ccStats) == 0 && !showAll {
return nil
}
_, _ = io.WriteString(&frameBuf, "\033[J")
_, _ = dockerCLI.Out().Write(frameBuf.Bytes())
case err, ok := <-closeChan:
if !ok || err == nil || errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
// Suppress "unexpected EOF" errors in the CLI so that
@@ -369,33 +366,38 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
// newEventHandler initializes and returns an eventHandler
func newEventHandler() *eventHandler {
return &eventHandler{handlers: make(map[events.Action]func(events.Message))}
return &eventHandler{handlers: make(map[events.Action]func(context.Context, events.Message))}
}
// eventHandler allows for registering specific events to setHandler.
type eventHandler struct {
handlers map[events.Action]func(events.Message)
handlers map[events.Action]func(context.Context, events.Message)
}
func (eh *eventHandler) setHandler(action events.Action, handler func(events.Message)) {
eh.handlers[action] = handler
func (eh *eventHandler) setHandler(actions []events.Action, handler func(context.Context, events.Message)) {
for _, action := range actions {
eh.handlers[action] = handler
}
}
// watch ranges over the passed in event chan and processes the events based on the
// handlers created for a given action.
// To stop watching, close the event chan.
func (eh *eventHandler) watch(c <-chan events.Message) {
func (eh *eventHandler) watch(ctx context.Context, c <-chan events.Message) {
for e := range c {
h, exists := eh.handlers[e.Action]
if !exists {
continue
}
if e.Actor.ID == "" {
log.G(context.TODO()).WithField("event", e).Errorf("event handler: received %s event with empty ID", e.Action)
log.G(ctx).WithField("event", e).Errorf("event handler: received %s event with empty ID", e.Action)
continue
}
logger := log.G(ctx).WithFields(log.Fields{
"event": e.Action,
"container": e.Actor.ID,
})
log.G(context.TODO()).WithField("event", e).Debugf("event handler: received %s event for: %s", e.Action, e.Actor.ID)
go h(e)
go h(log.WithLogger(ctx, logger), e)
}
}
+16
View File
@@ -49,6 +49,22 @@ func (s *stats) isKnownContainer(cid string) (int, bool) {
return -1, false
}
// snapshot returns a point-in-time copy of the tracked container list
// (the slice of *Stats pointers). The returned slice is safe for use
// without holding the stats lock, but the underlying Stats values may
// continue to change concurrently.
func (s *stats) snapshot() []*Stats {
s.mu.RLock()
defer s.mu.RUnlock()
if len(s.cs) == 0 {
return nil
}
// https://github.com/golang/go/issues/53643
cp := make([]*Stats, len(s.cs))
copy(cp, s.cs)
return cp
}
func collect(ctx context.Context, s *Stats, cli client.ContainerAPIClient, streamStats bool, waitFirst *sync.WaitGroup) { //nolint:gocyclo
var getFirst bool
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package container
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package command
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package context
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package context
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package context
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package context
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package context
+1 -1
View File
@@ -13,7 +13,7 @@ import (
func newUseCommand(dockerCLI command.Cli) *cobra.Command {
cmd := &cobra.Command{
Use: "use CONTEXT",
Short: "Set the current docker context",
Short: "Set the default docker context",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
name := args[0]
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package command
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package command
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package command
+60 -18
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package formatter
@@ -21,13 +21,14 @@ import (
const (
defaultContainerTableFormat = "table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.RunningFor}}\t{{.Status}}\t{{.Ports}}\t{{.Names}}"
namesHeader = "NAMES"
commandHeader = "COMMAND"
runningForHeader = "CREATED"
mountsHeader = "MOUNTS"
localVolumes = "LOCAL VOLUMES"
networksHeader = "NETWORKS"
platformHeader = "PLATFORM"
namesHeader = "NAMES"
commandHeader = "COMMAND"
runningForHeader = "CREATED"
mountsHeader = "MOUNTS"
localVolumes = "LOCAL VOLUMES"
networksHeader = "NETWORKS"
platformHeader = "PLATFORM"
healthStatusHeader = "HEALTH STATUS"
)
// Platform wraps a [ocispec.Platform] to implement the stringer interface.
@@ -121,6 +122,7 @@ func NewContainerContext() *ContainerContext {
"LocalVolumes": localVolumes,
"Networks": networksHeader,
"Platform": platformHeader,
"HealthStatus": healthStatusHeader,
}
return &containerCtx
}
@@ -141,25 +143,36 @@ func (c *ContainerContext) ID() string {
// Names returns a comma-separated string of the container's names, with their
// slash (/) prefix stripped. Additional names for the container (related to the
// legacy `--link` feature) are omitted.
// legacy `--link` feature) are omitted when formatting "truncated".
func (c *ContainerContext) Names() string {
names := StripNamePrefix(c.c.Names)
if c.trunc {
for _, name := range names {
if len(strings.Split(name, "/")) == 1 {
names = []string{name}
break
var b strings.Builder
for i, n := range c.c.Names {
name := strings.TrimPrefix(n, "/")
if c.trunc {
// When printing truncated, we only print a single name.
//
// Pick the first name that's not a legacy link (does not have
// slashes inside the name itself (e.g., "/other-container/link")).
// Normally this would be the first name found.
if strings.IndexByte(name, '/') == -1 {
return name
}
continue
}
if i > 0 {
b.WriteByte(',')
}
b.WriteString(name)
}
return strings.Join(names, ",")
return b.String()
}
// StripNamePrefix removes prefix from string, typically container names as returned by `ContainersList` API
// StripNamePrefix removes any "/" prefix from container names returned
// by the "ContainersList" API.
func StripNamePrefix(ss []string) []string {
sss := make([]string, len(ss))
for i, s := range ss {
sss[i] = s[1:]
sss[i] = strings.TrimPrefix(s, "/")
}
return sss
}
@@ -341,6 +354,35 @@ func (c *ContainerContext) Networks() string {
return strings.Join(networks, ",")
}
// HealthStatus returns the container's health status (for example, "healthy","unhealthy", or "starting").
// If no healthcheck is configured, an empty
// string is returned.
func (c *ContainerContext) HealthStatus() string {
if c.c.Health != nil && c.c.Health.Status != "" {
return string(c.c.Health.Status)
}
// Fallback for API versions before v1.52, which include health only in Status text;
// see https://github.com/moby/moby/pull/50281
// see https://github.com/moby/moby/blob/docker-v29.4.3/daemon/container/health.go#L18-L43
_, health, ok := strings.Cut(c.c.Status, "(")
if !ok || !strings.HasSuffix(health, ")") {
return ""
}
health = strings.TrimSuffix(health, ")")
health = strings.TrimPrefix(health, "health: ")
switch container.HealthStatus(health) {
case container.Healthy, container.Unhealthy, container.Starting:
return health
case container.NoHealthcheck:
return ""
default:
return ""
}
}
// DisplayablePorts returns formatted string representing open ports of container
// e.g. "0.0.0.0:80->9090/tcp, 9988/tcp"
// it's used by command 'docker ps'
+5 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package formatter
@@ -494,6 +494,7 @@ func TestContainerContextWriteJSON(t *testing.T) {
{
"Command": `""`,
"CreatedAt": expectedCreated,
"HealthStatus": "",
"ID": "containerID1",
"Image": "ubuntu",
"Labels": "",
@@ -511,6 +512,7 @@ func TestContainerContextWriteJSON(t *testing.T) {
{
"Command": `""`,
"CreatedAt": expectedCreated,
"HealthStatus": "",
"ID": "containerID2",
"Image": "ubuntu",
"Labels": "",
@@ -528,6 +530,7 @@ func TestContainerContextWriteJSON(t *testing.T) {
{
"Command": `""`,
"CreatedAt": expectedCreated,
"HealthStatus": "",
"ID": "containerID3",
"Image": "ubuntu",
"Labels": "",
@@ -615,6 +618,7 @@ func TestContainerBackCompat(t *testing.T) {
{field: "Image", expected: "docker.io/library/ubuntu"},
{field: "Command", expected: `"/bin/sh"`},
{field: "CreatedAt", expected: time.Unix(createdAtTime.Unix(), 0).String()},
{field: "HealthStatus", expected: ""},
{field: "RunningFor", expected: "12 months ago"},
{field: "Ports", expected: "8080/tcp"},
{field: "Status", expected: "running"},
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package formatter
-3
View File
@@ -46,7 +46,6 @@ func (ctx *DiskUsageContext) startSubsection(format Format) (*template.Template,
ctx.buffer = &bytes.Buffer{}
ctx.header = ""
ctx.Format = format
ctx.preFormat()
return ctx.parseFormat()
}
@@ -88,7 +87,6 @@ func (ctx *DiskUsageContext) Write() (err error) {
return ctx.verboseWrite()
}
ctx.buffer = &bytes.Buffer{}
ctx.preFormat()
tmpl, err := ctx.parseFormat()
if err != nil {
@@ -213,7 +211,6 @@ func (ctx *DiskUsageContext) verboseWrite() error {
return ctx.verboseWriteTable(duc)
}
ctx.preFormat()
tmpl, err := ctx.parseFormat()
if err != nil {
return err
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package formatter
+43 -35
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package formatter
@@ -33,7 +33,7 @@ func (f Format) IsTable() bool {
return strings.HasPrefix(string(f), TableFormatKey)
}
// IsJSON returns true if the format is the json format
// IsJSON returns true if the format is the JSON format
func (f Format) IsJSON() bool {
return string(f) == JSONFormatKey
}
@@ -43,6 +43,31 @@ func (f Format) Contains(sub string) bool {
return strings.Contains(string(f), sub)
}
// templateString pre-processes the format and returns it as a string
// for templating.
func (f Format) templateString() string {
out := string(f)
switch out {
case TableFormatKey:
// A bare "--format table" should already be handled before we
// hit this; a literal "table" here means a custom "table" format
// without template.
return ""
case JSONFormatKey:
// "--format json" only; not JSON formats ("--format '{{json .Field}}'").
return JSONFormat
}
// "--format 'table {{.Field}}\t{{.Field}}'" -> "{{.Field}}\t{{.Field}}"
if after, isTable := strings.CutPrefix(out, TableFormatKey); isTable {
out = after
}
out = strings.Trim(out, " ") // trim spaces, but preserve other whitespace.
out = strings.NewReplacer(`\t`, "\t", `\n`, "\n").Replace(out)
return out
}
// Context contains information required by the formatter to print the output as desired.
type Context struct {
// Output is the output stream to which the formatted string is written.
@@ -53,28 +78,12 @@ type Context struct {
Trunc bool
// internal element
finalFormat string
header any
buffer *bytes.Buffer
}
func (c *Context) preFormat() {
c.finalFormat = string(c.Format)
// TODO: handle this in the Format type
switch {
case c.Format.IsTable():
c.finalFormat = c.finalFormat[len(TableFormatKey):]
case c.Format.IsJSON():
c.finalFormat = JSONFormat
}
c.finalFormat = strings.Trim(c.finalFormat, " ")
r := strings.NewReplacer(`\t`, "\t", `\n`, "\n")
c.finalFormat = r.Replace(c.finalFormat)
header any
buffer *bytes.Buffer
}
func (c *Context) parseFormat() (*template.Template, error) {
tmpl, err := templates.Parse(c.finalFormat)
tmpl, err := templates.Parse(c.Format.templateString())
if err != nil {
return nil, fmt.Errorf("template parsing error: %w", err)
}
@@ -82,20 +91,21 @@ func (c *Context) parseFormat() (*template.Template, error) {
}
func (c *Context) postFormat(tmpl *template.Template, subContext SubContext) {
if c.Output == nil {
c.Output = io.Discard
out := c.Output
if out == nil {
out = io.Discard
}
if c.Format.IsTable() {
t := tabwriter.NewWriter(c.Output, 10, 1, 3, ' ', 0)
buffer := bytes.NewBufferString("")
tmpl.Funcs(templates.HeaderFunctions).Execute(buffer, subContext.FullHeader())
buffer.WriteTo(t)
t.Write([]byte("\n"))
c.buffer.WriteTo(t)
t.Flush()
} else {
c.buffer.WriteTo(c.Output)
if !c.Format.IsTable() {
_, _ = c.buffer.WriteTo(out)
return
}
// Write column-headers and rows to the tab-writer buffer, then flush the output.
tw := tabwriter.NewWriter(out, 10, 1, 3, ' ', 0)
_ = tmpl.Funcs(templates.HeaderFunctions).Execute(tw, subContext.FullHeader())
_, _ = tw.Write([]byte{'\n'})
_, _ = c.buffer.WriteTo(tw)
_ = tw.Flush()
}
func (c *Context) contextFormat(tmpl *template.Template, subContext SubContext) error {
@@ -115,8 +125,6 @@ type SubFormat func(func(SubContext) error) error
// Write the template to the buffer using this Context
func (c *Context) Write(sub SubContext, f SubFormat) error {
c.buffer = &bytes.Buffer{}
c.preFormat()
tmpl, err := c.parseFormat()
if err != nil {
return err
+66 -11
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package formatter
@@ -8,20 +8,75 @@ import (
"testing"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestFormat(t *testing.T) {
f := Format("json")
assert.Assert(t, f.IsJSON())
assert.Assert(t, !f.IsTable())
tests := []struct {
doc string
f Format
isJSON bool
isTable bool
template string
}{
{
doc: "json format",
f: "json",
isJSON: true,
isTable: false,
template: JSONFormat,
},
{
doc: "empty table format (no template)",
f: "table",
isJSON: false,
isTable: true,
template: "",
},
{
doc: "table with escaped tabs",
f: "table {{.Field}}\\t{{.Field2}}",
isJSON: false,
isTable: true,
template: "{{.Field}}\t{{.Field2}}",
},
{
doc: "table with raw string",
f: `table {{.Field}}\t{{.Field2}}`,
isJSON: false,
isTable: true,
template: "{{.Field}}\t{{.Field2}}",
},
{
doc: "other format",
f: "other",
isJSON: false,
isTable: false,
template: "other",
},
{
doc: "other with spaces",
f: " other ",
isJSON: false,
isTable: false,
template: "other",
},
{
doc: "other with newline preserved",
f: " other\n ",
isJSON: false,
isTable: false,
template: "other\n",
},
}
f = Format("table")
assert.Assert(t, !f.IsJSON())
assert.Assert(t, f.IsTable())
f = Format("other")
assert.Assert(t, !f.IsJSON())
assert.Assert(t, !f.IsTable())
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
assert.Check(t, is.Equal(tc.f.IsJSON(), tc.isJSON))
assert.Check(t, is.Equal(tc.f.IsTable(), tc.isTable))
assert.Check(t, is.Equal(tc.f.templateString(), tc.template))
})
}
}
type fakeSubContext struct {
+2 -2
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package formatter
@@ -24,7 +24,7 @@ func MarshalJSON(x any) ([]byte, error) {
// marshalMap marshals x to map[string]any
func marshalMap(x any) (map[string]any, error) {
val := reflect.ValueOf(x)
if val.Kind() != reflect.Ptr {
if val.Kind() != reflect.Pointer {
return nil, fmt.Errorf("expected a pointer to a struct, got %v", val.Kind())
}
if val.IsNil() {
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package formatter
+5
View File
@@ -1,7 +1,11 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.25
package formatter
import (
"fmt"
"slices"
"strconv"
"strings"
@@ -104,6 +108,7 @@ func (c *volumeContext) Labels() string {
for k, v := range c.v.Labels {
joinLabels = append(joinLabels, k+"="+v)
}
slices.Sort(joinLabels)
return strings.Join(joinLabels, ",")
}
+2 -4
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package formatter
@@ -48,9 +48,7 @@ func TestVolumeContext(t *testing.T) {
for _, c := range cases {
ctx = c.volumeCtx
v := c.call()
if strings.Contains(v, ",") {
test.CompareMultipleValues(t, v, c.expValue)
} else if v != c.expValue {
if v != c.expValue {
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package idresolver
-44
View File
@@ -25,11 +25,6 @@ import (
"github.com/moby/patternmatcher"
)
// DefaultDockerfileName is the Default filename with Docker commands, read by docker build
//
// Deprecated: this const is no longer used and will be removed in the next release.
const DefaultDockerfileName string = "Dockerfile"
const (
// defaultDockerfileName is the Default filename with Docker commands, read by docker build
defaultDockerfileName string = "Dockerfile"
@@ -100,17 +95,6 @@ func filepathMatches(matcher *patternmatcher.PatternMatcher, file string) (bool,
return matcher.MatchesOrParentMatches(file)
}
// DetectArchiveReader detects whether the input stream is an archive or a
// Dockerfile and returns a buffered version of input, safe to consume in lieu
// of input. If an archive is detected, ok is set to true, and to false
// otherwise, in which case it is safe to assume input represents the contents
// of a Dockerfile.
//
// Deprecated: this utility was only used internally, and will be removed in the next release.
func DetectArchiveReader(input io.ReadCloser) (rc io.ReadCloser, ok bool, err error) {
return detectArchiveReader(input)
}
// detectArchiveReader detects whether the input stream is an archive or a
// Dockerfile and returns a buffered version of input, safe to consume in lieu
// of input. If an archive is detected, ok is set to true, and to false
@@ -127,15 +111,6 @@ func detectArchiveReader(input io.ReadCloser) (rc io.ReadCloser, ok bool, err er
return newReadCloserWrapper(buf, func() error { return input.Close() }), isArchive(magic), nil
}
// WriteTempDockerfile writes a Dockerfile stream to a temporary file with a
// name specified by defaultDockerfileName and returns the path to the
// temporary directory containing the Dockerfile.
//
// Deprecated: this utility was only used internally, and will be removed in the next release.
func WriteTempDockerfile(rc io.ReadCloser) (dockerfileDir string, err error) {
return writeTempDockerfile(rc)
}
// writeTempDockerfile writes a Dockerfile stream to a temporary file with a
// name specified by defaultDockerfileName and returns the path to the
// temporary directory containing the Dockerfile.
@@ -201,14 +176,6 @@ func GetContextFromReader(rc io.ReadCloser, dockerfileName string) (out io.ReadC
}), defaultDockerfileName, nil
}
// IsArchive checks for the magic bytes of a tar or any supported compression
// algorithm.
//
// Deprecated: this utility was used internally and will be removed in the next release.
func IsArchive(header []byte) bool {
return isArchive(header)
}
// isArchive checks for the magic bytes of a tar or any supported compression
// algorithm.
func isArchive(header []byte) bool {
@@ -305,17 +272,6 @@ func GetContextFromLocalDir(localDir, dockerfileName string) (string, string, er
return localDir, relDockerfile, err
}
// ResolveAndValidateContextPath uses the given context directory for a `docker build`
// and returns the absolute path to the context directory.
//
// Deprecated: this utility was used internally and will be removed in the next
// release. Use [DetectContextType] to detect the context-type, and use
// [GetContextFromLocalDir], [GetContextFromLocalDir], [GetContextFromGitURL],
// or [GetContextFromURL] instead.
func ResolveAndValidateContextPath(givenContextDir string) (string, error) {
return resolveAndValidateContextPath(givenContextDir)
}
// resolveAndValidateContextPath uses the given context directory for a `docker build`
// and returns the absolute path to the context directory.
func resolveAndValidateContextPath(givenContextDir string) (string, error) {
+2 -16
View File
@@ -68,7 +68,7 @@ func TestGetContextFromLocalDirWithNoDirectory(t *testing.T) {
contextDir := createTestTempDir(t)
createTestTempFile(t, contextDir, defaultDockerfileName, dockerfileContents)
chdir(t, contextDir)
t.Chdir(contextDir)
absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, "")
assert.NilError(t, err)
@@ -110,7 +110,7 @@ func TestGetContextFromLocalDirLocalFile(t *testing.T) {
func TestGetContextFromLocalDirWithCustomDockerfile(t *testing.T) {
contextDir := createTestTempDir(t)
chdir(t, contextDir)
t.Chdir(contextDir)
createTestTempFile(t, contextDir, defaultDockerfileName, dockerfileContents)
@@ -248,20 +248,6 @@ func createTestTempFile(t *testing.T, dir, filename, contents string) string {
return filePath
}
// chdir changes current working directory to dir.
// It returns a function which changes working directory back to the previous one.
// This function is meant to be executed as a deferred call.
// When an error occurs, it terminates the test.
func chdir(t *testing.T, dir string) {
t.Helper()
workingDirectory, err := os.Getwd()
assert.NilError(t, err)
assert.NilError(t, os.Chdir(dir))
t.Cleanup(func() {
assert.NilError(t, os.Chdir(workingDirectory))
})
}
func TestIsArchive(t *testing.T) {
tests := []struct {
doc string
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package image
+2 -2
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package image
@@ -211,6 +211,6 @@ func printAmbiguousHint(stdErr io.Writer, matchName string) {
"save",
"tag":
_, _ = fmt.Fprintf(stdErr, "\nNo images found matching %q: did you mean \"docker image %[1]s\"?\n", matchName)
_, _ = fmt.Fprintf(stdErr, "No images found matching %q: did you mean \"docker image %[1]s\"?\n", matchName)
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package image
@@ -1,3 +1 @@
WARNING: This output is designed for human readability. For machine-readable output, please use --format.
No images found matching "ls": did you mean "docker image ls"?
+25 -37
View File
@@ -1,19 +1,17 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package image
import (
"context"
"fmt"
"os"
"slices"
"strings"
"github.com/containerd/platforms"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/formatter"
"github.com/docker/cli/cli/streams"
"github.com/docker/cli/internal/tui"
"github.com/docker/go-units"
imagetypes "github.com/moby/moby/api/types/image"
@@ -206,37 +204,41 @@ var allChips = []imageChip{
chipInUse,
}
// getPossibleChips returns the list of chips used by at least one image.
// It is used to determine which columns to print (and how much width to
// reserve).
func getPossibleChips(view treeView) (chips []imageChip) {
remaining := make([]imageChip, len(allChips))
copy(remaining, allChips)
remaining := slices.Clone(allChips)
var possible []imageChip
for _, img := range view.images {
details := []imageDetails{img.Details}
for _, c := range img.Children {
details = append(details, c.Details)
check := func(d imageDetails) (done bool) {
// filter without allocating
out := remaining[:0]
for _, chip := range remaining {
if chip.check(&d) {
chips = append(chips, chip)
continue
}
out = append(out, chip)
}
remaining = out
return len(remaining) == 0
}
for _, d := range details {
for idx := len(remaining) - 1; idx >= 0; idx-- {
chip := remaining[idx]
if chip.check(&d) {
possible = append(possible, chip)
remaining = append(remaining[:idx], remaining[idx+1:]...)
}
for _, img := range view.images {
if check(img.Details) {
return chips
}
for _, c := range img.Children {
if check(c.Details) {
return chips
}
}
}
return possible
return chips
}
func printImageTree(outs command.Streams, view treeView) {
if streamRedirected(outs.Out()) {
_, _ = fmt.Fprintln(outs.Err(), "WARNING: This output is designed for human readability. For machine-readable output, please use --format.")
}
out := tui.NewOutput(outs.Out())
isTerm := out.IsTerminal()
@@ -561,17 +563,3 @@ func widestFirstColumnValue(headers []imgColumn, images []topImage) int {
}
return width
}
func streamRedirected(s *streams.Out) bool {
fd := s.FD()
if os.Stdout.Fd() != fd {
return true
}
fi, err := os.Stdout.Stat()
if err != nil {
return true
}
return fi.Mode()&os.ModeCharDevice == 0
}
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package inspect
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package manifest
+5
View File
@@ -1,6 +1,10 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.25
package network
import (
"slices"
"strconv"
"strings"
@@ -115,6 +119,7 @@ func (c *networkContext) Labels() string {
for k, v := range c.n.Labels {
joinLabels = append(joinLabels, k+"="+v)
}
slices.Sort(joinLabels)
return strings.Join(joinLabels, ",")
}
+2 -4
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package network
@@ -68,9 +68,7 @@ func TestNetworkContext(t *testing.T) {
for _, c := range cases {
ctx = c.networkCtx
v := c.call()
if strings.Contains(v, ",") {
test.CompareMultipleValues(t, v, c.expValue)
} else if v != c.expValue {
if v != c.expValue {
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package network
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package node
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package node
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package node
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package plugin
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package plugin
+1 -17
View File
@@ -194,8 +194,7 @@ func RetrieveAuthTokenFromImage(cfg *configfile.ConfigFile, image string) (strin
if err != nil {
return "", err
}
configKey := getAuthConfigKey(reference.Domain(registryRef))
authConfig, err := cfg.GetAuthConfig(configKey)
authConfig, err := cfg.GetAuthConfig(reference.Domain(registryRef))
if err != nil {
return "", err
}
@@ -211,18 +210,3 @@ func RetrieveAuthTokenFromImage(cfg *configfile.ConfigFile, image string) (strin
RegistryToken: authConfig.RegistryToken,
})
}
// 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.
//
// It is similar to [registry.GetAuthConfigKey], but does not require on
// [registrytypes.IndexInfo] as intermediate.
//
// [registry.GetAuthConfigKey]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/registry#GetAuthConfigKey
// [registrytypes.IndexInfo]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/api/types/registry#IndexInfo
func getAuthConfigKey(domainName string) string {
if domainName == "docker.io" || domainName == "index.docker.io" {
return authConfigKey
}
return domainName
}
+5
View File
@@ -1,7 +1,11 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.25
package secret
import (
"fmt"
"slices"
"strings"
"time"
@@ -109,6 +113,7 @@ func (c *secretContext) Labels() string {
for k, v := range mapLabels {
joinLabels = append(joinLabels, k+"="+v)
}
slices.Sort(joinLabels)
return strings.Join(joinLabels, ",")
}
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package secret
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package service
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package service
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package service
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
// Package genericresource is a local fork of SwarmKit's [genericresource] package,
// without protobuf dependencies.
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package service
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package progress
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package service
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package stack
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package system
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package system
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
package system
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
//go:build go1.25
// Package pruner registers "prune" functions to be included as part of
// "docker system prune".

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