Compare commits

..
234 Commits
Author SHA1 Message Date
Sebastiaan van StijnandGitHub d072fe407f Merge pull request #7234 from vvoland/update-dco
docs: Update DCO identity guidance
2026-08-24 12:16:16 +02:00
Paweł Gronowski 3fe73079aa docs: Clarify DCO identity guidance
The DCO policy used here appears to have been derived from the Linux
kernel's guidance. In 2023, that guidance changed to relax the "real
name" requirement to a "known identity".

The DCO sign-off identifies the person certifying that they have the
right to submit a contribution. The previous wording required a "real
name" and rejected pseudonyms, which could discourage contributors
whose preferred or community name is the identity by which they can be
known.

The requirement is not anonymity: the identity in the sign-off must be
known enough for the project to follow up about the contribution if a
question arises. It does not require a legal name or a government-issued
identity. Use the clarified wording from the Linux kernel so known
pseudonyms and preferred names remain acceptable while anonymous
contributions remain excluded.

This also reflects concerns raised in the CNCF community about whether
"real name" means a legal name, a government-issued identity, or a
public identity that is sufficient to identify and contact a
contributor. The discussion emphasized that attribution and future
accountability do not require publishing a legal identity, and that
strict public-name rules can exclude people who cannot safely contribute
under that name.

Source: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=d4563201f33a022fc035

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-08-24 11:41:24 +02:00
Paweł GronowskiandGitHub 28f756087e Merge pull request #7224 from vvoland/update-go
update to go1.26.7
2026-08-20 12:38:49 +02:00
Paweł Gronowski 387b8e2af8 update to go1.26.7
This release includes a fix to address a breakage affecting unencrypted
HTTP/2 (h2c) connections caused by a security patch included in last
patch release.

See go.dev/issue/80876 for details.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-08-19 20:23:24 +02:00
Paweł GronowskiandGitHub 55e4a7eef5 Merge pull request #7222 from thaJeztah/bump_logrus
vendor: github.com/sirupsen/logrus v1.10.1
2026-08-19 12:34:56 +02:00
Sebastiaan van Stijn 58d9cdd2d9 vendor: github.com/sirupsen/logrus v1.10.1
Notable changes:

- Add bidirectional `log/slog` integration with a Logrus hook and `slog.Handler`.
- Add minimal, composable logging interfaces for individual log levels.
- Fix reentrant logging deadlocks and improve concurrency safety around formatters, hooks, and entries.
- Fix generic `Log`, `Logf`, `Logln`, and `LogFn` methods unexpectedly panicking at `PanicLevel`.
- Allow `Entry.Caller` to be set explicitly and preserved across derived entries.
- Improve `TextFormatter` performance and reduce allocations significantly.
- Improve common Logger and Entry hot paths and caller-reporting performance.
- Update `TextFormatter` handling for `[]byte`, debug/trace colors, and Windows ANSI terminals.
- Raise the minimum supported Go version to Go 1.23.
- Deprecate `Entry.HasCaller` and `MutexWrap`.

release-notes: https://github.com/sirupsen/logrus/releases/tag/v1.10.0
release-notes: https://github.com/sirupsen/logrus/releases/tag/v1.10.1
full diff: https://github.com/sirupsen/logrus/compare/v1.9.4...v1.10.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-08-19 11:34:53 +02:00
Sebastiaan van StijnandGitHub 8659aa5d4c Merge pull request #7185 from thaJeztah/duplicate_normalize
cli/command/manifest: remove redundant normalization
2026-08-14 18:42:04 +02:00
Paweł GronowskiandGitHub 8d08c75e81 Merge pull request #7191 from thaJeztah/bump_go
update to go1.26.6
2026-08-14 14:52:56 +02:00
Sebastiaan van Stijn 24af004cbf update to go1.26.6
This release includes 10 security fixes following the security policy:

- x/mod/sumdb/tlog: fix transparency log tile verification bypass

    A malicious GOPROXY was previously capable of forging
    up to two sumdb tiles that allow for a requested module
    to bypass the GOSUMDB check and persist attacker-controlled
    module content to a local Go module cache.

    This attack allows for a malicious GOPROXY to serve
    malicious module content that cannot be detected
    by evaluating the transparency log.

    All tiles are now correctly verified against their parents.

    In order to determine if you have been affected:

    rm -r go.sum go.work.sum vendor/ && go mod tidy

    Thanks to Filippo Valsorda (Geomys) for reporting this issue.

    This is CVE-2026-56865 and Go issue https://go.dev/issue/80744.

- x/mod/sumdb: ignore unrelated, unauthenticated hashes in Lookup

    A malicious GOSUMDB was capable of serving arbitrary
    module content not contained within the transparency
    log.

    This attack allows for a coordinating GOPROXY and
    GOSUMDB to serve a client malicious module content
    that cannot be detected by evaluating the transparency
    log.

    In order to determine if you have been affected:

    rm -r go.sum go.work.sum vendor/ && go mod tidy

    Thanks to mundur for reporting this issue.

    This is CVE-2026-56864 and Go issue https://go.dev/issue/80745.

- encoding/xml: add recursion depth guard during decode

    Previously, DecodeElement would reset the depth counter
    causing it to never fire; this could lead to stack
    exhaustion.

    This is CVE-2026-56859 and Go issue https://go.dev/issue/80481.

 net/http: apply ReadHeaderTimeout when doing unencrypted HTTP/2 check

    When a server is configured to support unencrypted HTTP/2, it reads a
    few bytes from each new connection to see if they contain the HTTP/2
    client preface. Previously, this was being done with no timeout applied.
    ReadHeaderTimeout is now applied for this.

    This is CVE-2026-56853 and Go issue https://go.dev/issue/80205.

- net/url: avoid quadratic complexity in resolvePath

    Previously, resolving relative paths containing parent directory (..) segments performed string conversions and buffer rewrites on each step, resulting in quadratic time complexity and high memory allocation overhead.

    Now, path resolution operates on a byte buffer using index-based backtracking for .. segments, eliminating the quadratic time complexity and significantly reducing memory allocations.

    This is CVE-2026-56860 and Go issue https://go.dev/issue/80494.

- golang.org/x/net/dns/dnsmessage: panic when parsing invalid SVCB record

    Parsing an invalid SVCB or HTTPS RR can panic when
    the size of a parameter value overflows the message buffer.

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

    This is CVE-2026-46600 and Go issue https://go.dev/issue/79795.

- crypto/tls: limit handshake messages we are willing to accept post-handshake

    Previously, we always counted handshake messages, such as KeyUpdate, as
    state-advancing, regardless of whether a handshake has been completed or
    not. As a result, a malicious client can keep sending KeyUpdate messages
    to force the server to keep performing key derivation operations
    indefinitely.

    Thanks to Qi Deng of Aurascape.ai for reporting this issue.

    This is CVE-2026-56862 and Go issue https://go.dev/issue/80528.

- html/template: fix Javascript regexp context tracking

    Previously, pathological inputs could close an
    unescaped / early, allowing for attack-controlled
    data to inject arbitrary content, potentially
    leading to XSS.

    Thanks to Ali Sherif for reporting this issue.

    This is CVE-2026-56858 and Go issue https://go.dev/issue/80435.

- x/net/idna: failure to reject ASCII-only Punycode-encoded labels

    The ToASCII and ToUnicode functions incorrectly accepted Punycode-encoded labels
    that decode to an ASCII-only label. For example, ToUnicode("xn--example-.com")
    incorrectly returned the name "example.com" rather than an error.

    The idna package implements the processing algorithm from UTS 46.
    Older versions of UTS 46 included a specification bug which permitted
    multiple ASCII labels to decode to the same Unicode label.
    UTS 46 revision 33 fixed the specification bug.
    The idna package now implements the updated specification.

    This behavior can lead to privilege escalation in programs using the idna package.
    For example, a program which performs privilege checks on the ASCII hostname
    may reject "example.com" but permit "xn--example-.com". If that program subsequently
    converts the ASCII hostname to Unicode, it will inadvertently permits access
    to the Unicode name "example.com".

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

    This is CVE-2026-39821 and Go issue https://go.dev/issue/78760.

- encoding/asn1: enforce maximum recursion depth

    Enforce a recursion limit in Unmarshal to prevent stack exhaustion
    when parsing deeply-nested, recursive structures.

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

    This is CVE-2026-33818 and Go issue https://go.dev/issue/80405.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-08-14 14:02:40 +02:00
Sebastiaan van StijnandGitHub eb68ed19d2 Merge pull request #7157 from docker/dependabot/github_actions/codeql-actions-8417568871
build(deps): bump the codeql-actions group across 1 directory with 3 updates
2026-08-13 22:42:40 +02:00
dependabot[bot]andGitHub 8a375034e2 build(deps): bump the codeql-actions group across 1 directory with 3 updates
Bumps the codeql-actions group with 3 updates in the / directory: [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/autobuild](https://github.com/github/codeql-action) and [github/codeql-action/analyze](https://github.com/github/codeql-action).


Updates `github/codeql-action/init` from 4.37.3 to 4.37.6
- [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/e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81...5595ccaf912efad79be6eef63a5619ff05969be3)

Updates `github/codeql-action/autobuild` from 4.37.3 to 4.37.6
- [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/e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81...5595ccaf912efad79be6eef63a5619ff05969be3)

Updates `github/codeql-action/analyze` from 4.37.3 to 4.37.6
- [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/e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81...5595ccaf912efad79be6eef63a5619ff05969be3)

---
updated-dependencies:
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.37.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-actions
- dependency-name: github/codeql-action/autobuild
  dependency-version: 4.37.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-actions
- dependency-name: github/codeql-action/init
  dependency-version: 4.37.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-13 08:46:20 +00:00
Sebastiaan van Stijn a7c0741fa7 cli/command/manifest: remove redundant normalization
commit 55fcffe743 added normalization
in config/configfile, so it's no longer needed to normalize here as
well.

updates 55fcffe743

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-08-12 16:38:26 +02:00
Sebastiaan van StijnandGitHub 12892075d2 Merge pull request #7180 from docker/dependabot/github_actions/docker-actions-ebe691cc85
build(deps): bump docker/github-builder/.github/workflows/bake.yml from 1.15.0 to 1.16.0 in the docker-actions group
2026-08-12 15:37:56 +02:00
dependabot[bot]andGitHub e1f9b284df build(deps): bump docker/github-builder/.github/workflows/bake.yml
Bumps the docker-actions group with 1 update: [docker/github-builder/.github/workflows/bake.yml](https://github.com/docker/github-builder).


Updates `docker/github-builder/.github/workflows/bake.yml` from 1.15.0 to 1.16.0
- [Release notes](https://github.com/docker/github-builder/releases)
- [Commits](https://github.com/docker/github-builder/compare/27ade872c1e2296e62ef15ab3b10d37665e57cf7...a492c6d04fd3315f67230809b44d60cc0acd50b3)

---
updated-dependencies:
- dependency-name: docker/github-builder/.github/workflows/bake.yml
  dependency-version: 1.16.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: docker-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-12 08:43:00 +00:00
Sebastiaan van StijnandGitHub 4f84911bfe Merge pull request #7144 from hirehamir/fix/ps-port-sort
cli/command/formatter: sort published ports numerically by IP
2026-08-06 15:50:28 +02:00
Paweł GronowskiandGitHub 228f29e810 Merge pull request #7159 from thaJeztah/version_29.8
VERSION: 29.8.0
2026-08-06 15:47:41 +02:00
Sebastiaan van Stijn d8c3543407 VERSION: 29.8.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-08-06 15:34:41 +02:00
Hamir 2dd7b7e15b cli/command/formatter: sort published ports numerically by IP
`comparePorts` compared host IPs with `i.IP.String() < j.IP.String()`, which
sorts them lexicographically, so "10.0.0.2" ordered before "9.0.0.1" in
`docker ps` output.

This also avoids formatting both addresses as strings on every comparison.

ports    before               after
64       1540 allocs/op       394 allocs/op
256      5364 allocs/op       1548 allocs/op

That's a ~74% (3.9x) reduction in allocations.

Signed-off-by: Hamir <hirehamir@gmail.com>
2026-08-05 11:38:20 -07:00
Paweł GronowskiandGitHub a7dcaa6fdb Merge pull request #7151 from vvoland/update-archive
vendor: github.com/moby/go-archive v0.3.3
2026-08-05 19:34:15 +02:00
Paweł Gronowski 400b45f682 vendor: github.com/moby/go-archive v0.3.3
full diff: https://github.com/moby/go-archive/compare/v0.3.2...v0.3.3

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-08-05 19:11:05 +02:00
Sebastiaan van StijnandGitHub 38887ec4ed Merge pull request #7145 from winklemad/fix-service-duplicate-removal-panic
cli/command/service: fix panic when removing duplicate values
2026-08-05 19:10:29 +02:00
Sebastiaan van StijnandGitHub 904aef7f69 Merge pull request #7147 from docker/dependabot/github_actions/docker-actions-269857e486
build(deps): bump docker/docker-agent-action/.github/workflows/review-pr.yml from 2.0.2 to 2.0.3 in the docker-actions group across 1 directory
2026-08-04 13:11:45 +02:00
dependabot[bot]andGitHub f08e60eb09 build(deps): bump docker/docker-agent-action/.github/workflows/review-pr.yml
Bumps the docker-actions group with 1 update in the / directory: [docker/docker-agent-action/.github/workflows/review-pr.yml](https://github.com/docker/docker-agent-action).


Updates `docker/docker-agent-action/.github/workflows/review-pr.yml` from 2.0.2 to 2.0.3
- [Release notes](https://github.com/docker/docker-agent-action/releases)
- [Commits](https://github.com/docker/docker-agent-action/compare/774b6e0e60d6c648b0f2dc43bd5221377a0a7420...baf90543d81f5de59751dfd10e6cf45e21a5a982)

---
updated-dependencies:
- dependency-name: docker/docker-agent-action/.github/workflows/review-pr.yml
  dependency-version: 2.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: docker-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-04 08:42:59 +00:00
Paweł GronowskiandGitHub abfd89157d Merge pull request #7149 from vvoland/update-docker
VERSION: 29.7.2
2026-08-03 18:24:10 +02:00
Paweł Gronowski 519eb45d03 VERSION: 29.7.2
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-08-03 18:18:56 +02:00
Madan Kumar 8717af7168 cli/command/service: fix panic when removing duplicate values
Both makeEnv and updateHosts removed elements from a slice while ranging
over that same slice. The range expression is evaluated once, so after a
removal the loop keeps using the original length: it reads stale elements
that shifted down, skips a live element, and can slice past the end of the
shrunken slice, which panics.

In makeEnv, the "no update required" continue also applied to the inner
loop instead of skipping the re-append, so an env-var passed twice with
the same value was stored twice. A third occurrence with a different value
then tried to remove both entries and panicked:

    docker service create --env A=1 --env A=1 --env A=2 --name repro nginx
    panic: runtime error: slice bounds out of range [2:1]

The same happens with an env-file that lists a variable twice, and the
panic occurs before any API call, so no daemon is needed to hit it.

updateHosts has the same problem when a hostname is listed more than once
in a single entry: --host-rm either leaves a copy behind or panics with
"slice bounds out of range". That needs a spec written through the API or
swarmkit directly, as the CLI does not produce such entries itself, so it
is less likely to be hit in practice.

Use slices.DeleteFunc for both, which removes every match in one pass, and
add tests for makeEnv, which had no coverage.

Signed-off-by: Madan Kumar <winklemad@outlook.com>
2026-08-02 07:13:16 +05:30
Paweł GronowskiandGitHub e9452d6e78 Merge pull request #7142 from thaJeztah/bump_go_archive_0.3.2
vendor: github.com/moby/go-archive v0.3.2
2026-07-31 19:03:37 +02:00
Sebastiaan van Stijn a6014a702b vendor: github.com/moby/go-archive v0.3.2
full diff: https://github.com/moby/go-archive/compare/v0.3.1...v0.3.2

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-31 18:53:47 +02:00
Paweł GronowskiandGitHub 2465fca604 Merge pull request #7140 from thaJeztah/bump_go_archive_0.3.1
vendor: github.com/moby/go-archive v0.3.1
2026-07-31 18:33:04 +02:00
Sebastiaan van StijnandGitHub 52b15cc571 Merge pull request #7141 from thaJeztah/bump_version2
update version to 29.7.1
2026-07-31 18:20:09 +02:00
Sebastiaan van Stijn 13590921a4 update version to 29.7.1
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-31 18:11:45 +02:00
Sebastiaan van Stijn 0b50545471 vendor: github.com/moby/go-archive v0.3.1
full diff: https://github.com/moby/go-archive/compare/v0.3.0...v0.3.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-31 18:10:04 +02:00
Paweł GronowskiandGitHub c1eba931e3 Merge pull request #7086 from mickael-docker/docs-authz-decoding
docs(authz): clarify daemon parsing semantics
2026-07-30 22:16:32 +02:00
Paweł GronowskiandGitHub 1a305be376 Merge pull request #7084 from thaJeztah/prompt_cleans
cli/command: PromptUserForCredentials: don't mutate cli
2026-07-30 22:15:34 +02:00
Sebastiaan van StijnandGitHub 4c1c648df1 Merge pull request #7129 from docker/dependabot/github_actions/codeql-actions-f42a4cd18a
build(deps): bump the codeql-actions group across 1 directory with 3 updates
2026-07-30 21:56:12 +02:00
Sebastiaan van StijnandGitHub ee046d35c0 Merge pull request #7134 from hirehamir/fix/flaky-client-hangs
cli/command: fix flaky TestInitializeFromClientHangs
2026-07-30 21:54:54 +02:00
Sebastiaan van StijnandGitHub d70c3fd5e7 Merge pull request #7139 from thaJeztah/vendor_go_archive
vendor: github.com/moby/go-archive v0.3.0
2026-07-30 21:49:42 +02:00
Sebastiaan van Stijn f6d6bede46 vendor: github.com/moby/go-archive v0.3.0
full diff: https://github.com/moby/go-archive/compare/v0.2.1...v0.3.0

v0.3.0

This release fixes CVE-2026-17106 / GHSA-hfg8-hc9c-6c3h, where a crafted
tar archive could use links to cause extraction operations to create or
overwrite files outside the intended destination directory.

The issue affected Unpack, UnpackLayer, Untar, UntarUncompressed, and the
ApplyLayer helpers. Users should upgrade and avoid extracting untrusted
archives with earlier versions.

What's Changed

* archive: harden tar extraction against path traversal
* archive: do not follow reparse points in chtimes
* archive: fix creation time updates on Windows
* archive: minor cleanups and godoc touch-up
* archive: RebaseArchiveEntries: fix archive path rebasing

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-30 19:17:27 +02:00
Sebastiaan van StijnandGitHub b47659808a Merge pull request #7026 from vvoland/fix-hostname
cli/file_store: Go 1.26 compatibility
2026-07-30 15:51:46 +02:00
dependabot[bot]andGitHub af626e1ed7 build(deps): bump the codeql-actions group across 1 directory with 3 updates
Bumps the codeql-actions group with 3 updates in the / directory: [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/autobuild](https://github.com/github/codeql-action) and [github/codeql-action/analyze](https://github.com/github/codeql-action).


Updates `github/codeql-action/init` from 4.37.1 to 4.37.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/7188fc363630916deb702c7fdcf4e481b751f97a...e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81)

Updates `github/codeql-action/autobuild` from 4.37.1 to 4.37.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/7188fc363630916deb702c7fdcf4e481b751f97a...e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81)

Updates `github/codeql-action/analyze` from 4.37.1 to 4.37.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/7188fc363630916deb702c7fdcf4e481b751f97a...e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81)

---
updated-dependencies:
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.37.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-actions
- dependency-name: github/codeql-action/autobuild
  dependency-version: 4.37.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-actions
- dependency-name: github/codeql-action/init
  dependency-version: 4.37.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-30 08:46:12 +00:00
Sebastiaan van StijnandGitHub 6d35de4601 Merge pull request #7137 from crazy-max/bin-image-github-builder
ci: use docker github builder to build bin image
2026-07-29 16:58:14 +02:00
CrazyMaxandCrazyMax ad8dce1012 ci: use docker github builder to build bin image
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2026-07-29 16:32:49 +02:00
Hamir 844210bd0f cli/command: fix flaky TestInitializeFromClientHangs
Signed-off-by: Hamir <hirehamir@gmail.com>
2026-07-28 15:06:53 -07:00
Paweł GronowskiandGitHub bd719d6703 Merge pull request #7131 from vvoland/build-oidc
gha/build: Use OIDC for Docker Hub login
2026-07-28 17:10:00 +02:00
Paweł Gronowski 52b1160c85 gha: bump docker/login-action from 4.4.0 to 4.5.0
OIDC login requires docker/login-action v4.5.0 or newer

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-07-28 15:38:23 +02:00
Paweł Gronowski 5ff2ff6311 gha/build: Use OIDC for Docker Hub login
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-07-28 14:28:40 +02:00
Paweł GronowskiandGitHub 775dd50364 Merge pull request #7111 from thaJeztah/bump_go_archive
vendor: github.com/moby/go-archive main / v0.3.0-dev
2026-07-27 22:13:35 +02:00
Paweł GronowskiandGitHub 3320996718 Merge pull request #7122 from thaJeztah/bump_x_deps
vendor: update golang.org/x/* dependencies
2026-07-27 22:13:17 +02:00
Sebastiaan van StijnandGitHub 4b02fd04b0 Merge pull request #7124 from JessThrysoee/completion-swarm-filters
completion: add completion for swarm "--filter" flags
2026-07-27 21:30:07 +02:00
Sebastiaan van Stijn 487686142c vendor: golang.org/x/net v0.57.0
Relevant changes (in vendor):

- bpf: add security considerations to package docs
- http2: initialize Transport on NewClientConn
  fixes: x/net/http2: zero Transport not ready for use
- idna: reject all-ASCII xn-- labels on all Go versions
  fixes x/net/idna: ToUnicode accepts Punycode labels encoding pure ASCII labels

full diff: https://github.com/golang/net/compare/v0.56.0...v0.57.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-27 21:28:05 +02:00
Sebastiaan van Stijn e03b83ef06 vendor: golang.org/x/text v0.40.0
- unicode/norm: avoid infinite loop on invalid input

full diff: https://github.com/golang/text/compare/v0.38.0...v0.40.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-27 21:28:05 +02:00
Sebastiaan van Stijn 8dfca49e6b vendor: golang.org/x/mod v0.38.0
full diff: https://github.com/golang/mod/compare/v0.37.0...v0.38.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-27 21:28:05 +02:00
Sebastiaan van Stijn 808405b67d vendor: golang.org/x/term v0.45.0
full diff: https://github.com/golang/term/compare/v0.44.0...v0.45.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-27 21:28:05 +02:00
Sebastiaan van Stijn 3f5a7b50ab vendor: golang.org/x/sys v0.47.0
- cpu: handle vendor suffixes in parseRelease
- unix: update glibc to 2.43
- unix: use epoll_pwait rather than epoll_wait
- windows: avoid length overflow in NewNTString
- windows: document safe usage of TrusteeValue

full diff: https://github.com/golang/sys/compare/v0.46.0...v0.47.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-27 21:28:05 +02:00
Sebastiaan van Stijn 40c63db4c8 vendor: golang.org/x/sync v0.22.0
semaphore: panic on negative weights

full diff: https://github.com/golang/sync/compare/v0.21.0...v0.22.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-27 21:28:03 +02:00
Sebastiaan van StijnandGitHub 977f88900e Merge pull request #7121 from thaJeztah/bump_go_connections
vendor: github.com/docker/go-connections v0.8.0
2026-07-27 21:26:50 +02:00
Sebastiaan van Stijn f6dfb40875 vendor: github.com/moby/go-archive main / v0.3.0-dev
full diff: https://github.com/moby/go-archive/compare/v0.2.0...2ff9bfb8b2ee

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-27 21:24:52 +02:00
Sebastiaan van StijnandGitHub a9ac44ea6b Merge pull request #7128 from vvoland/update-docker
vendor: github.com/moby/moby/client v0.5.1
2026-07-27 21:21:13 +02:00
Sebastiaan van StijnandGitHub 358f0135e3 Merge pull request #7127 from vvoland/win-unix
docs: Document Unix socket support on Windows
2026-07-27 21:20:26 +02:00
Paweł Gronowski aa610f321d vendor: github.com/moby/moby/client v0.5.1
full diff: https://github.com/moby/moby/compare/client/v0.5.0...client/v0.5.1

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-07-27 21:15:05 +02:00
Paweł Gronowski 0d32eed829 docs: Document Unix socket support on Windows
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-07-27 20:29:50 +02:00
Paweł GronowskiandGitHub be7865c09e Merge pull request #7123 from thaJeztah/bump_grpc
vendor: google.golang.org/grpc v1.82.1
2026-07-27 17:00:54 +02:00
Paweł GronowskiandGitHub 186b7283e1 Merge pull request #7126 from thaJeztah/bump_yaml
vendor: go.yaml.in/yaml/v3 v3.0.5
2026-07-27 16:59:57 +02:00
Sebastiaan van Stijn 9f4301e8f5 vendor: go.yaml.in/yaml/v3 v3.0.5
removes transitive dependencies on gopkg.in/check.v1

full diff: https://github.com/yaml/go-yaml/compare/v3.0.4...v3.0.5

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-27 14:43:56 +02:00
Jess Thrysoee 89e882a808 completion: add completion for swarm "--filter" flags
The migration to the generated (V2) shell completions dropped the
"--filter" key/value suggestions that the legacy bash completion used to
provide for the swarm commands, so e.g. "docker service ps <service>
--filter <TAB>" no longer completed anything.

Register filter completions for "docker service ps", "docker service
ls", and "docker node ps", following the same approach as the existing
completion for "docker events --filter".

Signed-off-by: Jess Thrysoee <jess@thrysoee.dk>
2026-07-26 15:52:58 +02:00
Sebastiaan van Stijn 4e6e8fe5c8 vendor: github.com/docker/go-connections v0.8.0
- sockets: set socket permissions without overriding umask
- sockets: improve abstract Unix socket handling
- sockets: InmemSocket: add DialContext
- sockets: remove double error decoration
- sockets: test-enhancements and improve coverage

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

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-25 19:47:59 +02:00
Sebastiaan van StijnandGitHub 5b21d378b0 Merge pull request #7118 from docker/dependabot/github_actions/codeql-actions-15f4d34eb1
build(deps): bump the codeql-actions group with 3 updates
2026-07-25 04:05:04 +02:00
Sebastiaan van Stijn 532dcc37f5 vendor: google.golang.org/grpc v1.82.1
Fixes xDS RBAC and HTTP/2 Vulnerabilities: GHSA-hrxh-6v49-42gf

full diff: https://github.com/grpc/grpc-go/compare/v1.81.1...v1.82.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-25 03:59:22 +02:00
Sebastiaan van StijnandGitHub 19a0ee3926 Merge pull request #7113 from nikolauspschuetz/test-device-opts-validators
opts: add tests for weight/throttle device opts validators
2026-07-25 02:16:11 +02:00
Sebastiaan van StijnandGitHub c700ac42b6 Merge pull request #7103 from hirehamir/fix/stray-pipe-in-docker-create-help-message
fix: stray `|` pipe in `docker create --help`
2026-07-25 02:01:52 +02:00
Sebastiaan van StijnandGitHub 03b45495db Merge pull request #7116 from docker/dependabot/github_actions/actions/setup-go-7.0.0
build(deps): bump actions/setup-go from 6.5.0 to 7.0.0
2026-07-25 01:51:41 +02:00
Sebastiaan van StijnandGitHub 8061c90dcd Merge pull request #7110 from HajimohammadiNet/fix-container-rm-force-output
container rm: suppress forced not found errors
2026-07-25 01:50:41 +02:00
Sebastiaan van StijnandGitHub 80698fa987 Merge pull request #7120 from docker/dependabot/github_actions/actions/checkout-7.0.1
build(deps): bump actions/checkout from 7.0.0 to 7.0.1
2026-07-25 01:41:59 +02:00
AmirHossein HajiMohammadi ee4c249cc2 container rm: suppress forced not found errors
Signed-off-by: AmirHossein HajiMohammadi <a.hajimohammadi@rahkar.team>
2026-07-24 17:38:57 +03:30
Sebastiaan van StijnandGitHub 9391c9889c Merge pull request #7119 from thaJeztah/bump_go_archive_0.2.1
vendor: github.com/moby/go-archive v0.2.1
2026-07-24 12:11:36 +02:00
dependabot[bot]andGitHub 90d14d3e7e build(deps): bump actions/checkout from 7.0.0 to 7.0.1
Bumps [actions/checkout](https://github.com/actions/checkout) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0...3d3c42e5aac5ba805825da76410c181273ba90b1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-24 08:43:24 +00:00
Sebastiaan van Stijn 349fad1635 vendor: github.com/moby/go-archive v0.2.1
full diff: https://github.com/moby/go-archive/compare/v0.2.0...v0.2.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-23 23:05:30 +02:00
dependabot[bot]andGitHub a3ea9a43e0 build(deps): bump the codeql-actions group with 3 updates
Bumps the codeql-actions group with 3 updates: [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/autobuild](https://github.com/github/codeql-action) and [github/codeql-action/analyze](https://github.com/github/codeql-action).


Updates `github/codeql-action/init` from 4.37.0 to 4.37.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/99df26d4f13ea111d4ec1a7dddef6063f76b97e9...7188fc363630916deb702c7fdcf4e481b751f97a)

Updates `github/codeql-action/autobuild` from 4.37.0 to 4.37.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/99df26d4f13ea111d4ec1a7dddef6063f76b97e9...7188fc363630916deb702c7fdcf4e481b751f97a)

Updates `github/codeql-action/analyze` from 4.37.0 to 4.37.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/99df26d4f13ea111d4ec1a7dddef6063f76b97e9...7188fc363630916deb702c7fdcf4e481b751f97a)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.37.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-actions
- dependency-name: github/codeql-action/autobuild
  dependency-version: 4.37.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-actions
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.37.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-23 08:46:14 +00:00
dependabot[bot]andGitHub fd78fdc62d build(deps): bump actions/setup-go from 6.5.0 to 7.0.0
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 6.5.0 to 7.0.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/924ae3a1cded613372ab5595356fb5720e22ba16...b7ad1dad31e06c5925ef5d2fc7ad053ef454303e)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-22 08:46:48 +00:00
Nikolaus Schuetz 8f3477fca1 Add tests for weight/throttle device opts validators
opts/weightdevice.go and opts/throttledevice.go had no tests, unlike the
sibling opts. Add table-driven coverage for ValidateWeightDevice,
ValidateThrottleBpsDevice, ValidateThrottleIOpsDevice and the Set/GetList
paths, including boundary (weight 0/10/1000/overflow) and error cases.

Signed-off-by: Nikolaus Schuetz <nikolauspschuetz@gmail.com>
2026-07-20 13:47:09 -07:00
Paweł GronowskiandGitHub 7a54334eb0 Merge pull request #7082 from thaJeztah/prompt_cleans_step1
internal/prompt: Confirm: don't wrap stdIn
2026-07-20 17:29:42 +02:00
Sebastiaan van StijnandGitHub 617d772fcc Merge pull request #7112 from vvoland/makesocket
docker.Makefile: Use active context socket
2026-07-17 17:52:07 +02:00
Paweł Gronowski 7aef4c8479 docker.Makefile: Use active context socket
The development and e2e containers hard-code /var/run/docker.sock, so
rootless and other local contexts cannot expose their daemon socket.
Resolve the active context's Docker endpoint and strip the unix scheme
before using it as the bind source.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-07-17 15:33:12 +02:00
Paweł GronowskiandGitHub dc997612d0 Merge pull request #7007 from lohitkolluri/e2e/private-registry-pull-push-5965
e2e: add private registry pull/push regression test
2026-07-16 14:18:00 +02:00
Sebastiaan van StijnandGitHub 7ea9e2158c Merge pull request #7107 from docker/dependabot/github_actions/codeql-actions-e021b9f00e
build(deps): bump the codeql-actions group with 3 updates
2026-07-16 02:01:21 +02:00
dependabot[bot]andGitHub 3519704227 build(deps): bump the codeql-actions group with 3 updates
Bumps the codeql-actions group with 3 updates: [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/autobuild](https://github.com/github/codeql-action) and [github/codeql-action/analyze](https://github.com/github/codeql-action).


Updates `github/codeql-action/init` from 4.36.3 to 4.37.0
- [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/54f647b7e1bb85c95cddabcd46b0c578ec92bc1a...99df26d4f13ea111d4ec1a7dddef6063f76b97e9)

Updates `github/codeql-action/autobuild` from 4.36.3 to 4.37.0
- [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/54f647b7e1bb85c95cddabcd46b0c578ec92bc1a...99df26d4f13ea111d4ec1a7dddef6063f76b97e9)

Updates `github/codeql-action/analyze` from 4.36.3 to 4.37.0
- [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/54f647b7e1bb85c95cddabcd46b0c578ec92bc1a...99df26d4f13ea111d4ec1a7dddef6063f76b97e9)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: codeql-actions
- dependency-name: github/codeql-action/autobuild
  dependency-version: 4.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: codeql-actions
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: codeql-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-15 08:46:00 +00:00
Hamir 0133bfa4fd fix: stray | pipe in docker create --help
Signed-off-by: Hamir <hirehamir@gmail.com>
2026-07-10 17:38:18 -07:00
Sebastiaan van StijnandGitHub 09754bc280 Merge pull request #7100 from docker/dependabot/github_actions/docker-actions-4dfd9f9ba8
build(deps): bump docker/login-action from 4.3.0 to 4.4.0 in the docker-actions group
2026-07-10 19:47:32 +02:00
Paweł GronowskiandGitHub 48370883e0 Merge pull request #7102 from vvoland/sync-master
gha/sync-release-branch: Use actions write permission
2026-07-10 19:35:02 +02:00
Paweł Gronowski 18d4f4ff6d gha/sync-release-branch: Use actions write permission
`workflows` is not a supported GITHUB_TOKEN permission key, so GitHub
rejects the workflow definition. Use the valid `actions` permission
for both jobs.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-07-10 17:06:49 +02:00
Sebastiaan van StijnandGitHub 1423089969 Merge pull request #7101 from vvoland/sync-master
sync-release-branch: Run from master against selected release
2026-07-10 15:22:02 +02:00
Paweł Gronowski 5853360a94 gha/sync-release-branch: Add workflows write permission
Without it GitHub rejects the push:

```
refusing to allow a GitHub App to create or update workflow `.github/workflows/build.yml` without `workflows` permission
```

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-07-10 13:49:45 +02:00
Paweł Gronowski f816d5c003 sync-release-branch: Run from master against selected release
The workflow previously inferred its target from the dispatch ref,
requiring operators to always sync the release branch with the
workflow/scripts on master.

Accept the release branch as an input, keep the dispatched master
checkout as the script source, and merge in a detached worktree at the
selected release revision.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-07-10 13:49:45 +02:00
Paweł GronowskiandGitHub 25a1d41669 Merge pull request #7094 from thaJeztah/rm_go_reportcard
README: remove Go Report Card badge
2026-07-10 12:27:07 +02:00
dependabot[bot]andGitHub 45d1a6cccf build(deps): bump docker/login-action in the docker-actions group
Bumps the docker-actions group with 1 update: [docker/login-action](https://github.com/docker/login-action).


Updates `docker/login-action` from 4.3.0 to 4.4.0
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/c99871dec2022cc055c062a10cc1a1310835ceb4...af1e73f918a031802d376d3c8bbc3fe56130a9b0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-10 08:46:40 +00:00
Sebastiaan van StijnandGitHub ba55f0c3b1 Merge pull request #7096 from docker/dependabot/github_actions/docker-actions-c21f7ea42a
build(deps): bump the docker-actions group with 3 updates
2026-07-10 08:39:04 +02:00
Sebastiaan van StijnandGitHub 9c1b7fc671 Merge pull request #7095 from docker/dependabot/github_actions/codeql-actions-920a780463
build(deps): bump the codeql-actions group with 3 updates
2026-07-10 08:37:26 +02:00
Sebastiaan van StijnandGitHub a8ddac1a8e Merge pull request #7098 from vvoland/sync-editor
scripts/sync-branch: Fix non-interactive merge
2026-07-10 08:31:05 +02:00
mickael emirkanian 2d978b80ae docs(authz): clarify daemon parsing semantics
Signed-off-by: mickael emirkanian <mickael.emirkanian@docker.com>
2026-07-09 13:36:32 -04:00
Paweł Gronowski f57e528457 scripts/sync-branch: Fix non-interactive merge
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-07-09 16:51:33 +02:00
Sebastiaan van StijnandGitHub 9ba113bc5d Merge pull request #7091 from vvoland/gha-sync-release
gha: Add release branch sync workflow
2026-07-09 14:29:42 +02:00
dependabot[bot]andGitHub cd79c7ebfc build(deps): bump the docker-actions group with 3 updates
Bumps the docker-actions group with 3 updates: [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action), [docker/login-action](https://github.com/docker/login-action) and [docker/metadata-action](https://github.com/docker/metadata-action).


Updates `docker/setup-buildx-action` from 4.1.0 to 4.2.0
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5...bb05f3f5519dd87d3ba754cc423b652a5edd6d2c)

Updates `docker/login-action` from 4.2.0 to 4.3.0
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/650006c6eb7dba73a995cc03b0b2d7f5ca915bee...c99871dec2022cc055c062a10cc1a1310835ceb4)

Updates `docker/metadata-action` from 6.1.0 to 6.2.0
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](https://github.com/docker/metadata-action/compare/80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9...dc802804100637a589fabce1cb79ff13a1411302)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: docker-actions
- dependency-name: docker/login-action
  dependency-version: 4.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: docker-actions
- dependency-name: docker/metadata-action
  dependency-version: 6.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: docker-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-09 08:47:18 +00:00
dependabot[bot]andGitHub 042528819a build(deps): bump the codeql-actions group with 3 updates
Bumps the codeql-actions group with 3 updates: [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/autobuild](https://github.com/github/codeql-action) and [github/codeql-action/analyze](https://github.com/github/codeql-action).


Updates `github/codeql-action/init` from 4.36.2 to 4.36.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/8aad20d150bbac5944a9f9d289da16a4b0d87c1e...54f647b7e1bb85c95cddabcd46b0c578ec92bc1a)

Updates `github/codeql-action/autobuild` from 4.36.2 to 4.36.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/8aad20d150bbac5944a9f9d289da16a4b0d87c1e...54f647b7e1bb85c95cddabcd46b0c578ec92bc1a)

Updates `github/codeql-action/analyze` from 4.36.2 to 4.36.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/8aad20d150bbac5944a9f9d289da16a4b0d87c1e...54f647b7e1bb85c95cddabcd46b0c578ec92bc1a)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.36.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-actions
- dependency-name: github/codeql-action/autobuild
  dependency-version: 4.36.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-actions
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.36.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-09 08:46:24 +00:00
Sebastiaan van Stijn 8c9e2f353e README: remove Go Report Card badge
The project was sunset;

> After more than a decade of serving the ecosystem, the time
> has come to sunset Go Report Card. Following the loss of our
> primary infrastructure sponsor, maintaining the web app is
> no longer sustainable.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-09 01:03:31 +02:00
Sebastiaan van StijnandGitHub a86b9aa542 Merge pull request #7090 from docker/dependabot/go_modules/cmd/docker-trust/go_modules-a3c8a40308
build(deps): bump golang.org/x/crypto from 0.50.0 to 0.52.0 in /cmd/docker-trust in the go_modules group across 1 directory
2026-07-08 23:31:27 +02:00
Paweł Gronowski 182f56fe8c gha: Add release branch sync workflow
Add a manually dispatched workflow for maintainers to sync a Docker
release branch to a selected release tag.

The sync-release-branch job checks out the release branch, computes
the list of unmerged tags up to the requested tag via
scripts/unmerged-tags, merges them in order via scripts/sync-branch
using git merge --no-ff (resolving conflicts by taking the tag's
content), then pushes the result to a temporary branch.

The push-release-branch job runs after manual approval via the
docker-releases environment. It verifies that neither the release
branch nor the temporary branch moved since the sync job ran before
force-advancing the release branch and deleting the temporary branch.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-07-08 20:22:22 +02:00
Sebastiaan van StijnandGitHub e5c424fd70 Merge pull request #7092 from docker/dependabot/github_actions/docker-actions-78c0e55afe
build(deps): bump the docker-actions group with 2 updates
2026-07-08 15:00:29 +02:00
dependabot[bot]andGitHub 145e7f83cd build(deps): bump the docker-actions group with 2 updates
Bumps the docker-actions group with 2 updates: [docker/bake-action](https://github.com/docker/bake-action) and [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action).


Updates `docker/bake-action` from 7.2.0 to 7.3.0
- [Release notes](https://github.com/docker/bake-action/releases)
- [Commits](https://github.com/docker/bake-action/compare/6614cfa25eff9a0b2b2697efb0b6159e7680d584...d3418bd7d0e9324001bca92fa8ba175ea7e6dc9b)

Updates `docker/setup-qemu-action` from 4.1.0 to 4.2.0
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/06116385d9baf250c9f4dcb4858b16962ea869c3...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/bake-action
  dependency-version: 7.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: docker-actions
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: docker-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-08 10:20:16 +00:00
Paweł GronowskiandGitHub e8ded3fce7 Merge pull request #7085 from thaJeztah/dependabot_group
gha: dependabot: group docker/* and codeql action updates
2026-07-08 12:15:03 +02:00
dependabot[bot]andGitHub 91db63cf5b build(deps): bump golang.org/x/crypto
Bumps the go_modules group with 1 update in the /cmd/docker-trust directory: [golang.org/x/crypto](https://github.com/golang/crypto).


Updates `golang.org/x/crypto` from 0.50.0 to 0.52.0
- [Commits](https://github.com/golang/crypto/compare/v0.50.0...v0.52.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.52.0
  dependency-type: indirect
  dependency-group: go_modules
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-08 09:15:41 +00:00
Paweł GronowskiandGitHub d4218f26dc Merge pull request #7087 from thaJeztah/update_go1.26.5
update to go1.26.5
2026-07-08 11:14:40 +02:00
Sebastiaan van Stijn 6d245daa91 update to go1.26.5
go1.26.5 (released 2026-07-07) includes security fixes to the crypto/tls
and os packages, as well as bug fixes to the compiler, the runtime, the
go command, and the net, os, and syscall packages. See the Go 1.26.5
milestone on our issue tracker for details;

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

From the security announcement:

We have just released Go versions 1.26.5 and 1.25.12, minor point releases.

These releases include 2 security fixes following the security policy:

- os: Root escape via symlink plus trailing slash

  On Unix systems, opening a file in an os.Root improperly
  followed symlinks to locations outside of the Root when
  the final path component of the a path is a symbolic link
  and the path ends in /.

  For example, root.Open("symlink/") would open "symlink"
  even when "symlink" is a symbolic link pointing outside of the root.

  On Unix, openat(fd, path, O_NOFOLLOW) will follow symlinks
  in path when path ends in a /. Root failed to account for
  this behavior, permitting paths with a trailing / to escape.
  It now properly sanitizes the path parameter provided to openat.

  hanks to Mundur for reporting this issue.

  This is CVE-2026-39822 and Go issue https://go.dev/issue/79005.

- crypto/tls: Encrypted Client Hello privacy leak

  he Encrypted Client Hello implementation would leak the pre-shared key
  dentities during the handshake, allowing a passive network observer who can
  ollect handshakes to de-anonymize the hostname of the server, even when ECH was
  eing used.

  Thanks to Coia Prant (github.com/rbqvq) for reporting this issue.

  This is CVE-2026-42505 and Go issue https://go.dev/issue/79282.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-08 00:01:42 +02:00
Sebastiaan van Stijn 2282b23f02 gha: dependabot: group docker/* and codeql action updates
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-07 15:56:25 +02:00
Sebastiaan van Stijn fd3497683d internal/prompt: Confirm: don't wrap stdIn
This was a remnant from when `PromptForConfirmation` accepted a `InStream`;
it was added in [moby@30b8f08], which got left behind when [cli@37ccc00]
updated its signature to accept a plain `io.Reader`.

While updating, also update the comment to provide more context on the reason
we're unconditionally using `os.Stdin` on Windows.

[moby@30b8f08]: https://github.com/moby/moby/commit/30b8f084436a2a1d5e8523fcd2c5ea64cc805224
[cli@37ccc00]: https://github.com/docker/cli/commit/37ccc00d0e14461bcf29e98669773625dfed2fce

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-07 14:19:44 +02:00
Sebastiaan van Stijn 2abf92e95c cli/command: PromptUserForCredentials: don't mutate cli
PromptUserForCredentials accepted a Cli as argument so that it could swap
the input stream on Windows (cli.SetIn).

Given that we only require this swap for the duration of this function (if
needed at all), we can use a local variable that either uses cli.In() or
os.Stdin (on Windows).

We currently still need to wrap the os.Stdin into a streams.In, but can use
the raw os.Stdin (and/or cli.In().File()) once prompt.DisableInputEcho is
updated.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-07-07 14:19:20 +02:00
Sebastiaan van StijnandGitHub 40a8e8e754 Merge pull request #7081 from vvoland/work-gha
Update docker-agent-action to v2.0.2
2026-07-06 11:43:13 +02:00
Paweł Gronowski da2622ed8e Update docker-agent-action to v2.0.2
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-07-06 11:35:08 +02:00
Sebastiaan van StijnandGitHub c4c704e62b Merge pull request #7079 from derekmisler/fix/pr-review-trigger-concurrency
ci: add concurrency group to pr-review-trigger to prevent duplicate reviews
2026-07-02 16:40:57 +02:00
Derek MislerandDerek Misler 1c742902b6 ci: add concurrency group and remove bot filter in pr-review-trigger
Add a concurrency group keyed on PR number to prevent duplicate reviews
from simultaneous review_requested events.

Remove the sender.type != 'Bot' guard so Dependabot PRs remain
reviewable — per maintainer feedback, those reviews are useful for
catching behavior changes in dependency updates.

Signed-off-by: Derek Misler <derek.misler@docker.com>
2026-07-02 14:26:26 +00:00
Sebastiaan van StijnandGitHub 0f83acece4 Merge pull request #7077 from docker/dependabot/github_actions/actions/setup-go-6.5.0
build(deps): bump actions/setup-go from 6.3.0 to 6.5.0
2026-06-30 20:40:27 +02:00
Paweł GronowskiandGitHub 3edebc433e Merge pull request #7078 from thaJeztah/version
bump VERSION to v29.7.0-dev
2026-06-30 16:58:09 +02:00
Sebastiaan van Stijn 635fed89d2 bump VERSION to v29.7.0-dev
This reverts commit d9c59c9cfe.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-30 16:31:14 +02:00
dependabot[bot]andGitHub 444bab12d9 build(deps): bump actions/setup-go from 6.3.0 to 6.5.0
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 6.3.0 to 6.5.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/4b73464bb391d4059bd26b0524d20df3927bd417...924ae3a1cded613372ab5595356fb5720e22ba16)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: 6.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-30 08:44:46 +00:00
Sebastiaan van StijnandGitHub 8900f1d330 Merge pull request #7074 from thaJeztah/version
version 29.6.1
2026-06-25 22:08:24 +02:00
Sebastiaan van StijnandGitHub 22b8f1396e Merge pull request #7069 from docker-agent/auto/migrate-to-docker-agent-action
chore: migrate cagent-action to docker-agent-action (v2.0.0)
2026-06-25 22:05:29 +02:00
Sebastiaan van Stijn d9c59c9cfe version 29.6.1
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-25 22:03:12 +02:00
Sebastiaan van StijnandGitHub 64307515e6 Merge pull request #7073 from thaJeztah/bump_moby_user
vendor: github.com/moby/sys/user v0.4.1
2026-06-25 21:59:18 +02:00
Sebastiaan van Stijn 8fda97b545 vendor: github.com/moby/sys/user v0.4.1
- user: prevent possible DoS via unbounded parsing of user and group
  database files in GHSA-mjcv-p78q-w5fw. This fixes a similar issue
  as CVE-2026-47262 in containerd.
- user: prevent falling back to looking up numeric usernames
  Improve handling of numeric user/group to prevent looking up numeric
  values as usernames. This fixes a similar issue as [CVE-2026-46680] in
  containerd.
- user: update minimum go version to go1.18
- assorted testing and linting fixes.

[CVE-2026-46680]: https://github.com/advisories/GHSA-fqw6-gf59-qr4w

full diff: https://github.com/moby/sys/compare/user/v0.4.0...user/v0.4.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-25 21:31:42 +02:00
Docker Agent f9dc4e413b chore: bump docker-agent-action to v2.0.1
Signed-off-by: Docker Agent <svc-github-docker-agent@docker.com>
2026-06-24 13:32:46 +00:00
Sebastiaan van StijnandGitHub f8a2d2b253 Merge pull request #7070 from docker/dependabot/github_actions/actions/checkout-7.0.0
build(deps): bump actions/checkout from 6.0.3 to 7.0.0
2026-06-24 10:48:34 +02:00
dependabot[bot]andGitHub 7eb15d3454 build(deps): bump actions/checkout from 6.0.3 to 7.0.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.3 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/df4cb1c069e1874edd31b4311f1884172cec0e10...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-24 08:42:35 +00:00
Sebastiaan van StijnandGitHub 033b0ff9ff Merge pull request #7063 from docker/dependabot/github_actions/crazy-max/dot-github/dot-github/workflows/zizmor.yml-1.10.1
build(deps): bump crazy-max/.github/.github/workflows/zizmor.yml from 1.10.0 to 1.10.1
2026-06-22 16:42:54 +02:00
Sebastiaan van StijnandGitHub 317bfd1231 Merge pull request #7067 from docker/dependabot/github_actions/docker/cagent-action/dot-github/workflows/review-pr.yml-1.5.5
build(deps): bump docker/cagent-action/.github/workflows/review-pr.yml from 1.5.4 to 1.5.5
2026-06-22 16:41:07 +02:00
Sebastiaan van StijnandGitHub 1b4c7d7807 Merge pull request #7068 from thaJeztah/bump_version
bump VERSION to v29.7.0-dev
2026-06-22 16:39:50 +02:00
Sebastiaan van Stijn e45822e51d bump VERSION to v29.7.0-dev
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-22 15:52:40 +02:00
dependabot[bot]andGitHub a97303090d build(deps): bump docker/cagent-action/.github/workflows/review-pr.yml
Bumps [docker/cagent-action/.github/workflows/review-pr.yml](https://github.com/docker/cagent-action) from 1.5.4 to 1.5.5.
- [Release notes](https://github.com/docker/cagent-action/releases)
- [Commits](https://github.com/docker/cagent-action/compare/3f5dc9969f307d3c76acb7e9ccaefdd96bd62f4b...367a30ddb41e0156459d03750f508eac03f3c38a)

---
updated-dependencies:
- dependency-name: docker/cagent-action/.github/workflows/review-pr.yml
  dependency-version: 1.5.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-22 08:43:57 +00:00
dependabot[bot]andGitHub d516a10f93 build(deps): bump crazy-max/.github/.github/workflows/zizmor.yml
Bumps [crazy-max/.github/.github/workflows/zizmor.yml](https://github.com/crazy-max/.github) from 1.10.0 to 1.10.1.
- [Release notes](https://github.com/crazy-max/.github/releases)
- [Commits](https://github.com/crazy-max/.github/compare/716fd1c51a46c5d93a41d44a94b439c9ee802536...46267a6e61cd56aac2fc79943df180152f4c89d6)

---
updated-dependencies:
- dependency-name: crazy-max/.github/.github/workflows/zizmor.yml
  dependency-version: 1.10.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-19 08:42:27 +00:00
Sebastiaan van StijnandGitHub fb59821d45 Merge pull request #7062 from vvoland/update-docker
vendor: github.com/moby/moby api v1.55.0 and client v0.5.0
2026-06-18 21:53:10 +02:00
Paweł Gronowski ee2f737013 vendor: github.com/moby/moby/client v0.5.0
full diff: https://github.com/moby/moby/compare/client/v0.5.0-rc.1...client/v0.5.0

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-06-18 21:41:38 +02:00
Paweł Gronowski 1f80e23560 vendor: github.com/moby/moby/api v1.55.0
full diff: https://github.com/moby/moby/compare/api/v1.55.0-rc.1...api/v1.55.0

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-06-18 21:39:19 +02:00
Lohit Kolluri d9698edc85 e2e: add private registry pull/push regression test
This adds an e2e regression test for authenticated pull/push against a private registry, covering the auth regression reported in docker/cli#5963.

Includes:
- New privateregistry service in the e2e Compose stack with htpasswd auth on port 5001, and --insecure-registry for the engine container.
- TestPullPushPrivateRepository test that verifies authenticated push/pull and rejects unauthenticated operations.
- Auth config and test credentials in e2e/testdata/registry/.
- 90-second retry loop for transient DNS/container startup races.
- Service health wait loop in scripts/test/e2e/run.
- Increase TestProcessTermination timeout from 10s to 20s for connhelper-ssh + engine 25 combination.
- Connhelper-ssh engine Dockerfile for private registry integration.

Signed-off-by: Lohit Kolluri <lohitkolluri@gmail.com>
2026-06-13 00:22:00 +05:30
Paweł GronowskiandGitHub 1d1562e004 Merge pull request #7029 from agirault/login-password-dash-stdin
cli/registry: support password dash stdin
2026-06-12 19:31:17 +02:00
Sebastiaan van StijnandGitHub 8c2e80070b Merge pull request #7051 from thaJeztah/bump_moby
vendor: github.com/moby/moby/api v1.55.0-rc.1, moby/client v0.5.0-rc.1
2026-06-12 19:07:07 +02:00
Sebastiaan van Stijn 233cd4a643 vendor: github.com/moby/moby/api v1.55.0-rc.1, moby/client v0.5.0-rc.1
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-12 18:54:32 +02:00
Paweł GronowskiandGitHub 5b600d015d Merge pull request #7047 from thaJeztah/bump_go_events
vendor: github.com/docker/go-events v0.0.0-20260608200158-dbf6103125a4
2026-06-12 18:44:35 +02:00
Paweł GronowskiandGitHub e6decf4d85 Merge pull request #7048 from thaJeztah/bump_compress
vendor: github.com/klauspost/compress v1.18.6
2026-06-12 18:44:24 +02:00
Paweł GronowskiandGitHub a9284d1161 Merge pull request #7049 from thaJeztah/bump_x_net
vendor: golang.org/x/net v0.56.0
2026-06-12 18:44:11 +02:00
Paweł GronowskiandGitHub e7319c78af Merge pull request #7050 from thaJeztah/update_authors_mailmap
update AUTHORS and mailmap
2026-06-12 18:43:43 +02:00
Sebastiaan van Stijn 0f1dbdea86 update AUTHORS and mailmap
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-12 18:05:12 +02:00
Sebastiaan van Stijn fef3ef83fe vendor: golang.org/x/net v0.56.0
full diff: https://github.com/golang/net/compare/v0.55.0...v0.56.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-12 17:13:23 +02:00
Sebastiaan van Stijn 9eff92c55a vendor: github.com/klauspost/compress v1.18.6
full diff: https://github.com/klauspost/compress/compare/v1.18.5...v1.18.6

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-12 17:11:24 +02:00
Sebastiaan van Stijn 43f745b242 vendor: github.com/docker/go-events v0.0.0-20260608200158-dbf6103125a4
full diff: https://github.com/docker/go-events/compare/605354379745...dbf6103125a4

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-12 17:07:53 +02:00
Alexis GiraultandSebastiaan van Stijn 450df790a0 cli/registry: support password dash stdin
Signed-off-by: Alexis Girault <agirault@nvidia.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-12 16:15:23 +02:00
Sebastiaan van StijnandGitHub 0df3977cb3 Merge pull request #7042 from thaJeztah/carry_deprecation
Add a deprecation warning about --link on default nw
2026-06-11 15:07:14 +02:00
Sebastiaan van StijnandGitHub 86cf0a5185 Merge pull request #7043 from docker/dependabot/github_actions/crazy-max/dot-github/dot-github/workflows/zizmor.yml-1.10.0
build(deps): bump crazy-max/.github/.github/workflows/zizmor.yml from 1.8.0 to 1.10.0
2026-06-11 14:44:57 +02:00
Sebastiaan van StijnandGitHub 3b10370c40 Merge pull request #7044 from docker/dependabot/github_actions/github/codeql-action-4.36.2
build(deps): bump github/codeql-action from 4.36.1 to 4.36.2
2026-06-11 14:39:44 +02:00
dependabot[bot]andGitHub a23b1c6770 build(deps): bump github/codeql-action from 4.36.1 to 4.36.2
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.36.1 to 4.36.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/87557b9c84dde89fdd9b10e88954ac2f4248e463...8aad20d150bbac5944a9f9d289da16a4b0d87c1e)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-11 08:44:24 +00:00
dependabot[bot]andGitHub 3a197287d2 build(deps): bump crazy-max/.github/.github/workflows/zizmor.yml
Bumps [crazy-max/.github/.github/workflows/zizmor.yml](https://github.com/crazy-max/.github) from 1.8.0 to 1.10.0.
- [Release notes](https://github.com/crazy-max/.github/releases)
- [Commits](https://github.com/crazy-max/.github/compare/9ba6e6f9450baf3b1237f8035c1fdc45932510bd...716fd1c51a46c5d93a41d44a94b439c9ee802536)

---
updated-dependencies:
- dependency-name: crazy-max/.github/.github/workflows/zizmor.yml
  dependency-version: 1.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-11 08:42:57 +00:00
Albin KerouantonandSebastiaan van Stijn aaaeb80ceb Add a deprecation warning about --link on default nw
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-10 20:12:17 +02:00
Sebastiaan van StijnandGitHub 278ac25271 Merge pull request #6039 from thaJeztah/gomod_symlink
scripts/with-go-mod.sh: use symlink instead of -modfile
2026-06-10 16:11:07 +02:00
Sebastiaan van StijnandGitHub 54f330978e Merge pull request #7028 from docker/dependabot/github_actions/docker/setup-qemu-action-4.1.0
build(deps): bump docker/setup-qemu-action from 4.0.0 to 4.1.0
2026-06-10 15:49:05 +02:00
Sebastiaan van StijnandGitHub f4a4c1e2cf Merge pull request #7038 from thaJeztah/bump_runewidth
vendor: github.com/mattn/go-runewidth v0.0.24
2026-06-10 15:48:14 +02:00
Sebastiaan van Stijn deacdc6feb scripts/with-go-mod.sh: simplify symlink comparison
Make sure the symlinks point to the same file; not if both are
identical (absolute or relative symlink); it's only for suppressing
the warning so not critical.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-10 15:44:04 +02:00
Sebastiaan van Stijn c17c247ed4 scripts/with-go-mod.sh: use symlink instead of -modfile
While the "-modfile=vendor.mod" is slightly more correct, using a
symlink allows for most of the go tools to work "as usual", just
without an acual `go.mod` being committed in the repository.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-10 15:44:01 +02:00
Sebastiaan van StijnandGitHub b680c49f57 Merge pull request #7041 from thaJeztah/no_tools
man: remove tools.go in favor of tools directive
2026-06-10 15:40:42 +02:00
Sebastiaan van StijnandGitHub 4748c4e4d3 Merge pull request #7040 from thaJeztah/fix_go_version
scripts/with-go-mod: use correct minimum Go version
2026-06-10 15:37:02 +02:00
Paweł GronowskiandGitHub 9c7701eb48 Merge pull request #7037 from thaJeztah/bump_otels
vendor: go.opentelemetry.io/otel v1.44.0, go.opentelemetry.io/contrib v0.69.0
2026-06-10 12:26:23 +02:00
Paweł GronowskiandGitHub ddc801807d Merge pull request #7036 from thaJeztah/bump_x_deps
vendor: update golang.org/x/* dependencies
2026-06-10 12:26:08 +02:00
Paweł GronowskiandGitHub 83963b759c Merge pull request #7035 from thaJeztah/bump_sequential
vendor: github.com/moby/sys/sequential v0.7.0
2026-06-10 12:25:21 +02:00
Paweł GronowskiandGitHub 2e635d7baf Merge pull request #7032 from thaJeztah/bump_creds_helper
vendor: github.com/docker/docker-credential-helpers v0.9.8
2026-06-10 12:24:02 +02:00
Sebastiaan van Stijn 1c22ca1aac scripts/docs: use "go install tool"
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-10 11:49:33 +02:00
Sebastiaan van Stijn 55016421fd man: remove tools.go in favor of tools directive
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-10 11:37:02 +02:00
Sebastiaan van Stijn 8b2a990843 scripts/with-go-mod: use correct minimum Go version
Follow-up to 8f7dc04070, which updated the
minimum Go version in vendor.mod, but did not adjust this script.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-10 10:23:25 +02:00
Sebastiaan van Stijn a9c82c0a9f vendor: github.com/mattn/go-runewidth v0.0.24
- Optimize EastAsian RuneWidth with precomputed width table

full diff: https://github.com/mattn/go-runewidth/compare/v0.0.23...v0.0.24

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-09 16:58:46 +02:00
Sebastiaan van Stijn 22d7ca46a3 vendor: go.opentelemetry.io/otel v1.44.0, go.opentelemetry.io/contrib v0.69.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-09 16:35:02 +02:00
Sebastiaan van Stijn a721bd651b vendor: google.golang.org/genproto 3dc84a4a5aaa
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-09 16:32:02 +02:00
Sebastiaan van Stijn 51583aec0b vendor: golang.org/x/text v0.38.0
full diff: https://github.com/golang/text/compare/v0.37.0...v0.38.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-09 16:26:28 +02:00
Sebastiaan van Stijn 399c9456a7 vendor: golang.org/x/sync v0.21.0
full diff: https://github.com/golang/sync/compare/v0.20.0...v0.21.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-09 16:25:46 +02:00
Sebastiaan van Stijn de24d1cbc0 vendor: golang.org/x/mod v0.37.0
full diff: https://github.com/golang/mod/compare/v0.36.0...v0.37.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-09 16:25:11 +02:00
Sebastiaan van Stijn 49dc46afed vendor: golang.org/x/term v0.44.0
full diff: https://github.com/golang/term/compare/v0.43.0...v0.44.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-09 16:24:33 +02:00
Sebastiaan van Stijn c72acd0a08 vendor: golang.org/x/sys v0.46.0
full diff: https://github.com/golang/sys/compare/v0.45.0...v0.46.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-09 16:23:59 +02:00
Sebastiaan van Stijn b601b2577a vendor: github.com/moby/sys/sequential v0.7.0
- update minimum go version to 1.24
- use os.OpenFile with O_FILE_FLAG_SEQUENTIAL_SCAN on Go 1.26+

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-09 16:22:14 +02:00
Sebastiaan van StijnandGitHub e8f837ba7f Merge pull request #7034 from docker/dependabot/github_actions/actions/checkout-6.0.3
build(deps): bump actions/checkout from 6.0.2 to 6.0.3
2026-06-09 16:12:57 +02:00
Sebastiaan van StijnandGitHub abba6bb358 Merge pull request #7033 from docker/dependabot/github_actions/github/codeql-action-4.36.1
build(deps): bump github/codeql-action from 4.36.0 to 4.36.1
2026-06-09 16:12:11 +02:00
dependabot[bot]andGitHub 9caec62f25 build(deps): bump actions/checkout from 6.0.2 to 6.0.3
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.2 to 6.0.3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/de0fac2e4500dabe0009e67214ff5f5447ce83dd...df4cb1c069e1874edd31b4311f1884172cec0e10)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-09 08:44:17 +00:00
dependabot[bot]andGitHub 6d9c126733 build(deps): bump github/codeql-action from 4.36.0 to 4.36.1
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.36.0 to 4.36.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/7211b7c8077ea37d8641b6271f6a365a22a5fbfa...87557b9c84dde89fdd9b10e88954ac2f4248e463)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-09 08:43:54 +00:00
Sebastiaan van Stijn 1aae5d7822 vendor: github.com/docker/docker-credential-helpers v0.9.8
full diff: https://github.com/docker/docker-credential-helpers/compare/v0.9.7...v0.9.8

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-08 20:58:17 +02:00
Paweł GronowskiandGitHub 4ed0e4e65d Merge pull request #7014 from vvoland/work-gha
gha: Add docker cagent PR reviewer workflows
2026-06-05 15:11:37 +02:00
Sebastiaan van StijnandGitHub 90f2f30fd1 Merge pull request #7030 from thaJeztah/refactor_handleAux
cli/command/image: handleAux: avoid using global var
2026-06-04 15:44:14 +02:00
Sebastiaan van Stijn a11beec944 cli/command/image: handleAux: avoid using global var
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-04 15:05:41 +02:00
Sebastiaan van StijnandGitHub b50c369d8b Merge pull request #6957 from WilliamK112/6890-respect-no-color-push
image push: respect NO_COLOR in aux notes
2026-06-04 14:38:54 +02:00
dependabot[bot]andGitHub d788f2d81f build(deps): bump docker/setup-qemu-action from 4.0.0 to 4.1.0
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 4.0.0 to 4.1.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/ce360397dd3f832beb865e1373c09c0e9f86d70a...06116385d9baf250c9f4dcb4858b16962ea869c3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-04 01:23:33 +00:00
Ching Wei Kang fe262bc2f9 image/push: respect NO_COLOR in aux notes
Signed-off-by: WilliamK112 <164879897+WilliamK112@users.noreply.github.com>
Signed-off-by: Ching Wei Kang <164879897+WilliamK112@users.noreply.github.com>
2026-06-03 14:57:36 -05:00
Paweł GronowskiandGitHub d1341e7caf Merge pull request #7027 from vvoland/bump-version
VERSION: 29.6.0
2026-06-03 20:25:41 +02:00
Paweł Gronowski 54636c8e2e VERSION: 29.6.0
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-06-03 20:02:59 +02:00
Paweł Gronowski a1cf501956 gha: Add docker cagent PR reviewer workflows
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-06-03 19:18:44 +02:00
Paweł GronowskiandGitHub d1c06ef6b4 Merge pull request #7022 from mickael-docker/docs-request-field
docs/plugins/authz: recommend default deny and clarify requesturi field
2026-06-03 19:16:33 +02:00
Paweł GronowskiandGitHub 7dd053b1d1 Merge pull request #7003 from thaJeztah/logs_links
docs: container logs: add headers for flags
2026-06-03 19:14:10 +02:00
Sebastiaan van StijnandGitHub 37c3d316cc Merge pull request #7024 from thaJeztah/add_zizmor
gha: add zizmor workflow
2026-06-03 18:13:43 +02:00
Paweł GronowskiandGitHub 45f10f226e Merge pull request #7025 from vvoland/update-go
update to go1.26.4
2026-06-03 17:45:58 +02:00
Paweł Gronowski 3cc61496db cli/file_store: Preserve IPv6 URL normalization after Go change
Add a fallback for unbracketed IPv6 literals to preserve behavior.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-06-03 17:40:39 +02:00
Paweł Gronowski 55b88882d7 cli/file_store: Clarify ConvertToHostname
The implementation seems to have diverged already.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-06-03 17:40:39 +02:00
Paweł Gronowski b458dc9e81 update to go1.26.4
This release include 3 security fixes following the security policy:

- mime: quadratic complexity in WordDecoder.DecodeHeader

    Decoding a maliciously-crafted MIME header containing many invalid
    encoded-words could consume excessive CPU.
    The MIME decoder now better handles this case.

    Thanks to p4p3r (https://hackerone.com/p4p3r_hak) for reporting this issue.

    This is CVE-2026-42504 and Go issue https://go.dev/issue/79217.

- net/textproto: arbitrary input are included in errors without any escaping

    When returning errors, functions in the net/textproto package would
    include its input as part of the error, without any escaping. Note that
    said input is often controlled by external parties when using this
    package naturally. For example, a net/http client uses ReadMIMEHeader
    when parsing the headers it receive from a server.

    As a result, an attacker could inject arbitrary content into the error.
    Practically, this can result in an attacker injecting misleading
    content, terminal control bytes, etc. into a victim's output or logs.

    This is CVE-2026-42507 and Go issue https://go.dev/issue/79346

- crypto/x509: split candidate hostname only once

    (*x509.Certificate).VerifyHostname previously called matchHostnames in a loop
    over all DNS Subject Alternative Name (SAN) entries. This caused
    strings.Split(host, ".") to execute repeatedly on the same input hostname.

    With a large DNS SAN list, verification costs scaled quadratically based on the
    number of SAN entries multiplied by the hostname's label count. Because
    x509.Verify validates hostnames before building the certificate chain, this
    overhead occurred even for untrusted certificates.

    Thanks to Jakub Ciolek (https://ciolek.dev) for reporting this issue.

    This is CVE-2026-27145 and https://go.dev/issue/79694.

View the release notes for more information:
https://go.dev/doc/devel/release#go1.26.4

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-06-03 17:07:58 +02:00
Sebastiaan van Stijn 1953194bd5 gha: apply zizmor fixes
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-03 16:45:20 +02:00
Sebastiaan van Stijn ac0419ea90 gha: add zizmor workflow
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-03 12:03:45 +02:00
mickael emirkanian 1aa0416b8a docs: recommend default deny and clarify requesturi field
Signed-off-by: mickael emirkanian <mickael.emirkanian@docker.com>
2026-06-02 15:18:36 -04:00
Paweł GronowskiandGitHub 3a85952984 Merge pull request #7020 from thaJeztah/full_semver
ci: update comments to show full (major.minor.patch) version
2026-06-02 15:18:14 +02:00
Paweł GronowskiandGitHub 8d3fbdf570 Merge pull request #7019 from thaJeztah/dependabot_labels
ci: use "area/ci" label for dependabot actions updates
2026-06-02 11:54:27 +02:00
Sebastiaan van Stijn 69c3f5c7af ci: update comments to show full (major.minor.patch) version
This makes it easier to verify the sha matches the tag, and may help
with dependabot not updating the version-comment correctly.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-01 15:21:54 +02:00
Sebastiaan van Stijn 0ae4aac726 ci: use "area/ci" label for dependabot actions updates
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-01 15:02:21 +02:00
Sebastiaan van StijnandGitHub 36edcdf9f3 Merge pull request #7004 from docker/dependabot/github_actions/codecov/codecov-action-6.0.1
build(deps): bump codecov/codecov-action from 6.0.0 to 6.0.1
2026-06-01 14:39:16 +02:00
Sebastiaan van StijnandGitHub 4e815a7e97 Merge pull request #7013 from docker/dependabot/github_actions/docker/metadata-action-6.1.0
build(deps): bump docker/metadata-action from 6.0.0 to 6.1.0
2026-05-29 14:56:40 +02:00
Sebastiaan van StijnandGitHub 32e1269780 Merge pull request #7012 from docker/dependabot/github_actions/github/codeql-action-4.36.0
build(deps): bump github/codeql-action from 4.35.5 to 4.36.0
2026-05-29 14:55:48 +02:00
Sebastiaan van StijnandGitHub 5f09e74d48 Merge pull request #7011 from docker/dependabot/github_actions/docker/login-action-4.2.0
build(deps): bump docker/login-action from 4.1.0 to 4.2.0
2026-05-29 14:55:13 +02:00
Sebastiaan van StijnandGitHub 98df84cf86 Merge pull request #7010 from docker/dependabot/github_actions/docker/setup-buildx-action-4.1.0
build(deps): bump docker/setup-buildx-action from 4.0.0 to 4.1.0
2026-05-29 14:54:33 +02:00
dependabot[bot]andGitHub 20b3bc3f64 build(deps): bump docker/metadata-action from 6.0.0 to 6.1.0
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 6.0.0 to 6.1.0.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](https://github.com/docker/metadata-action/compare/030e881283bb7a6894de51c315a6bfe6a94e05cf...80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-29 09:02:12 +00:00
dependabot[bot]andGitHub 8a9d271adf build(deps): bump github/codeql-action from 4.35.5 to 4.36.0
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.35.5 to 4.36.0.
- [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/9e0d7b8d25671d64c341c19c0152d693099fb5ba...7211b7c8077ea37d8641b6271f6a365a22a5fbfa)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-29 09:01:52 +00:00
dependabot[bot]andGitHub f4f8ffb9d7 build(deps): bump docker/login-action from 4.1.0 to 4.2.0
Bumps [docker/login-action](https://github.com/docker/login-action) from 4.1.0 to 4.2.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/4907a6ddec9925e35a0a9e82d7399ccc52663121...650006c6eb7dba73a995cc03b0b2d7f5ca915bee)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-29 09:00:23 +00:00
dependabot[bot]andGitHub 2aeb70a5b9 build(deps): bump docker/setup-buildx-action from 4.0.0 to 4.1.0
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 4.0.0 to 4.1.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd...d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-29 09:00:12 +00:00
Sebastiaan van StijnandGitHub d476e1d9bf Merge pull request #7009 from docker/dependabot/github_actions/docker/bake-action-7.2.0
build(deps): bump docker/bake-action from 7.1.0 to 7.2.0
2026-05-28 14:47:12 +02:00
dependabot[bot]andGitHub 07bb5e458c build(deps): bump docker/bake-action from 7.1.0 to 7.2.0
Bumps [docker/bake-action](https://github.com/docker/bake-action) from 7.1.0 to 7.2.0.
- [Release notes](https://github.com/docker/bake-action/releases)
- [Commits](https://github.com/docker/bake-action/compare/a66e1c87e2eca0503c343edf1d208c716d54b8a8...6614cfa25eff9a0b2b2697efb0b6159e7680d584)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-28 09:02:20 +00:00
dependabot[bot]andGitHub 224685e1ea build(deps): bump codecov/codecov-action from 6.0.0 to 6.0.1
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 6.0.0 to 6.0.1.
- [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/57e3a136b779b570ffcdbf80b3bdc90e7fab3de2...e79a6962e0d4c0c17b229090214935d2e33f8354)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-25 12:43:12 +00:00
Sebastiaan van StijnandGitHub 9f16882de4 Merge pull request #7001 from thaJeztah/bump_x_deps
vendor: golang.org/x/sys v0.45.0, golang.org/x/net v0.55.0
2026-05-22 16:03:13 +02:00
Sebastiaan van Stijn 9e098b0f82 docs: container logs: add headers for flags
Add separate headers for each flag, so that they can be linked from
the flag-description table.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-22 14:15:38 +02:00
Paweł GronowskiandGitHub f3f823c78b Merge pull request #7002 from thaJeztah/smaller_size
scripts/build: set grpcnotrace build-tag to reduce binary size
2026-05-22 12:16:45 +02:00
Sebastiaan van StijnandGitHub 02e2c2ecda Merge pull request #7000 from docker/dependabot/github_actions/github/codeql-action-4.35.5
build(deps): bump github/codeql-action from 4.35.4 to 4.35.5
2026-05-22 12:03:35 +02:00
Sebastiaan van Stijn 45fc3b034a scripts/build: set grpcnotrace build-tag to reduce binary size
Reduces the binary size (39810466 - 39289106 => 521360 (521 kb)

We only import google.golang.org/grpc as an indirect dependency, and
do not make gRPC connections.

grpcnotrace avoids importing golang.org/x/net/trace, which in turn enables
dead code elimination, which can yield 10-15% improvements in binary size
when tracing is not needed.

see https://github.com/grpc/grpc-go/blob/v1.81.1/trace_notrace.go#L23-L25

Before:

    ls -l ./build/docker-darwin-arm64
    -rwxr-xr-x  1 thajeztah  staff  39810466 May 22 11:44 ./build/docker-darwin-arm64*
    ls -lh ./build/docker-darwin-arm64
    -rwxr-xr-x  1 thajeztah  staff    38M May 22 11:44 ./build/docker-darwin-arm64*

After:

    ls -l ./build/docker-darwin-arm64
    -rwxr-xr-x  1 thajeztah  staff  39289106 May 22 11:45 ./build/docker-darwin-arm64*
    ls -lh ./build/docker-darwin-arm64
    -rwxr-xr-x  1 thajeztah  staff    37M May 22 11:45 ./build/docker-darwin-arm64*

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-22 11:54:09 +02:00
Sebastiaan van Stijn 20f5e7c08c vendor: golang.org/x/net v0.55.0
security changes (not used in our code)

- html: escape greater-than symbol in doctype identifiers (CVE-2026-25681)
- html: improve Noah's Ark clause performance (CVE-2026-25680)
- html: properly render fostered elements in foreign content (CVE-2026-42502)
- html: properly check namespace in "in body" any other end tag (CVE-2026-42506)
- html: ignore duplicate attributes during tokenization (CVE-2026-27136)

other changes:

- quic: fix appendMaxDataFrame erroneously accumulating sentLimit
- quic: establish a "happened-before" relationship between stream write and read
- quic: fix buffer slicing when handling overlapping stream data
- http2: avoid API changes when built with go1.27

security announce: https://groups.google.com/g/golang-announce/c/iI-mYSI0lu8
full diff: https://github.com/golang/net/compare/v0.54.0...v0.55.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-22 11:27:01 +02:00
Sebastiaan van Stijn 20debc9c01 vendor: golang.org/x/sys v0.45.0
notable changes:

- unix: update to Linux kernel 7.0
- unix: add Readv, Writev, Preadv, Pwritev for OpenBSD
- windows: add NtSetEaFile, NtQueryEaFile and NtQueryInformationFile
- cpu: add LLACQ_SCREL, SCQ, DBAR_HINTS detection for loong64
- cpu: detect zbc extension on riscv64

full diff: https://github.com/golang/sys/compare/v0.44.0...v0.45.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-22 11:25:58 +02:00
dependabot[bot]andGitHub cf5f060b4d build(deps): bump github/codeql-action from 4.35.4 to 4.35.5
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.35.4 to 4.35.5.
- [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/68bde559dea0fdcac2102bfdf6230c5f70eb485e...9e0d7b8d25671d64c341c19c0152d693099fb5ba)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-22 08:50:13 +00:00
Paweł GronowskiandGitHub 9712e537b9 Merge pull request #6999 from thaJeztah/bump_version
bump VERSION to v29.5.3-dev
2026-05-21 11:12:04 +02:00
Sebastiaan van Stijn d6eded1632 bump VERSION to v29.5.3-dev
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-05-21 09:54:04 +02:00
Sebastiaan van StijnandGitHub 4494f1af5a Merge pull request #6998 from mickael-docker/docs-clarify-authz
docs: further clarify authz plugins
2026-05-21 09:52:08 +02:00
mickael emirkanian 066d508bd3 docs: further clarify authz plugins
Signed-off-by: mickael emirkanian <mickael.emirkanian@docker.com>
2026-05-19 15:44:59 -04:00
428 changed files with 14654 additions and 4888 deletions
+8 -1
View File
@@ -5,7 +5,14 @@ updates:
schedule:
interval: "daily"
labels:
- "area/testing"
- "area/ci"
- "status/2-code-review"
cooldown:
default-days: 7
groups:
codeql-actions:
patterns:
- "github/codeql-action/*"
docker-actions:
patterns:
- "docker/*"
+39 -48
View File
@@ -35,7 +35,9 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
-
name: Create matrix
id: platforms
@@ -63,10 +65,10 @@ jobs:
steps:
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
-
name: Build
uses: docker/bake-action@a66e1c87e2eca0503c343edf1d208c716d54b8a8 # v7.1.0
uses: docker/bake-action@d3418bd7d0e9324001bca92fa8ba175ea7e6dc9b # v7.3.0
with:
targets: ${{ matrix.target }}
set: |
@@ -78,63 +80,50 @@ jobs:
working-directory: ./build
run: |
mkdir /tmp/out
platform=${{ matrix.platform }}
platformPair=${platform//\//-}
platformPair=${PLATFORM//\//-}
tar -cvzf "/tmp/out/docker-${platformPair}.tar.gz" .
if [ -z "${{ matrix.use_glibc }}" ]; then
echo "ARTIFACT_NAME=${{ matrix.target }}-${platformPair}" >> $GITHUB_ENV
else
echo "ARTIFACT_NAME=${{ matrix.target }}-${platformPair}-glibc" >> $GITHUB_ENV
fi
env:
PLATFORM: ${{ matrix.platform }}
-
name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ env.ARTIFACT_NAME }}
path: /tmp/out/*
if-no-files-found: error
bin-image:
runs-on: ubuntu-24.04
if: ${{ github.event_name != 'pull_request' && github.repository == 'docker/cli' }}
steps:
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
username: ${{ secrets.DOCKERHUB_CLIBIN_USERNAME }}
password: ${{ secrets.DOCKERHUB_CLIBIN_TOKEN }}
-
name: Set up QEMU
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
-
name: Docker meta
id: meta
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
with:
images: dockereng/cli-bin
tags: |
type=semver,pattern={{version}}
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
-
name: Build and push image
uses: docker/bake-action@a66e1c87e2eca0503c343edf1d208c716d54b8a8 # v7.1.0
with:
files: |
./docker-bake.hcl
cwd://${{ steps.meta.outputs.bake-file }}
targets: bin-image-cross
push: ${{ github.event_name != 'pull_request' }}
set: |
*.cache-from=type=gha,scope=bin-image
*.cache-to=type=gha,scope=bin-image,mode=max
uses: docker/github-builder/.github/workflows/bake.yml@a492c6d04fd3315f67230809b44d60cc0acd50b3 # v1.16.0
permissions:
contents: read # same as global permission
id-token: write # for signing attestation(s) and authenticating to Docker Hub with GitHub OIDC Token
with:
setup-qemu: true
target: bin-image-cross
cache: true
cache-scope: bin-image
output: image
push: true
vars: |
VERSION=${{ github.ref }}
meta-images: |
dockereng/cli-bin
meta-tags: |
type=semver,pattern={{version}}
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
registry-identities: |
- type: dockerhub
username: dockereng
connection_id: ${{ vars.DOCKERHUB_OIDC_CONNECTIONID }}
prepare-plugins:
runs-on: ubuntu-24.04
@@ -143,7 +132,9 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
-
name: Create matrix
id: platforms
@@ -165,10 +156,10 @@ jobs:
steps:
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
-
name: Build
uses: docker/bake-action@a66e1c87e2eca0503c343edf1d208c716d54b8a8 # v7.1.0
uses: docker/bake-action@d3418bd7d0e9324001bca92fa8ba175ea7e6dc9b # v7.3.0
with:
targets: plugins-cross
set: |
+7 -6
View File
@@ -46,9 +46,10 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 2
persist-credentials: false
# CodeQL 2.16.4's auto-build added support for multi-module repositories,
# and is trying to be smart by searching for modules in every directory,
# including vendor directories. If no module is found, it's creating one
@@ -61,20 +62,20 @@ jobs:
ln -s vendor.sum go.sum
-
name: Update Go
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.26.3"
go-version: "1.26.7"
cache: false
-
name: Initialize CodeQL
uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
uses: github/codeql-action/init@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
with:
languages: go
-
name: Autobuild
uses: github/codeql-action/autobuild@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
uses: github/codeql-action/autobuild@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
-
name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
uses: github/codeql-action/analyze@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
with:
category: "/language:go"
+5 -3
View File
@@ -44,7 +44,9 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
-
name: Update daemon.json
run: |
@@ -63,7 +65,7 @@ jobs:
docker info
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
-
name: Run ${{ matrix.target }}
run: |
@@ -74,7 +76,7 @@ jobs:
TESTFLAGS: -coverprofile=/tmp/coverage/coverage.txt
-
name: Send to Codecov
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v5
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
with:
files: ./build/coverage/coverage.txt
token: ${{ secrets.CODECOV_TOKEN }}
+42
View File
@@ -0,0 +1,42 @@
name: PR Review - Trigger
on:
pull_request:
types: [ready_for_review, opened, review_requested]
pull_request_review_comment:
types: [created]
permissions: {}
# Deduplicate simultaneous pull_request events for the same fork PR.
# When reviewers are requested at the same time, GitHub fires multiple
# review_requested events. Without this group each event triggers a
# separate review via workflow_run, producing duplicate reviews.
concurrency:
group: pr-review-trigger-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
save-context:
if: github.event.pull_request.head.repo.fork
runs-on: ubuntu-latest
steps:
- name: Save event context
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
COMMENT_JSON: ${{ toJSON(github.event.comment) }}
run: |
mkdir -p context
printf '%s' "${{ github.event_name }}" > context/event_name.txt
printf '%s' "$PR_NUMBER" > context/pr_number.txt
printf '%s' "$PR_HEAD_SHA" > context/pr_head_sha.txt
if [ "${{ github.event_name }}" = "pull_request_review_comment" ]; then
printf '%s' "$COMMENT_JSON" > context/comment.json
fi
- name: Upload context
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: pr-review-context
path: context/
retention-days: 1
+23
View File
@@ -0,0 +1,23 @@
name: PR Review
on:
issue_comment:
types: [created]
workflow_run:
workflows: ["PR Review - Trigger"]
types: [completed]
permissions:
contents: read
jobs:
review:
uses: docker/docker-agent-action/.github/workflows/review-pr.yml@baf90543d81f5de59751dfd10e6cf45e21a5a982 # v2.0.3
permissions:
contents: read # Read repository files and PR diffs
pull-requests: write # Post review comments
issues: write # Create security incident issues if secrets detected
checks: write # (Optional) Show review progress as a check run
id-token: write # Required for OIDC authentication to AWS Secrets Manager
actions: read # Download artifacts from trigger workflow
with:
trigger-run-id: ${{ github.event_name == 'workflow_run' && format('{0}', github.event.workflow_run.id) || '' }}
+157
View File
@@ -0,0 +1,157 @@
name: Sync Docker release branch
concurrency:
group: ${{ github.workflow }}-${{ inputs.release_branch }}
cancel-in-progress: false
permissions:
contents: read
on:
workflow_dispatch:
inputs:
release_branch:
description: Release branch to sync, for example 29.x
required: true
type: string
tag:
description: Tag to sync from, for example v29.6.0
required: true
type: string
dry_run:
description: Merge but don't push
required: true
default: false
type: boolean
jobs:
sync-release-branch:
runs-on: ubuntu-24.04
permissions:
contents: write
actions: write
outputs:
base_sha: ${{ steps.sync.outputs.base_sha }}
has_changes: ${{ steps.sync.outputs.has_changes }}
temporary_branch: ${{ steps.sync.outputs.temporary_branch }}
temporary_sha: ${{ steps.sync.outputs.temporary_sha }}
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Validate
env:
BRANCH: ${{ github.ref_name }}
RELEASE_BRANCH: ${{ inputs.release_branch }}
run: |
if [ "$BRANCH" != "master" ]; then
echo "::error::This workflow is expected to be run on master, not $BRANCH"
exit 1
fi
if [ "$RELEASE_BRANCH" = "master" ]; then
echo "::error::The release branch must not be master"
exit 1
fi
if ! [[ "$RELEASE_BRANCH" =~ ^[0-9]+\.[x0-9]+$ ]]; then
echo "::error::Invalid release branch name: '$RELEASE_BRANCH'. Expected format: 29.x"
exit 1
fi
- name: Configure git author
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Sync release branch to tag range
id: sync
env:
DRY_RUN: ${{ inputs.dry_run }}
RELEASE_BRANCH: ${{ inputs.release_branch }}
RUN_ATTEMPT: ${{ github.run_attempt }}
RUN_ID: ${{ github.run_id }}
TAG: ${{ inputs.tag }}
run: |
set -o pipefail
base_sha=$(git rev-parse "origin/$RELEASE_BRANCH")
temporary_branch="process/sync-release-branch/$RUN_ID-$RUN_ATTEMPT"
echo "base_sha=$base_sha" >> "$GITHUB_OUTPUT"
echo "temporary_branch=$temporary_branch" >> "$GITHUB_OUTPUT"
# Keep the master checkout unchanged so scripts run from the dispatched revision.
release_worktree="$RUNNER_TEMP/release-branch"
git worktree add --detach "$release_worktree" "$base_sha"
cd "$release_worktree"
tags_file=$(mktemp)
"$GITHUB_WORKSPACE/scripts/unmerged-tags" \
"origin/$RELEASE_BRANCH" \
"$TAG" \
> "$tags_file"
echo >> "$GITHUB_STEP_SUMMARY"
echo "## Tags to sync" >> "$GITHUB_STEP_SUMMARY"
echo >> "$GITHUB_STEP_SUMMARY"
sed 's/^/- /' "$tags_file" >> "$GITHUB_STEP_SUMMARY"
xargs -r "$GITHUB_WORKSPACE/scripts/sync-branch" < "$tags_file" | tee -a "$GITHUB_STEP_SUMMARY"
if [[ "$DRY_RUN" == "true" ]]; then
echo "has_changes=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if [[ $(git rev-parse HEAD) == $(git rev-parse "origin/$RELEASE_BRANCH") ]]; then
echo "has_changes=false" >> "$GITHUB_OUTPUT"
echo "No changes to push"
exit 0
fi
echo "has_changes=true" >> "$GITHUB_OUTPUT"
git push origin "HEAD:refs/heads/$temporary_branch"
echo "temporary_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
push-release-branch:
needs: sync-release-branch
if: ${{ !inputs.dry_run && needs.sync-release-branch.outputs.has_changes == 'true' }}
runs-on: ubuntu-24.04
environment: docker-releases
permissions:
contents: write
actions: write
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Push release branch
env:
BASE_SHA: ${{ needs.sync-release-branch.outputs.base_sha }}
RELEASE_BRANCH: ${{ inputs.release_branch }}
TEMPORARY_BRANCH: ${{ needs.sync-release-branch.outputs.temporary_branch }}
TEMPORARY_SHA: ${{ needs.sync-release-branch.outputs.temporary_sha }}
run: |
git fetch origin "$RELEASE_BRANCH"
current_sha=$(git rev-parse "origin/$RELEASE_BRANCH")
if [[ "$current_sha" != "$BASE_SHA" ]]; then
echo "$RELEASE_BRANCH changed from $BASE_SHA to $current_sha"
exit 1
fi
git fetch origin "$TEMPORARY_BRANCH"
current_temporary_sha=$(git rev-parse FETCH_HEAD)
if [[ "$current_temporary_sha" != "$TEMPORARY_SHA" ]]; then
echo "$TEMPORARY_BRANCH changed from $TEMPORARY_SHA to $current_temporary_sha"
exit 1
fi
git push origin "FETCH_HEAD:$RELEASE_BRANCH"
- name: Delete temporary branch
env:
TEMPORARY_BRANCH: ${{ needs.sync-release-branch.outputs.temporary_branch }}
run: git push origin --delete "$TEMPORARY_BRANCH"
+8 -7
View File
@@ -30,15 +30,15 @@ jobs:
steps:
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
-
name: Test
uses: docker/bake-action@a66e1c87e2eca0503c343edf1d208c716d54b8a8 # v7.1.0
uses: docker/bake-action@d3418bd7d0e9324001bca92fa8ba175ea7e6dc9b # v7.3.0
with:
targets: test-coverage
-
name: Send to Codecov
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v5
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
with:
files: ./build/coverage/coverage.txt
token: ${{ secrets.CODECOV_TOKEN }}
@@ -60,14 +60,15 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: ${{ env.GOPATH }}/src/github.com/docker/cli
persist-credentials: false
-
name: Set up Go
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.26.3"
go-version: "1.26.7"
cache: false
-
name: Test
@@ -81,7 +82,7 @@ jobs:
shell: bash
-
name: Send to Codecov
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v5
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
with:
files: /tmp/coverage.txt
working-directory: ${{ env.GOPATH }}/src/github.com/docker/cli
+11 -6
View File
@@ -38,7 +38,7 @@ jobs:
steps:
-
name: Run
uses: docker/bake-action@a66e1c87e2eca0503c343edf1d208c716d54b8a8 # v7.1.0
uses: docker/bake-action@d3418bd7d0e9324001bca92fa8ba175ea7e6dc9b # v7.3.0
with:
targets: ${{ matrix.target }}
@@ -48,7 +48,9 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
-
name: Generate
shell: 'script --return --quiet --command "bash {0}"'
@@ -74,7 +76,9 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
-
name: Run
shell: 'script --return --quiet --command "bash {0}"'
@@ -89,14 +93,15 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: src/github.com/docker/cli
persist-credentials: false
-
name: Set up Go
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.26.3"
go-version: "1.26.7"
cache: false
-
name: Run gocompat check
+31
View File
@@ -0,0 +1,31 @@
name: zizmor
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
on:
workflow_dispatch:
push:
branches:
- 'main'
- 'master'
- '[0-9]+.[0-9]+'
- '[0-9]+.x'
tags:
- 'v*'
pull_request:
jobs:
run:
uses: crazy-max/.github/.github/workflows/zizmor.yml@46267a6e61cd56aac2fc79943df180152f4c89d6 # v1.10.1
permissions:
contents: read
security-events: write
with:
min-severity: medium
min-confidence: medium
persona: pedantic
+1 -1
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.26.3"
go: "1.26.7"
timeout: 5m
+6 -1
View File
@@ -39,6 +39,7 @@ Alexander Larsson <alexl@redhat.com> <alexander.larsson@gmail.com>
Alexander Morozov <lk4d4math@gmail.com>
Alexander Morozov <lk4d4math@gmail.com> <lk4d4@docker.com>
Alexandre Beslic <alexandre.beslic@gmail.com> <abronan@docker.com>
Alexandre Levavasseur <alexandre+oss@13x.fr>
Alexis Couvreur <alexiscouvreur.pro@gmail.com>
Alicia Lauerman <alicia@eta.im> <allydevour@me.com>
Allen Sun <allensun.shl@alibaba-inc.com> <allen.sun@daocloud.io>
@@ -354,9 +355,10 @@ 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>
Lukas Michael <lukas.23022005@gmail.com>
Luke Marsden <me@lukemarsden.net> <luke@digital-crocus.com>
Luo Jiyin <luojiyin@hotmail.com>
Lyn <energylyn@zju.edu.cn>
Lynda O'Leary <lyndaoleary29@gmail.com>
Lynda O'Leary <lyndaoleary29@gmail.com> <lyndaoleary@hotmail.com>
@@ -403,6 +405,7 @@ Michael Huettermann <michael@huettermann.net>
Michael Käufl <docker@c.michael-kaeufl.de> <michael-k@users.noreply.github.com>
Michael Spetsiotis <michael_spets@hotmail.com>
Michal Minář <miminar@redhat.com>
Mickael Emirkanian <mickael.emirkanian@docker.com>
Miguel Angel Alvarez Cabrerizo <doncicuto@gmail.com> <30386061+doncicuto@users.noreply.github.com>
Miguel Angel Fernández <elmendalerenda@gmail.com>
Mihai Borobocea <MihaiBorob@gmail.com> <MihaiBorobocea@gmail.com>
@@ -572,6 +575,8 @@ Ulysses Souza <ulysses.souza@docker.com>
Ulysses Souza <ulysses.souza@docker.com> <ulyssessouza@gmail.com>
Umesh Yadav <umesh4257@gmail.com>
Umesh Yadav <umesh4257@gmail.com> <dungeonmaster18@users.noreply.github.com>
Varun Hotani <varunhotani@gmail.com>
Vibhu Anan <vibhuanand@outlook.com>
Victor Lyuboslavsky <victor@victoreda.com>
Victor Vieux <victor.vieux@docker.com> <dev@vvieux.com>
Victor Vieux <victor.vieux@docker.com> <victor.vieux@dotcloud.com>
+15 -1
View File
@@ -43,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 Levavasseur <alexandre+oss@13x.fr>
Alexandre Vallières-Lagacé <alexandre.valliereslagace@docker.com>
Alexey Igrychev <alexey.igrychev@flant.com>
Alexis Couvreur <alexiscouvreur.pro@gmail.com>
@@ -161,6 +162,7 @@ Chen Chuanliang <chen.chuanliang@zte.com.cn>
Chen Hanxiao <chenhanxiao@cn.fujitsu.com>
Chen Mingjie <chenmingjie0828@163.com>
Chen Qiu <cheney-90@hotmail.com>
Ching Wei Kang <164879897+WilliamK112@users.noreply.github.com>
Chris Chinchilla <chris@chrischinchilla.com>
Chris Couzens <ccouzens@gmail.com>
Chris Gavin <chris@chrisgavin.me>
@@ -366,7 +368,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>
@@ -552,6 +554,7 @@ Ludovic Temgoua Abanda <abandaludovic500@gmail.com>
Luis Henrique Mulinari <luis.mulinari@gmail.com>
Luka Hartwig <mail@lukahartwig.de>
Lukas Heeren <lukas-heeren@hotmail.com>
Lukas Michael <lukas.23022005@gmail.com>
Lukasz Zajaczkowski <Lukasz.Zajaczkowski@ts.fujitsu.com>
Luo Jiyin <luojiyin@hotmail.com>
Lydell Manganti <LydellManganti@users.noreply.github.com>
@@ -562,6 +565,7 @@ Maciej Kalisz <maciej.d.kalisz@gmail.com>
Madhav Puri <madhav.puri@gmail.com>
Madhu Venugopal <madhu@socketplane.io>
Madhur Batra <madhurbatra097@gmail.com>
Mahesh Thakur <maheshthakur9152@gmail.com>
Malte Janduda <mail@janduda.net>
Manjunath A Kumatagi <mkumatag@in.ibm.com>
Mansi Nahar <mmn4185@rit.edu>
@@ -589,10 +593,12 @@ Mathieu Rollet <matletix@gmail.com>
Matt Gucci <matt9ucci@gmail.com>
Matt Robenolt <matt@ydekproductions.com>
Matteo Orefice <matteo.orefice@bites4bits.software>
Matteo Panzeri <matteo1782@gmail.com>
Matthew Heon <mheon@redhat.com>
Matthieu Hauglustaine <matt.hauglustaine@gmail.com>
Matthieu MOREL <matthieu.morel35@gmail.com>
Mauro Porras P <mauroporrasp@gmail.com>
Max Morozov <gtmax.yo@gmail.com>
Max Shytikov <mshytikov@gmail.com>
Max-Julian Pogner <max-julian@pogner.at>
Maxime Petazzoni <max@signalfuse.com>
@@ -617,6 +623,7 @@ Michael West <mwest@mdsol.com>
Michael Zampani <michael.zampani@docker.com>
Michal Minář <miminar@redhat.com>
Michał Czeraszkiewicz <czerasz@gmail.com>
Mickael Emirkanian <mickael.emirkanian@docker.com>
Miguel Angel Alvarez Cabrerizo <doncicuto@gmail.com>
Mihai Borobocea <MihaiBorob@gmail.com>
Mihuleacc Sergiu <mihuleac.sergiu@gmail.com>
@@ -638,6 +645,7 @@ Mohammad Banikazemi <mb@us.ibm.com>
Mohammad Hossein <mhm98035@gmail.com>
Mohammed Aaqib Ansari <maaquib@gmail.com>
Mohammed Aminu Futa <mohammedfuta2000@gmail.com>
Mohammed Thaha <mohammedthahacse@gmail.com>
Mohini Anne Dsouza <mohini3917@gmail.com>
Moorthy RS <rsmoorthy@gmail.com>
Morgan Bauer <mbauer@us.ibm.com>
@@ -689,6 +697,7 @@ Olli Janatuinen <olli.janatuinen@gmail.com>
Oscar Wieman <oscrx@icloud.com>
Otto Kekäläinen <otto@seravo.fi>
Ovidio Mallo <ovidio.mallo@gmail.com>
Park Jaeon <me@finalchild.dev>
Pascal Borreli <pascal@borreli.com>
Patrick Böänziger <patrick.baenziger@bsi-software.com>
Patrick Daigle <114765035+pdaig@users.noreply.github.com>
@@ -715,6 +724,7 @@ Peter Jaffe <pjaffe@nevo.com>
Peter Kehl <peter.kehl@gmail.com>
Peter Nagy <xificurC@gmail.com>
Peter Salvatore <peter@psftw.com>
Peter Valdemar Mørch <peter@morch.com>
Peter Waller <p@pwaller.net>
Phil Estes <estesp@gmail.com>
Philip Alexander Etling <paetling@gmail.com>
@@ -739,6 +749,7 @@ Ray Tsang <rayt@google.com>
Reficul <xuzhenglun@gmail.com>
Remy Suen <remy.suen@gmail.com>
Renaud Gaubert <rgaubert@nvidia.com>
René Hermenau <rene-hermenau@users.noreply.github.com>
Ricardo N Feliciano <FelicianoTech@gmail.com>
Rich Moyse <rich@moyse.us>
Richard Chen Zheng <58443436+rchenzheng@users.noreply.github.com>
@@ -789,6 +800,7 @@ Scott Collier <emailscottcollier@gmail.com>
Sean Christopherson <sean.j.christopherson@intel.com>
Sean Rodman <srodman7689@gmail.com>
Sebastiaan van Stijn <github@gone.nl>
Seiya Miyata <odradek38@gmail.com>
Sergey Tryuber <Sergeant007@users.noreply.github.com>
Serhat Gülçiçek <serhat25@gmail.com>
Sevki Hasirci <s@sevki.org>
@@ -889,8 +901,10 @@ Umesh Yadav <umesh4257@gmail.com>
Vaclav Struhar <struharv@gmail.com>
Valentin Lorentz <progval+git@progval.net>
Vardan Pogosian <vardan.pogosyan@gmail.com>
Varun Hotani <varunhotani@gmail.com>
Venkateswara Reddy Bukkasamudram <bukkasamudram@outlook.com>
Veres Lajos <vlajos@gmail.com>
Vibhu Anan <vibhuanand@outlook.com>
Victor Vieux <victor.vieux@docker.com>
Victoria Bialas <victoria.bialas@docker.com>
Viktor Stanchev <me@viktorstanchev.com>
+1 -1
View File
@@ -267,7 +267,7 @@ Then you just add a line to every git commit message:
Signed-off-by: Joe Smith <joe.smith@email.com>
Use your real name (sorry, no pseudonyms or anonymous contributions.)
Use a known identity (sorry, no anonymous contributions.)
If you set your `user.name` and `user.email` git configs, you can sign your
commit automatically with `git commit -s`.
+1 -1
View File
@@ -8,7 +8,7 @@ ARG BASE_VARIANT=alpine
ARG ALPINE_VERSION=3.23
ARG BASE_DEBIAN_DISTRO=bookworm
ARG GO_VERSION=1.26.3
ARG GO_VERSION=1.26.7
# 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.
-1
View File
@@ -3,7 +3,6 @@
[![PkgGoDev](https://pkg.go.dev/badge/github.com/docker/cli)](https://pkg.go.dev/github.com/docker/cli)
[![Build Status](https://img.shields.io/github/actions/workflow/status/docker/cli/build.yml?branch=master&label=build&logo=github)](https://github.com/docker/cli/actions?query=workflow%3Abuild)
[![Test Status](https://img.shields.io/github/actions/workflow/status/docker/cli/test.yml?branch=master&label=test&logo=github)](https://github.com/docker/cli/actions?query=workflow%3Atest)
[![Go Report Card](https://goreportcard.com/badge/github.com/docker/cli)](https://goreportcard.com/report/github.com/docker/cli)
[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/docker/cli/badge)](https://scorecard.dev/viewer/?uri=github.com/docker/cli)
[![Codecov](https://img.shields.io/codecov/c/github/docker/cli?logo=codecov)](https://codecov.io/gh/docker/cli)
+1 -1
View File
@@ -1 +1 @@
29.5.2
29.8.0
+33 -15
View File
@@ -208,52 +208,70 @@ func TestInitializeFromClient(t *testing.T) {
// Makes sure we don't hang forever on the initial connection.
// https://github.com/docker/cli/issues/3652
func TestInitializeFromClientHangs(t *testing.T) {
const (
// Sized against measured scheduler stalls:
// under CPU pressure this test sees 40-90ms stalls;
// this should give about 5x headroom.
// See https://github.com/docker/cli/issues/6003.
clientInitTimeout = 500 * time.Millisecond
// This is only a backstop against a genuine hang.
// It should never be reached on a healthy run.
// So, it should be fine to have a lenient timeout here.
waitTimeout = 10 * time.Second
)
tmpDir := t.TempDir()
socket := filepath.Join(tmpDir, "my.sock")
l, err := net.Listen("unix", socket)
assert.NilError(t, err)
receiveReqCh := make(chan bool)
timeoutCtx, cancel := context.WithTimeout(context.TODO(), time.Second)
defer cancel()
// Buffered, so the handler can record
// that it was reached without a reader
// having to be ready at that instant.
receivedReqCh := make(chan struct{}, 1)
releaseHandlerCh := make(chan struct{})
// Simulate a server that hangs on connections.
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
select {
case <-timeoutCtx.Done():
case receiveReqCh <- true: // Blocks until someone receives on the channel.
case receivedReqCh <- struct{}{}:
default:
}
_, _ = w.Write([]byte("OK"))
<-releaseHandlerCh
}))
ts.Listener = l
ts.Start()
defer ts.Close()
t.Cleanup(func() {
close(releaseHandlerCh)
ts.Close()
})
opts := &flags.ClientOptions{Hosts: []string{"unix://" + socket}}
configFile := &configfile.ConfigFile{}
apiClient, err := NewAPIClientFromFlags(opts, configFile)
assert.NilError(t, err)
initializedCh := make(chan bool)
initErrCh := make(chan error, 1)
go func() {
cli := &DockerCli{client: apiClient, initTimeout: time.Millisecond}
cli := &DockerCli{client: apiClient, initTimeout: clientInitTimeout}
err := cli.Initialize(flags.NewClientOptions())
assert.Check(t, err)
cli.CurrentVersion()
close(initializedCh)
initErrCh <- err
}()
select {
case <-timeoutCtx.Done():
case err := <-initErrCh:
assert.Check(t, err)
case <-time.After(waitTimeout):
t.Fatal("timeout waiting for initialization to complete")
case <-initializedCh:
}
select {
case <-timeoutCtx.Done():
case <-receivedReqCh:
case <-time.After(waitTimeout):
t.Fatal("server never received an init request")
case <-receiveReqCh:
}
}
+22
View File
@@ -172,6 +172,28 @@ func FromList(options ...string) cobra.CompletionFunc {
return Unique(cobra.FixedCompletions(options, cobra.ShellCompDirectiveNoFileComp))
}
// WithPrefix prefixes every element in the slice with the given prefix.
// It is a helper for building "--filter" completions, where each candidate
// value is offered as "key=value".
func WithPrefix(prefix string, values []string) []string {
result := make([]string, len(values))
for i, v := range values {
result[i] = prefix + v
}
return result
}
// WithSuffix appends the given suffix to every element in the slice. It is a
// helper for building "--filter" completions, where filter keys are offered
// with a trailing "=" (combined with [cobra.ShellCompDirectiveNoSpace]).
func WithSuffix(suffix string, values []string) []string {
result := make([]string, len(values))
for i, v := range values {
result[i] = v + suffix
}
return result
}
// FileNames is a convenience function to use [cobra.ShellCompDirectiveDefault],
// which indicates to let the shell perform its default behavior after
// completions have been provided.
+12
View File
@@ -196,6 +196,18 @@ func TestCompleteFromList(t *testing.T) {
assert.Check(t, is.DeepEqual(values, expected))
}
func TestWithPrefix(t *testing.T) {
assert.Check(t, is.DeepEqual(WithPrefix("node=", []string{"n1", "n2"}), []string{"node=n1", "node=n2"}))
assert.Check(t, is.DeepEqual(WithPrefix("node=", []string{}), []string{}))
assert.Check(t, is.DeepEqual(WithPrefix("", []string{"n1"}), []string{"n1"}))
}
func TestWithSuffix(t *testing.T) {
assert.Check(t, is.DeepEqual(WithSuffix("=", []string{"id", "name"}), []string{"id=", "name="}))
assert.Check(t, is.DeepEqual(WithSuffix("=", []string{}), []string{}))
assert.Check(t, is.DeepEqual(WithSuffix("", []string{"id"}), []string{"id"}))
}
func TestCompleteImageNames(t *testing.T) {
tests := []struct {
doc string
+1 -1
View File
@@ -71,7 +71,7 @@ func newCreateCommand(dockerCLI command.Cli) *cobra.Command {
flags.SetInterspersed(false)
flags.StringVar(&options.name, "name", "", "Assign a name to the container")
flags.StringVar(&options.pull, "pull", PullImageMissing, `Pull image before creating ("`+PullImageAlways+`", "|`+PullImageMissing+`", "`+PullImageNever+`")`)
flags.StringVar(&options.pull, "pull", PullImageMissing, `Pull image before creating ("`+PullImageAlways+`", "`+PullImageMissing+`", "`+PullImageNever+`")`)
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Suppress the pull output")
flags.BoolVarP(&options.useAPISocket, "use-api-socket", "", false, "Bind mount Docker API socket and required auth")
_ = flags.SetAnnotation("use-api-socket", "experimentalCLI", nil) // Mark flag as experimental for now.
-1
View File
@@ -88,7 +88,6 @@ func runRm(ctx context.Context, dockerCLI command.Cli, opts *rmOptions) error {
for _, name := range opts.containers {
if err := <-errChan; err != nil {
if opts.force && errdefs.IsNotFound(err) {
_, _ = fmt.Fprintln(dockerCLI.Err(), err)
continue
}
errs = append(errs, err)
+1
View File
@@ -52,6 +52,7 @@ func TestRemoveForce(t *testing.T) {
} else {
assert.NilError(t, err)
}
assert.Equal(t, cli.ErrBuffer().String(), "")
sort.Strings(removed)
assert.DeepEqual(t, removed, []string{"mycontainer", "nosuchcontainer"})
})
+1 -1
View File
@@ -458,7 +458,7 @@ func comparePorts(i, j container.PortSummary) bool {
}
if i.IP != j.IP {
return i.IP.String() < j.IP.String()
return i.IP.Less(j.IP)
}
if i.PublicPort != j.PublicPort {
+18
View File
@@ -946,6 +946,24 @@ func TestDisplayablePorts(t *testing.T) {
},
expected: "80/tcp, 80/udp, 1024/tcp, 1024/udp, 12345/sctp, 1.1.1.1:1024->80/tcp, 1.1.1.1:1024->80/udp, 2.1.1.1:1024->80/tcp, 2.1.1.1:1024->80/udp, 1.1.1.1:80->1024/tcp, 1.1.1.1:80->1024/udp, 2.1.1.1:80->1024/tcp, 2.1.1.1:80->1024/udp", //nolint:revive // ignore line-length-limit (revive)
},
{
// host IPs are ordered numerically, not lexicographically:
// "10.0.0.2" sorts as a string before "9.0.0.1".
ports: []container.PortSummary{
{
IP: netip.MustParseAddr("10.0.0.2"),
PublicPort: 8080,
PrivatePort: 80,
Type: "tcp",
}, {
IP: netip.MustParseAddr("9.0.0.1"),
PublicPort: 8081,
PrivatePort: 80,
Type: "tcp",
},
},
expected: "9.0.0.1:8081->80/tcp, 10.0.0.2:8080->80/tcp",
},
}
for _, port := range cases {
+8 -9
View File
@@ -123,6 +123,7 @@ To push the complete multi-platform image, remove the --platform flag.
return err
}
var notes []string
defer func() {
_ = responseBody.Close()
for _, note := range notes {
@@ -131,18 +132,16 @@ To push the complete multi-platform image, remove the --platform flag.
}()
if opts.quiet {
err = jsonstream.Display(ctx, responseBody, streams.NewOut(io.Discard), jsonstream.WithAuxCallback(handleAux()))
err = jsonstream.Display(ctx, responseBody, streams.NewOut(io.Discard), jsonstream.WithAuxCallback(handleAux(&notes, out)))
if err == nil {
_, _ = fmt.Fprintln(dockerCli.Out(), ref.String())
}
return err
}
return jsonstream.Display(ctx, responseBody, dockerCli.Out(), jsonstream.WithAuxCallback(handleAux()))
return jsonstream.Display(ctx, responseBody, dockerCli.Out(), jsonstream.WithAuxCallback(handleAux(&notes, out)))
}
var notes []string
func handleAux() func(jm jsonstream.JSONMessage) {
func handleAux(notes *[]string, out tui.Output) func(jm jsonstream.JSONMessage) {
return func(jm jsonstream.JSONMessage) {
b := []byte(*jm.Aux)
@@ -150,10 +149,10 @@ func handleAux() func(jm jsonstream.JSONMessage) {
err := json.Unmarshal(b, &stripped)
if err == nil && stripped.ManifestPushedInsteadOfIndex {
note := fmt.Sprintf("Not all multiplatform-content is present and only the available single-platform image was pushed\n%s -> %s",
aec.RedF.Apply(stripped.OriginalIndex.Digest.String()),
aec.GreenF.Apply(stripped.SelectedManifest.Digest.String()),
out.Color(aec.RedF).Apply(stripped.OriginalIndex.Digest.String()),
out.Color(aec.GreenF).Apply(stripped.SelectedManifest.Digest.String()),
)
notes = append(notes, note)
*notes = append(*notes, note)
}
var missing auxprogress.ContentMissing
@@ -166,7 +165,7 @@ func handleAux() func(jm jsonstream.JSONMessage) {
Make sure you have all the referenced content and try again.
You can also push only a single platform specific manifest directly by specifying the platform you want to push with the --platform flag.`
notes = append(notes, note)
*notes = append(*notes, note)
}
}
}
+30
View File
@@ -1,13 +1,18 @@
package image
import (
"bytes"
"encoding/json"
"errors"
"io"
"net/http"
"strings"
"testing"
"github.com/docker/cli/internal/test"
"github.com/moby/moby/api/types/auxprogress"
"github.com/moby/moby/client"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"gotest.tools/v3/assert"
)
@@ -86,3 +91,28 @@ func TestNewPushCommandSuccess(t *testing.T) {
})
}
}
func TestRunPushRespectsNoColorForAuxNotes(t *testing.T) {
t.Setenv("NO_COLOR", "1")
cli := test.NewFakeCli(&fakeClient{
imagePushFunc: func(ref string, options client.ImagePushOptions) (client.ImagePushResponse, error) {
aux, err := json.Marshal(auxprogress.ManifestPushedInsteadOfIndex{
ManifestPushedInsteadOfIndex: true,
OriginalIndex: ocispec.Descriptor{Digest: "sha256:1111111111111111111111111111111111111111111111111111111111111111"},
SelectedManifest: ocispec.Descriptor{Digest: "sha256:2222222222222222222222222222222222222222222222222222222222222222"},
})
assert.NilError(t, err)
line := append([]byte(`{"aux":`), aux...)
line = append(line, '}', '\n')
return fakeStreamResult{ReadCloser: io.NopCloser(bytes.NewReader(line))}, nil
},
})
cli.Out().SetIsTerminal(true)
err := runPush(t.Context(), cli, pushOptions{remote: "image:tag"})
assert.NilError(t, err)
out := cli.OutBuffer().String()
assert.Assert(t, strings.Contains(out, "sha256:1111111111111111111111111111111111111111111111111111111111111111 -> sha256:2222222222222222222222222222222222222222222222222222222222222222"))
assert.Assert(t, !strings.Contains(out, "\x1b["), "output should not contain ANSI escape codes, output: %s", out)
}
+1 -23
View File
@@ -48,27 +48,6 @@ func newManifestStore(dockerCLI command.Cli) store.Store {
return store.NewStore(filepath.Join(config.Dir(), "manifests"))
}
// authConfigKey is the key used to store credentials for Docker Hub. It is
// a copy of [registry.IndexServer].
//
// [registry.IndexServer]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/registry#IndexServer
const authConfigKey = "https://index.docker.io/v1/"
// 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
}
// newRegistryClient returns a client for communicating with a Docker distribution
// registry
func newRegistryClient(dockerCLI command.Cli, allowInsecure bool) registryclient.RegistryClient {
@@ -78,8 +57,7 @@ func newRegistryClient(dockerCLI command.Cli, allowInsecure bool) registryclient
}
cfg := dockerCLI.ConfigFile()
resolver := func(ctx context.Context, domainName string) registry.AuthConfig {
configKey := getAuthConfigKey(domainName)
a, _ := cfg.GetAuthConfig(configKey)
a, _ := cfg.GetAuthConfig(domainName)
return registry.AuthConfig{
Username: a.Username,
Password: a.Password,
+34
View File
@@ -2,12 +2,26 @@ package node
import (
"os"
"strings"
"github.com/docker/cli/cli/command/completion"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
var (
// nodePsFilters are the filters that can be used with "docker node ps --filter".
nodePsFilters = []string{"desired-state", "id", "label", "name"}
// taskDesiredStates are the valid values for the "desired-state" task filter.
taskDesiredStates = []string{
string(swarm.TaskStateRunning),
string(swarm.TaskStateShutdown),
string(swarm.TaskStateAccepted),
}
)
// completeNodeNames offers completion for swarm node (host)names and optional IDs.
// By default, only names are returned.
// Set DOCKER_COMPLETION_SHOW_NODE_IDS=yes to also complete IDs.
@@ -35,3 +49,23 @@ func completeNodeNames(dockerCLI completion.APIClientProvider) cobra.CompletionF
return names, cobra.ShellCompDirectiveNoFileComp
}
}
// completeNodePsFilters provides completion for the filters that can be used
// with "docker node ps --filter".
func completeNodePsFilters(_ completion.APIClientProvider) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
key, _, ok := strings.Cut(toComplete, "=")
if !ok {
return completion.WithSuffix("=", nodePsFilters), cobra.ShellCompDirectiveNoSpace
}
switch key {
case "desired-state":
return completion.WithPrefix("desired-state=", taskDesiredStates), cobra.ShellCompDirectiveNoFileComp
case "id", "name", "label":
// Task IDs, names, and labels are not easily discoverable; only offer the key.
return nil, cobra.ShellCompDirectiveNoFileComp
default:
return completion.WithSuffix("=", nodePsFilters), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
}
}
}
+52
View File
@@ -0,0 +1,52 @@
package node
import (
"testing"
"github.com/docker/cli/internal/test"
"github.com/spf13/cobra"
"gotest.tools/v3/assert"
)
func TestCompleteNodePsFilters(t *testing.T) {
tests := []struct {
doc string
toComplete string
expected []string
directive cobra.ShellCompDirective
}{
{
doc: "no input offers the filter keys",
toComplete: "",
expected: []string{"desired-state=", "id=", "label=", "name="},
directive: cobra.ShellCompDirectiveNoSpace,
},
{
doc: "desired-state values",
toComplete: "desired-state=",
expected: []string{"desired-state=running", "desired-state=shutdown", "desired-state=accepted"},
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "label offers no values",
toComplete: "label=",
expected: nil,
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "unknown key falls back to the filter keys",
toComplete: "bogus=",
expected: []string{"desired-state=", "id=", "label=", "name="},
directive: cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp,
},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{})
completions, directive := completeNodePsFilters(cli)(newPsCommand(cli), nil, tc.toComplete)
assert.DeepEqual(t, completions, tc.expected)
assert.Equal(t, directive, tc.directive)
})
}
}
+2
View File
@@ -48,6 +48,8 @@ func newPsCommand(dockerCLI command.Cli) *cobra.Command {
flags.StringVar(&options.format, "format", "", "Pretty-print tasks using a Go template")
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Only display task IDs")
_ = cmd.RegisterFlagCompletionFunc("filter", completeNodePsFilters(dockerCLI))
return cmd
}
+12 -7
View File
@@ -97,17 +97,22 @@ func GetDefaultAuthConfig(cfg *configfile.ConfigFile, checkCredStore bool, serve
// If defaultUsername is not empty, the username prompt includes that username
// and the user can hit enter without inputting a username to use that default
// username.
func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword, defaultUsername, serverAddress string) (registrytypes.AuthConfig, error) {
func PromptUserForCredentials(ctx context.Context, cli Streams, argUser, argPassword, defaultUsername, serverAddress string) (registrytypes.AuthConfig, error) {
// On Windows, force the use of the regular OS stdin stream.
//
// StdStreams() may wrap stdin with windowsconsole.NewAnsiReader to
// emulate VT input on consoles that do not support it natively, but
// that wrapper has historically caused interactive prompts to hang
// or behave incorrectly.
//
// See:
// - https://github.com/moby/moby/issues/14336
// - https://github.com/moby/moby/issues/14210
// - https://github.com/moby/moby/pull/17738
//
// TODO(thaJeztah): we need to confirm if this special handling is still needed, as we may not be doing this in other places.
stdIn := cli.In()
if runtime.GOOS == "windows" {
cli.SetIn(streams.NewIn(os.Stdin))
// TODO(thaJeztah); change to io.Reader and skip wrapping once prompt.DisableInputEcho no longer requires a streams.In
stdIn = streams.NewIn(os.Stdin)
}
argUser = strings.TrimSpace(argUser)
@@ -132,7 +137,7 @@ func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword
}
var err error
argUser, err = prompt.ReadInput(ctx, cli.In(), cli.Out(), msg)
argUser, err = prompt.ReadInput(ctx, stdIn, cli.Out(), msg)
if err != nil {
return registrytypes.AuthConfig{}, err
}
@@ -146,7 +151,7 @@ func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword
isEmpty := strings.TrimSpace(argPassword) == ""
if isEmpty {
restoreInput, err := prompt.DisableInputEcho(cli.In())
restoreInput, err := prompt.DisableInputEcho(stdIn)
if err != nil {
return registrytypes.AuthConfig{}, err
}
@@ -166,7 +171,7 @@ func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword
"To create a PAT, visit " + aec.Underline.Apply("https://app.docker.com/settings") + "\n\n")
}
argPassword, err = prompt.ReadInput(ctx, cli.In(), cli.Out(), "Password: ")
argPassword, err = prompt.ReadInput(ctx, stdIn, cli.Out(), "Password: ")
if err != nil {
return registrytypes.AuthConfig{}, err
}
+8 -3
View File
@@ -62,7 +62,7 @@ func newLoginCommand(dockerCLI command.Cli) *cobra.Command {
flags := cmd.Flags()
flags.StringVarP(&opts.user, "username", "u", "", "Username")
flags.StringVarP(&opts.password, "password", "p", "", "Password or Personal Access Token (PAT)")
flags.StringVarP(&opts.password, "password", "p", "", `Password or Personal Access Token (PAT), or "-" to read from stdin`)
flags.BoolVar(&opts.passwordStdin, "password-stdin", false, "Take the Password or Personal Access Token (PAT) from stdin")
return cmd
@@ -72,8 +72,8 @@ func newLoginCommand(dockerCLI command.Cli) *cobra.Command {
//
// TODO(thaJeztah); combine with verifyLoginOptions, but this requires rewrites of many tests.
func verifyLoginFlags(flags *pflag.FlagSet, opts loginOptions) error {
if flags.Changed("password-stdin") {
if flags.Changed("password") {
if flags.Changed("password-stdin") || opts.password == "-" {
if flags.Changed("password") && opts.password != "-" {
return errors.New("conflicting options: cannot specify both --password and --password-stdin")
}
if !flags.Changed("username") {
@@ -122,6 +122,11 @@ func readSecretFromStdin(r io.Reader) (string, error) {
}
func verifyLoginOptions(dockerCLI command.Streams, opts *loginOptions) error {
if opts.password == "-" {
opts.password = ""
opts.passwordStdin = true
}
if opts.password != "" {
_, _ = fmt.Fprintln(dockerCLI.Err(), "WARNING! Using --password via the CLI is insecure. Use --password-stdin.")
}
+74 -1
View File
@@ -339,6 +339,57 @@ func TestRunLogin(t *testing.T) {
},
},
},
{
doc: "password dash reads password from stdin",
priorCredentials: map[string]configtypes.AuthConfig{},
stdIn: "my password\r\n",
input: loginOptions{
serverAddress: "reg1",
user: "my-username",
password: "-",
},
expectedCredentials: map[string]configtypes.AuthConfig{
"reg1": {
Username: "my-username",
Password: "my password",
ServerAddress: "reg1",
},
},
},
{
doc: "password dash empty stdin",
priorCredentials: map[string]configtypes.AuthConfig{},
input: loginOptions{
serverAddress: "reg1",
user: "my-username",
password: "-",
},
expectedErr: `password is empty`,
expectedCredentials: map[string]configtypes.AuthConfig{
"reg1": {
Username: "my-username",
ServerAddress: "reg1",
},
},
},
{
doc: "password dash and password stdin read password from stdin",
priorCredentials: map[string]configtypes.AuthConfig{},
stdIn: "my password\r\n",
input: loginOptions{
serverAddress: "reg1",
user: "my-username",
password: "-",
passwordStdin: true,
},
expectedCredentials: map[string]configtypes.AuthConfig{
"reg1": {
Username: "my-username",
Password: "my password",
ServerAddress: "reg1",
},
},
},
{
doc: "password with leading and trailing spaces",
priorCredentials: map[string]configtypes.AuthConfig{},
@@ -397,7 +448,7 @@ func TestRunLogin(t *testing.T) {
cfg := configfile.New(filepath.Join(tmpDir, "config.json"))
cli := test.NewFakeCli(&fakeClient{})
cli.SetConfigFile(cfg)
if tc.input.passwordStdin {
if tc.input.passwordStdin || tc.input.password == "-" || tc.stdIn != "" {
if tc.expectedErr == "TEST_READ_ERR" {
cli.SetIn(streams.NewIn(io.NopCloser(iotest.ErrReader(errors.New(tc.expectedErr)))))
} else {
@@ -632,6 +683,21 @@ func TestLoginValidateFlags(t *testing.T) {
args: []string{"--password-stdin", "--password", ""},
expectedErr: `conflicting options: cannot specify both --password and --password-stdin`,
},
{
name: "password stdin and password dash without stdin",
args: []string{"--password-stdin", "--username", "my-username", "--password", "-"},
expectedErr: `password is empty`,
},
{
name: "password dash without username",
args: []string{"--password", "-"},
expectedErr: `the --password-stdin option requires --username to be set`,
},
{
name: "short password dash without username",
args: []string{"-p", "-"},
expectedErr: `the --password-stdin option requires --username to be set`,
},
{
name: "empty --password",
args: []string{"--password", ""},
@@ -658,3 +724,10 @@ func TestLoginValidateFlags(t *testing.T) {
})
}
}
func TestLoginHelpDocumentsPasswordDash(t *testing.T) {
cmd := newLoginCommand(test.NewFakeCli(&fakeClient{}))
flag := cmd.Flags().Lookup("password")
assert.Check(t, flag != nil)
assert.Check(t, is.Contains(flag.Usage, `"-"`))
}
+91
View File
@@ -2,12 +2,32 @@ package service
import (
"os"
"strings"
"github.com/docker/cli/cli/command/completion"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
var (
// serviceListFilters are the filters that can be used with "docker service ls --filter".
serviceListFilters = []string{"id", "label", "mode", "name"}
// serviceModes are the valid values for the "mode" filter of "docker service ls".
serviceModes = []string{"replicated", "global", "replicated-job", "global-job"}
// servicePsFilters are the filters that can be used with "docker service ps --filter".
servicePsFilters = []string{"desired-state", "id", "name", "node"}
// taskDesiredStates are the valid values for the "desired-state" task filter.
taskDesiredStates = []string{
string(swarm.TaskStateRunning),
string(swarm.TaskStateShutdown),
string(swarm.TaskStateAccepted),
}
)
// completeServiceNames offers completion for swarm service names and optional IDs.
// By default, only names are returned.
// Set DOCKER_COMPLETION_SHOW_SERVICE_IDS=yes to also complete IDs.
@@ -31,3 +51,74 @@ func completeServiceNames(dockerCLI completion.APIClientProvider) cobra.Completi
return names, cobra.ShellCompDirectiveNoFileComp
}
}
// completeServiceListFilters provides completion for the filters that can be
// used with "docker service ls --filter".
func completeServiceListFilters(dockerCLI completion.APIClientProvider) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
key, _, ok := strings.Cut(toComplete, "=")
if !ok {
return completion.WithSuffix("=", serviceListFilters), cobra.ShellCompDirectiveNoSpace
}
switch key {
case "id", "name":
return completion.WithPrefix(key+"=", serviceNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp
case "mode":
return completion.WithPrefix("mode=", serviceModes), cobra.ShellCompDirectiveNoFileComp
case "label":
return nil, cobra.ShellCompDirectiveNoFileComp
default:
return completion.WithSuffix("=", serviceListFilters), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
}
}
}
// completeServicePsFilters provides completion for the filters that can be
// used with "docker service ps --filter".
func completeServicePsFilters(dockerCLI completion.APIClientProvider) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
key, _, ok := strings.Cut(toComplete, "=")
if !ok {
return completion.WithSuffix("=", servicePsFilters), cobra.ShellCompDirectiveNoSpace
}
switch key {
case "desired-state":
return completion.WithPrefix("desired-state=", taskDesiredStates), cobra.ShellCompDirectiveNoFileComp
case "node":
return completion.WithPrefix("node=", nodeNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp
case "id", "name":
// Task IDs and names are not easily discoverable; only offer the key.
return nil, cobra.ShellCompDirectiveNoFileComp
default:
return completion.WithSuffix("=", servicePsFilters), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
}
}
}
// serviceNames contacts the API to get a list of service names.
// In case of an error, an empty list is returned.
func serviceNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
res, err := dockerCLI.Client().ServiceList(cmd.Context(), client.ServiceListOptions{})
if err != nil {
return []string{}
}
names := make([]string, 0, len(res.Items))
for _, service := range res.Items {
names = append(names, service.Spec.Name)
}
return names
}
// nodeNames contacts the API to get a list of node (host)names.
// In case of an error, an empty list is returned.
func nodeNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
res, err := dockerCLI.Client().NodeList(cmd.Context(), client.NodeListOptions{})
if err != nil {
return []string{}
}
names := make([]string, 0, len(res.Items))
for _, node := range res.Items {
names = append(names, node.Description.Hostname)
}
return names
}
+156
View File
@@ -0,0 +1,156 @@
package service
import (
"context"
"errors"
"testing"
"github.com/docker/cli/internal/test"
"github.com/docker/cli/internal/test/builders"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
"gotest.tools/v3/assert"
)
func TestCompleteServicePsFilters(t *testing.T) {
tests := []struct {
doc string
client *fakeClient
toComplete string
expected []string
directive cobra.ShellCompDirective
}{
{
doc: "no input offers the filter keys",
toComplete: "",
expected: []string{"desired-state=", "id=", "name=", "node="},
directive: cobra.ShellCompDirectiveNoSpace,
},
{
doc: "desired-state values",
toComplete: "desired-state=",
expected: []string{"desired-state=running", "desired-state=shutdown", "desired-state=accepted"},
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "node values",
client: &fakeClient{
nodeListFunc: func(_ context.Context, _ client.NodeListOptions) (client.NodeListResult, error) {
return client.NodeListResult{
Items: []swarm.Node{
*builders.Node(builders.Hostname("n1")),
*builders.Node(builders.Hostname("n2")),
},
}, nil
},
},
toComplete: "node=",
expected: []string{"node=n1", "node=n2"},
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "node values on API error",
client: &fakeClient{
nodeListFunc: func(_ context.Context, _ client.NodeListOptions) (client.NodeListResult, error) {
return client.NodeListResult{}, errors.New("API error")
},
},
toComplete: "node=",
expected: []string{},
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "id offers no values",
toComplete: "id=",
expected: nil,
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "unknown key falls back to the filter keys",
toComplete: "bogus=",
expected: []string{"desired-state=", "id=", "name=", "node="},
directive: cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp,
},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
cli := test.NewFakeCli(tc.client)
completions, directive := completeServicePsFilters(cli)(newPsCommand(cli), nil, tc.toComplete)
assert.DeepEqual(t, completions, tc.expected)
assert.Equal(t, directive, tc.directive)
})
}
}
func TestCompleteServiceListFilters(t *testing.T) {
tests := []struct {
doc string
client *fakeClient
toComplete string
expected []string
directive cobra.ShellCompDirective
}{
{
doc: "no input offers the filter keys",
toComplete: "",
expected: []string{"id=", "label=", "mode=", "name="},
directive: cobra.ShellCompDirectiveNoSpace,
},
{
doc: "mode values",
toComplete: "mode=",
expected: []string{"mode=replicated", "mode=global", "mode=replicated-job", "mode=global-job"},
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "name values",
client: &fakeClient{
serviceListFunc: func(_ context.Context, _ client.ServiceListOptions) (client.ServiceListResult, error) {
return client.ServiceListResult{
Items: []swarm.Service{
*builders.Service(builders.ServiceName("s1")),
*builders.Service(builders.ServiceName("s2")),
},
}, nil
},
},
toComplete: "name=",
expected: []string{"name=s1", "name=s2"},
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "name values on API error",
client: &fakeClient{
serviceListFunc: func(_ context.Context, _ client.ServiceListOptions) (client.ServiceListResult, error) {
return client.ServiceListResult{}, errors.New("API error")
},
},
toComplete: "name=",
expected: []string{},
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "label offers no values",
toComplete: "label=",
expected: nil,
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "unknown key falls back to the filter keys",
toComplete: "bogus=",
expected: []string{"id=", "label=", "mode=", "name="},
directive: cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp,
},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
cli := test.NewFakeCli(tc.client)
completions, directive := completeServiceListFilters(cli)(newListCommand(cli), nil, tc.toComplete)
assert.DeepEqual(t, completions, tc.expected)
assert.Equal(t, directive, tc.directive)
})
}
}
+2
View File
@@ -38,6 +38,8 @@ func newListCommand(dockerCLI command.Cli) *cobra.Command {
flags.StringVar(&options.format, "format", "", flagsHelper.FormatHelp)
flags.VarP(&options.filter, "filter", "f", "Filter output based on conditions provided")
_ = cmd.RegisterFlagCompletionFunc("filter", completeServiceListFilters(dockerCLI))
return cmd
}
+7 -8
View File
@@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"net/netip"
"slices"
"sort"
"strconv"
"strings"
@@ -687,15 +688,13 @@ func (options *serviceOptions) makeEnv() ([]string, error) {
}
currentEnv := make([]string, 0, len(envVariables))
for _, env := range envVariables { // need to process each var, in order
k, _, _ := strings.Cut(env, "=")
for i, current := range currentEnv { // remove duplicates
if current == env {
continue // no update required, may hide this behind flag to preserve order of envVariables
}
if strings.HasPrefix(current, k+"=") {
currentEnv = append(currentEnv[:i], currentEnv[i+1:]...)
}
if slices.Contains(currentEnv, env) {
continue // no update required, may hide this behind flag to preserve order of envVariables
}
k, _, _ := strings.Cut(env, "=")
currentEnv = slices.DeleteFunc(currentEnv, func(current string) bool { // remove duplicates
return strings.HasPrefix(current, k+"=")
})
currentEnv = append(currentEnv, env)
}
+46
View File
@@ -373,3 +373,49 @@ func TestToServiceSysCtls(t *testing.T) {
assert.NilError(t, err)
assert.Check(t, is.DeepEqual(service.TaskTemplate.ContainerSpec.Sysctls, expected))
}
func TestMakeEnv(t *testing.T) {
tests := []struct {
doc string
env []string
expected []string
}{
{
doc: "no duplicates",
env: []string{"one=1", "two=2"},
expected: []string{"one=1", "two=2"},
},
{
doc: "same variable repeated",
env: []string{"one=1", "one=1"},
expected: []string{"one=1"},
},
{
doc: "same variable repeated, then overridden",
env: []string{"one=1", "one=1", "one=2"},
expected: []string{"one=2"},
},
{
doc: "repeated variable last",
env: []string{"one=1", "two=2", "two=2"},
expected: []string{"one=1", "two=2"},
},
{
doc: "last value wins",
env: []string{"one=1", "two=2", "one=3"},
expected: []string{"two=2", "one=3"},
},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
o := newServiceOptions()
for _, env := range tc.env {
assert.NilError(t, o.env.Set(env))
}
actual, err := o.makeEnv()
assert.NilError(t, err)
assert.Check(t, is.DeepEqual(tc.expected, actual))
})
}
}
+2
View File
@@ -45,6 +45,8 @@ func newPsCommand(dockerCLI command.Cli) *cobra.Command {
flags.StringVar(&options.format, "format", "", "Pretty-print tasks using a Go template")
flags.VarP(&options.filter, "filter", "f", "Filter output based on conditions provided")
_ = cmd.RegisterFlagCompletionFunc("filter", completeServicePsFilters(dockerCLI))
return cmd
}
+3 -5
View File
@@ -1213,11 +1213,9 @@ func updateHosts(flags *pflag.FlagSet, hosts *[]string) error {
if rm.IPAddr != "" && rm.IPAddr != ip {
continue
}
for i, h := range hostNames {
if h == rm.Host {
hostNames = append(hostNames[:i], hostNames[i+1:]...)
}
}
hostNames = slices.DeleteFunc(hostNames, func(h string) bool {
return h == rm.Host
})
}
if len(hostNames) > 0 {
newHosts = append(newHosts, fmt.Sprintf("%s %s", ip, strings.Join(hostNames, " ")))
+15
View File
@@ -1727,3 +1727,18 @@ func TestUpdateUlimits(t *testing.T) {
})
}
}
func TestUpdateHostsRemoveRepeatedHost(t *testing.T) {
flags := newUpdateCommand(nil).Flags()
flags.Set("host-rm", "host1")
//nolint:dupword // ignore "Duplicate words (host1) found"
hosts := []string{"127.0.0.1 host1 host1 host2", "127.0.0.2 host2 host1 host1"}
err := updateHosts(flags, &hosts)
assert.NilError(t, err)
// All occurrences of `host1` should be removed, also if the same host
// is listed multiple times in the same entry.
assert.Check(t, is.DeepEqual([]string{"127.0.0.1 host2", "127.0.0.2 host2"}, hosts))
}
+42 -1
View File
@@ -104,7 +104,7 @@ func (c *fileStore) Store(authConfig types.AuthConfig) error {
// stored as hostname or as hostname including scheme (in legacy configuration
// files).
//
// It's the equivalent to [registry.ConvertToHostname] in the daemon.
// It's based on [registry.ConvertToHostname] from Moby daemon.
//
// [registry.ConvertToHostname]: https://pkg.go.dev/github.com/moby/moby/v2@v2.0.0-beta.7/daemon/pkg/registry#ConvertToHostname
func ConvertToHostname(maybeURL string) string {
@@ -117,7 +117,48 @@ func ConvertToHostname(maybeURL string) string {
}
return net.JoinHostPort(u.Hostname(), u.Port())
}
if hostName := hostFromURLFallback(stripped); hostName != "" {
return hostName
}
}
hostName, _, _ := strings.Cut(stripped, "/")
return hostName
}
// hostFromURLFallback extracts a host from scheme URLs that net/url rejects.
// Go rejects unbracketed IPv6 literals in URL hosts since
// https://github.com/golang/go/commit/0c28789bd7dfc55099cac86a3212dda0d6c091f6
func hostFromURLFallback(maybeURL string) string {
_, rest, ok := strings.Cut(maybeURL, "://")
if !ok {
return ""
}
hostName, _, _ := strings.Cut(rest, "/")
if hostName == "" {
return ""
}
if strings.Count(hostName, ":") > 1 && !strings.HasPrefix(hostName, "[") {
portStart := strings.LastIndex(hostName, ":")
addr, port := hostName[:portStart], hostName[portStart+1:]
if addr != "" && isPort(port) {
return net.JoinHostPort(addr, port)
}
}
return hostName
}
func isPort(port string) bool {
if port == "" {
return false
}
for _, r := range port {
if r < '0' || r > '9' {
return false
}
}
return true
}
+5 -5
View File
@@ -65,11 +65,11 @@ require (
go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect
go.opentelemetry.io/otel/trace v1.43.0 // indirect
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
golang.org/x/crypto v0.50.0 // indirect
golang.org/x/net v0.53.0 // indirect
golang.org/x/sys v0.43.0 // indirect
golang.org/x/term v0.42.0 // indirect
golang.org/x/text v0.36.0 // indirect
golang.org/x/crypto v0.52.0 // indirect
golang.org/x/net v0.54.0 // indirect
golang.org/x/sys v0.45.0 // indirect
golang.org/x/term v0.43.0 // indirect
golang.org/x/text v0.37.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/grpc v1.80.0 // indirect
+10 -10
View File
@@ -265,14 +265,14 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -285,14 +285,14 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
+4 -3
View File
@@ -8,6 +8,7 @@
DOCKER_CLI_MOUNTS ?= -v "$(CURDIR)":/go/src/github.com/docker/cli
DOCKER_CLI_CONTAINER_NAME ?=
DOCKER_CLI_GO_BUILD_CACHE ?= y
DOCKER_SOCK ?= $(or $(patsubst unix://%,%,$(filter unix://%,$(shell docker context inspect --format '{{.Endpoints.docker.Host}}'))),/var/run/docker.sock)
# Sets the name of the company that produced the windows binary.
PACKAGER_NAME ?=
@@ -62,7 +63,7 @@ dynbinary: ## build dynamically linked binary
.PHONY: dev
dev: build_docker_image ## start a build container in interactive mode for in-container development
$(DOCKER_RUN) -it \
--mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \
--mount 'type=bind,src=$(DOCKER_SOCK),dst=/var/run/docker.sock' \
$(DEV_DOCKER_IMAGE_NAME)
shell: dev ## alias for dev
@@ -134,14 +135,14 @@ test-e2e: test-e2e-local test-e2e-connhelper-ssh ## run all e2e tests
test-e2e-local: build-e2e-image # run experimental e2e tests
docker run --rm $(ENVVARS) \
--mount type=bind,src=$(CURDIR)/build/coverage,dst=/tmp/coverage \
--mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \
--mount 'type=bind,src=$(DOCKER_SOCK),dst=/var/run/docker.sock' \
$(E2E_IMAGE_NAME)
.PHONY: test-e2e-connhelper-ssh
test-e2e-connhelper-ssh: build-e2e-image # run experimental SSH-connection helper e2e tests
docker run --rm $(ENVVARS) -e TEST_CONNHELPER=ssh \
--mount type=bind,src=$(CURDIR)/build/coverage,dst=/tmp/coverage \
--mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \
--mount 'type=bind,src=$(DOCKER_SOCK),dst=/var/run/docker.sock' \
$(E2E_IMAGE_NAME)
.PHONY: help
+1 -1
View File
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.26.3
ARG GO_VERSION=1.26.7
# ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image.
# It must be a supported tag in the docker.io/library/alpine image repository
+1 -1
View File
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.26.3
ARG GO_VERSION=1.26.7
# ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image.
# It must be a supported tag in the docker.io/library/alpine image repository
+1 -1
View File
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.26.3
ARG GO_VERSION=1.26.7
# ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image.
# It must be a supported tag in the docker.io/library/alpine image repository
+14
View File
@@ -113,6 +113,7 @@ The following table provides an overview of the current status of deprecated fea
| Deprecated | [`-h` shorthand for `--help`](#-h-shorthand-for---help) | v1.12 | v17.09 |
| Removed | [`-e` and `--email` flags on `docker login`](#-e-and---email-flags-on-docker-login) | v1.11 | v17.06 |
| Deprecated | [Separator (`:`) of `--security-opt` flag on `docker run`](#separator--of---security-opt-flag-on-docker-run) | v1.11 | v17.06 |
| Deprecated | [Links on the default bridge network](#links-on-the-default-bridge-network) | v1.10 | - |
| Deprecated | [Ambiguous event fields in API](#ambiguous-event-fields-in-api) | v1.10 | - |
| Removed | [`-f` flag on `docker tag`](#-f-flag-on-docker-tag) | v1.10 | v1.12 |
| Removed | [HostConfig at API container start](#hostconfig-at-api-container-start) | v1.10 | v1.12 |
@@ -1203,6 +1204,19 @@ The `docker login` no longer automatically registers an account with the target
The flag `--security-opt` doesn't use the colon separator (`:`) anymore to divide keys and values, it uses the equal symbol (`=`) for consistency with other similar flags, like `--storage-opt`.
### Links on the default bridge network
**Deprecated in release: v1.10**
**Target for removal in release: v30.0**
The `--link` option on `docker create` and `docker run`, when used with no
`--network` specified, was deprecated in v1.10 and will be removed in a future
release. Custom networks should be used instead. Docker 29.6 added a deprecation
warning when this option is used for the default bridge network.
Note that the `--link` option is still supported when a non-default network
is used.
### Ambiguous event fields in API
**Deprecated in release: [v1.10.0](https://github.com/docker/docker/releases/tag/v1.10.0)**
+26 -16
View File
@@ -74,11 +74,14 @@ The sequence diagrams below depict an allow and deny authorization flow:
Each request sent to the plugin includes the authenticated user, the HTTP
headers, and the request/response body. Only the user name and the
authentication method used are passed to the plugin. Most importantly, no user
credentials or tokens are passed. Finally, not all request/response bodies
are sent to the authorization plugin. Only request/response bodies where
the `Content-Type` is `application/json` are sent to the authorization plugin;
bodies of any other `Content-Type` are not visible to the plugin and cannot
be used for enforcement, even though the daemon may still act on this data.
credentials or tokens are passed.
> [!NOTE]
> Authorization plugins enforce requests to the Docker daemon's HTTP API only. gRPC method
> calls, whether dispatched natively or upgraded through `POST /grpc`, are not subject to authorization.
> Furthermore, HTTP request/response bodies where the `Content-Type` is `application/json` are forwarded;
> bodies of any other type are not visible to the plugin and cannot be used for enforcement,
> even though the daemon acts on this data.
For commands that can potentially hijack the HTTP connection (`HTTP
Upgrade`), such as `exec`, the authorization plugin is only called for the
@@ -88,6 +91,22 @@ passed to the authorization plugins. For commands that return chunked HTTP
response, such as `logs` and `events`, only the HTTP request is sent to the
authorization plugins.
The Engine's authorization middleware fails closed: when a plugin returns an error or returns `Allow: false`,
the request is denied and the error is surfaced to the client. Plugins should also fail closed: if the plugin
cannot confidently evaluate a request, it should return an error or `Allow: false`.
> [!WARNING]
> Because the plugin receives the [**raw** request body](#authzpluginauthzreq) from the daemon, it must
> apply the same decoding semantics as the daemon to be sure it evaluates the request the daemon will
> act on. The daemon decodes JSON with Go's [`encoding/json.Unmarshal`](https://pkg.go.dev/encoding/json#Unmarshal).
>
> The same requirement applies to the response body. Plugins that depend on `ResponseBody`
> inspection for redaction or content-filtering should restrict their policies to endpoints
> whose response is produced as a single write (typical of REST-style API responses). For
> commands whose responses are streamed or are likely to exceed the [buffer](#response-body-size-and-partial-buffering) through multiple
> writes, do not rely on `ResponseBody` for security-relevant decisions; perform the filtering
> in a separate layer in front of the daemon.
### Response body size and partial buffering
The internal buffer that holds the response body between the daemon's HTTP
@@ -104,15 +123,6 @@ is the practical effect of this 64 KiB threshold combined with the
is immediately drained to the client and is therefore no longer available
for plugin inspection by the time the handler returns.
> [!NOTE]
> Plugins that depend on `ResponseBody` inspection for redaction or
> content-filtering should restrict their policies to endpoints whose
> response is produced as a single write (typical of REST-style API
> responses). For commands whose responses are streamed or are likely to
> exceed the buffer through multiple writes, do not rely on `ResponseBody`
> for security-relevant decisions; perform the filtering in a separate
> layer in front of the daemon.
During request/response processing, some authorization flows might
need to do additional queries to the Docker daemon. To complete such flows,
plugins can call the daemon API similar to a regular user. To enable these
@@ -236,7 +246,7 @@ Name | Type | Description
User | string | The user identification
Authentication method | string | The authentication method used
Request method | enum | The HTTP method (GET/DELETE/POST)
Request URI | string | The HTTP request URI including API version (e.g., v.1.17/containers/json)
Request URI | string | The HTTP request URI including API version, as sent by the client (e.g., v.1.17/containers/json)
Request headers | map[string]string | Request headers as key value pairs (without the authorization header)
Request body | []byte | Raw request body
@@ -259,7 +269,7 @@ Name | Type | Description
User | string | The user identification
Authentication method | string | The authentication method used
Request method | string | The HTTP method (GET/DELETE/POST)
Request URI | string | The HTTP request URI including API version (e.g., v.1.17/containers/json)
Request URI | string | The HTTP request URI including API version, as sent by the client (e.g., v.1.17/containers/json)
Request headers | map[string]string | Request headers as key value pairs (without the authorization header)
Request body | []byte | Raw request body
Response status code | int | Status code from the Docker daemon
@@ -87,7 +87,7 @@ Create a new container
| `--privileged` | `bool` | | Give extended privileges to this container |
| `-p`, `--publish` | `list` | | Publish a container's port(s) to the host |
| `-P`, `--publish-all` | `bool` | | Publish all exposed ports to random ports |
| `--pull` | `string` | `missing` | Pull image before creating (`always`, `\|missing`, `never`) |
| `--pull` | `string` | `missing` | Pull image before creating (`always`, `missing`, `never`) |
| `-q`, `--quiet` | `bool` | | Suppress the pull output |
| `--read-only` | `bool` | | Mount the container's root filesystem as read only |
| `--restart` | `string` | `no` | Restart policy to apply when a container exits |
+21 -9
View File
@@ -9,14 +9,14 @@ Fetch the logs of a container
### Options
| Name | Type | Default | Description |
|:---------------------|:---------|:--------|:---------------------------------------------------------------------------------------------------|
| `--details` | `bool` | | Show extra details provided to logs |
| `-f`, `--follow` | `bool` | | Follow log output |
| `--since` | `string` | | Show logs since timestamp (e.g. `2013-01-02T13:23:37Z`) or relative (e.g. `42m` for 42 minutes) |
| `-n`, `--tail` | `string` | `all` | Number of lines to show from the end of the logs |
| `-t`, `--timestamps` | `bool` | | Show timestamps |
| [`--until`](#until) | `string` | | Show logs before a timestamp (e.g. `2013-01-02T13:23:37Z`) or relative (e.g. `42m` for 42 minutes) |
| Name | Type | Default | Description |
|:---------------------------------------------------|:---------|:--------|:---------------------------------------------------------------------------------------------------|
| [`--details`](#details) | `bool` | | Show extra details provided to logs |
| [`-f`](#follow), [`--follow`](#follow) | `bool` | | Follow log output |
| [`--since`](#since) | `string` | | Show logs since timestamp (e.g. `2013-01-02T13:23:37Z`) or relative (e.g. `42m` for 42 minutes) |
| [`-n`](#tail), [`--tail`](#tail) | `string` | `all` | Number of lines to show from the end of the logs |
| [`-t`](#timestamps), [`--timestamps`](#timestamps) | `bool` | | Show timestamps |
| [`--until`](#until) | `string` | | Show logs before a timestamp (e.g. `2013-01-02T13:23:37Z`) or relative (e.g. `42m` for 42 minutes) |
<!---MARKER_GEN_END-->
@@ -28,21 +28,34 @@ The `docker logs` command batch-retrieves logs present at the time of execution.
For more information about selecting and configuring logging drivers, refer to
[Configure logging drivers](https://docs.docker.com/engine/logging/configure/).
## Examples
### <a name="follow"></a> Stream log output (-f, --follow)
The `docker logs --follow` command will continue streaming the new output from
the container's `STDOUT` and `STDERR`.
### <a name="tail"></a> Retrieve the last logs (-n, --tail)
Passing a negative number or a non-integer to `--tail` is invalid and the
value is set to `all` in that case.
### <a name="timestamps"></a> Retrieve logs with timestamps (-t, --timestamps)
The `docker logs --timestamps` command will add an [RFC3339Nano timestamp](https://pkg.go.dev/time#RFC3339Nano)
, for example `2014-09-16T06:17:46.000000000Z`, to each
log entry. To ensure that the timestamps are aligned the
nano-second part of the timestamp will be padded with zero when necessary.
### <a name="details"></a> Retrieve logs with additional attributes (--details)
The `docker logs --details` command will add on extra attributes, such as
environment variables and labels, provided to `--log-opt` when creating the
container.
### <a name="since"></a> Retrieve logs generated since a specific point in time (--since)
The `--since` option shows only the container logs generated after
a given date. You can specify the date as an RFC 3339 date, a UNIX
timestamp, or a Go duration string (e.g. `1m30s`, `3h`). Besides RFC3339 date
@@ -56,7 +69,6 @@ seconds (aka Unix epoch or Unix time), and the optional .nanoseconds field is a
fraction of a second no more than nine digits long. You can combine the
`--since` option with either or both of the `--follow` or `--tail` options.
## Examples
### <a name="until"></a> Retrieve logs until a specific point in time (--until)
+1 -1
View File
@@ -87,7 +87,7 @@ Create a new container
| `--privileged` | `bool` | | Give extended privileges to this container |
| `-p`, `--publish` | `list` | | Publish a container's port(s) to the host |
| `-P`, `--publish-all` | `bool` | | Publish all exposed ports to random ports |
| `--pull` | `string` | `missing` | Pull image before creating (`always`, `\|missing`, `never`) |
| `--pull` | `string` | `missing` | Pull image before creating (`always`, `missing`, `never`) |
| `-q`, `--quiet` | `bool` | | Suppress the pull output |
| `--read-only` | `bool` | | Mount the container's root filesystem as read only |
| `--restart` | `string` | `no` | Restart policy to apply when a container exits |
+1 -1
View File
@@ -431,7 +431,7 @@ a `docker` command. You can use the following protocols:
| Scheme | Description | Example |
|----------------------------------------|---------------------------|----------------------------------|
| `unix://[<path>]` | Unix socket (Linux only) | `unix:///var/run/docker.sock` |
| `unix://[<path>]` | Unix socket | `unix:///var/run/docker.sock` |
| `tcp://[<IP or host>[:port]]` | TCP connection | `tcp://174.17.0.1:2376` |
| `ssh://[username@]<IP or host>[:port]` | SSH connection | `ssh://user@192.168.64.5` |
| `npipe://[<name>]` | Named pipe (Windows only) | `npipe:////./pipe/docker_engine` |
+12 -5
View File
@@ -6,11 +6,11 @@ Defaults to Docker Hub if no server is specified.
### Options
| Name | Type | Default | Description |
|:---------------------------------------------|:---------|:--------|:------------------------------------------------------------|
| `-p`, `--password` | `string` | | Password or Personal Access Token (PAT) |
| [`--password-stdin`](#password-stdin) | `bool` | | Take the Password or Personal Access Token (PAT) from stdin |
| [`-u`](#username), [`--username`](#username) | `string` | | Username |
| Name | Type | Default | Description |
|:---------------------------------------------|:---------|:--------|:-------------------------------------------------------------------|
| `-p`, `--password` | `string` | | Password or Personal Access Token (PAT), or `-` to read from stdin |
| [`--password-stdin`](#password-stdin) | `bool` | | Take the Password or Personal Access Token (PAT) from stdin |
| [`-u`](#username), [`--username`](#username) | `string` | | Username |
<!---MARKER_GEN_END-->
@@ -244,6 +244,13 @@ The following example reads a password from a file, and passes it to the
$ cat ~/my_password.txt | docker login --username foo --password-stdin
```
You can also pass `-` as the value for `--password` or `-p` to read the
password from `STDIN`.
```console
$ cat ~/my_password.txt | docker login --username foo --password -
```
## Related commands
* [logout](logout.md)
+5
View File
@@ -0,0 +1,5 @@
# Generated by gen-certs.sh at setup time
testdata/registry/certs/ca.crt
testdata/registry/certs/ca.key
testdata/registry/certs/tlsregistry.crt
testdata/registry/certs/tlsregistry.key
+28 -2
View File
@@ -3,9 +3,35 @@ services:
registry:
image: 'registry:3'
privateregistry:
build:
context: ./testdata/registry
environment:
- REGISTRY_HTTP_ADDR=0.0.0.0:5001
- REGISTRY_HTTP_DEBUG_ADDR=0.0.0.0:5002
- REGISTRY_AUTH=htpasswd
- REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm
- REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd
tlsregistry:
build:
context: ./testdata/registry
environment:
- REGISTRY_HTTP_ADDR=0.0.0.0:5003
- REGISTRY_HTTP_DEBUG_ADDR=0.0.0.0:5004
- REGISTRY_AUTH=htpasswd
- REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm
- REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd
- REGISTRY_HTTP_TLS_CERTIFICATE=/certs/tlsregistry.crt
- REGISTRY_HTTP_TLS_KEY=/certs/tlsregistry.key
engine:
image: 'docker:${ENGINE_VERSION:-29}-dind'
build:
context: ./testdata
dockerfile: engine/Dockerfile
args:
ENGINE_VERSION: ${ENGINE_VERSION:-29}
privileged: true
command: ['--insecure-registry=registry:5000', '--experimental']
command: ['--insecure-registry=registry:5000', '--insecure-registry=privateregistry:5001', '--experimental']
environment:
- DOCKER_TLS_CERTDIR=
+5 -1
View File
@@ -209,7 +209,11 @@ func TestProcessTermination(t *testing.T) {
assert.NilError(t, result.Cmd.Process.Signal(syscall.SIGTERM))
icmd.WaitOnCmd(time.Second*10, result).Assert(t, icmd.Expected{
// Use a generous timeout (20s) because when run through SSH connhelper,
// the Docker engine may take longer to close the attach stream after
// the container exits. This is a known timing difference across engine
// versions (e.g. engine 25 over SSH connhelper).
icmd.WaitOnCmd(time.Second*20, result).Assert(t, icmd.Expected{
ExitCode: 0,
})
}
+126
View File
@@ -0,0 +1,126 @@
package image
import (
"strings"
"testing"
"time"
"github.com/docker/cli/e2e/internal/fixtures"
"gotest.tools/v3/assert"
"gotest.tools/v3/icmd"
)
// Regression test for https://github.com/docker/cli/issues/5963
func TestPullPushPrivateRepository(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
name string
registryPrefix string
tagSuffix string
}{
{name: "insecure", registryPrefix: "privateregistry:5001", tagSuffix: "private"},
{name: "tls", registryPrefix: "tlsregistry:5003", tagSuffix: "tls"},
} {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
dir := fixtures.SetupConfigFile(t)
t.Cleanup(dir.Remove)
emptyConfigDir := t.TempDir()
sourceImage := fixtures.AlpineImage
privateImage := tc.registryPrefix + "/private/alpine:test-" + tc.tagSuffix + "-pull-push"
runWithPrivateRegistryRetry(t,
icmd.Command("docker", "pull", sourceImage),
).Assert(t, icmd.Success)
t.Cleanup(func() {
icmd.RunCommand("docker", "image", "rm", "-f", privateImage).Assert(t, icmd.Success)
})
icmd.RunCommand("docker", "tag", sourceImage, privateImage).Assert(t, icmd.Success)
pushNoAuth := runWithPrivateRegistryRetry(t,
icmd.Command("docker", "push", privateImage),
fixtures.WithConfig(emptyConfigDir),
)
pushNoAuth.Assert(t, icmd.Expected{ExitCode: 1})
assertAuthDenied(t, pushNoAuth)
pushWithAuth := runWithPrivateRegistryRetry(t,
icmd.Command("docker", "push", privateImage),
fixtures.WithConfig(dir.Path()),
)
pushWithAuth.Assert(t, icmd.Success)
// Docker omits the tag in the "push refers to repository" line; strip it before asserting.
privateRepo := privateImage[:strings.LastIndex(privateImage, ":")]
assert.Check(t, strings.Contains(pushWithAuth.Combined(), "The push refers to repository ["+privateRepo+"]"), pushWithAuth.Combined())
icmd.RunCommand("docker", "image", "rm", "-f", privateImage).Assert(t, icmd.Success)
pullNoAuth := runWithPrivateRegistryRetry(t,
icmd.Command("docker", "pull", privateImage),
fixtures.WithConfig(emptyConfigDir),
)
pullNoAuth.Assert(t, icmd.Expected{ExitCode: 1})
assertAuthDenied(t, pullNoAuth)
pullWithAuth := runWithPrivateRegistryRetry(t,
icmd.Command("docker", "pull", privateImage),
fixtures.WithConfig(dir.Path()),
)
pullWithAuth.Assert(t, icmd.Success)
assert.Check(t, strings.Contains(pullWithAuth.Combined(), privateImage), pullWithAuth.Combined())
})
}
}
func assertAuthDenied(t *testing.T, result *icmd.Result) {
t.Helper()
output := result.Combined()
if isPrivateRegistryTransient(output) {
t.Fatalf("private registry unavailable while expecting auth failure: %s", output)
}
assert.Assert(t,
strings.Contains(output, "requested access to the resource is denied") ||
strings.Contains(output, "no basic auth credentials") ||
strings.Contains(output, "unauthorized") ||
strings.Contains(output, "authentication required"),
output,
)
}
func runWithPrivateRegistryRetry(t *testing.T, cmd icmd.Cmd, opts ...icmd.CmdOp) *icmd.Result {
t.Helper()
deadline := time.Now().Add(90 * time.Second)
for {
result := icmd.RunCmd(cmd, opts...)
output := result.Combined()
if isPrivateRegistryTransient(output) {
if time.Now().Before(deadline) {
t.Logf("waiting for private registry availability: %s", output)
time.Sleep(500 * time.Millisecond)
continue
}
}
return result
}
}
func isPrivateRegistryTransient(output string) bool {
return strings.Contains(output, "lookup privateregistry") ||
strings.Contains(output, "lookup tlsregistry") ||
strings.Contains(output, "lookup registry") ||
strings.Contains(output, "no such host") ||
strings.Contains(output, "server misbehaving") ||
strings.Contains(output, "Temporary failure in name resolution") ||
strings.Contains(output, "connection refused") ||
strings.Contains(output, "i/o timeout") ||
strings.Contains(output, "TLS handshake timeout") ||
strings.Contains(output, "context deadline exceeded") ||
strings.Contains(output, "connection reset by peer") ||
strings.Contains(output, "unexpected EOF")
}
+6
View File
@@ -23,6 +23,12 @@ func SetupConfigFile(t *testing.T) fs.Dir {
"auths": {
"registry:5000": {
"auth": "ZWlhaXM6cGFzc3dvcmQK"
},
"privateregistry:5001": {
"auth": "ZTJlOnBhc3N3b3Jk"
},
"tlsregistry:5003": {
"auth": "ZTJlOnBhc3N3b3Jk"
}
}}`), fs.WithDir("trust", fs.WithDir("private")))
return *dir
+3
View File
@@ -12,6 +12,9 @@ RUN apk --no-cache add openssl openssh-client openssh-server shadow && \
useradd --create-home --shell /bin/sh --password $(head -c32 /dev/urandom | base64) penguin && \
usermod -aG docker penguin && \
ssh-keygen -A
# Trust the tlsregistry CA so dockerd connects without --insecure-registry.
COPY registry/certs/ca.crt /usr/local/share/ca-certificates/tlsregistry-ca.crt
RUN update-ca-certificates
# workaround: ssh session excludes /usr/local/bin from $PATH
RUN ln -s /usr/local/bin/docker /usr/bin/docker
COPY ./connhelper-ssh/entrypoint.sh /
+6
View File
@@ -0,0 +1,6 @@
ARG ENGINE_VERSION
FROM docker:${ENGINE_VERSION}-dind
# Trust the tlsregistry CA so dockerd connects without --insecure-registry.
COPY registry/certs/ca.crt /usr/local/share/ca-certificates/tlsregistry-ca.crt
RUN update-ca-certificates
+3
View File
@@ -0,0 +1,3 @@
FROM registry:3
COPY auth /auth
COPY certs /certs
+1
View File
@@ -0,0 +1 @@
e2e:$2y$05$DxRBsGSy61vZsBgNVxwUh.UtZmlg3wZHMxYcHYAlupY7r1xbIiuoq
+33
View File
@@ -0,0 +1,33 @@
#!/bin/sh
set -eu
# Regenerate test certificates for the TLS-enabled private registry.
# Run this from the repository root or from e2e/testdata/registry/certs/.
cd "$(dirname "$0")"
# --- CA ---
openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 3650 \
-key ca.key \
-subj '/CN=Test CA (TLS Registry)' \
-out ca.crt
# --- Server cert for tlsregistry (signed by CA) ---
cat > openssl-tlsregistry.cnf <<-EOF
[v3_req]
subjectAltName=DNS:tlsregistry
EOF
openssl genrsa -out tlsregistry.key 2048
openssl req -new \
-key tlsregistry.key \
-subj '/CN=tlsregistry' \
-out tlsregistry.csr
openssl x509 -req -days 3650 \
-in tlsregistry.csr \
-CA ca.crt -CAkey ca.key \
-CAcreateserial \
-out tlsregistry.crt \
-extfile openssl-tlsregistry.cnf \
-extensions v3_req
rm -f tlsregistry.csr ca.srl openssl-tlsregistry.cnf
+11 -1
View File
@@ -88,8 +88,18 @@ func Confirm(ctx context.Context, in io.Reader, out io.Writer, message string) (
_, _ = out.Write([]byte(message))
// On Windows, force the use of the regular OS stdin stream.
//
// StdStreams() may wrap stdin with windowsconsole.NewAnsiReader to
// emulate VT input on consoles that do not support it natively, but
// that wrapper has historically caused interactive prompts to hang
// or behave incorrectly.
//
// See:
// - https://github.com/moby/moby/issues/14336
// - https://github.com/moby/moby/issues/14210
// - https://github.com/moby/moby/pull/17738
if runtime.GOOS == "windows" {
in = streams.NewIn(os.Stdin)
in = os.Stdin
}
result := make(chan bool)
-5
View File
@@ -1,5 +0,0 @@
//go:build tools
package main
import _ "github.com/cpuguy83/go-md2man/v2"
+84
View File
@@ -0,0 +1,84 @@
package opts
import (
"testing"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestValidateThrottleBpsDevice(t *testing.T) {
tests := []struct {
doc string
input string
expectedErr string
expectedPath string
expectedRate uint64
}{
{doc: "plain integer", input: "/dev/sda:1000", expectedPath: "/dev/sda", expectedRate: 1000},
{doc: "with unit", input: "/dev/sda:1mb", expectedPath: "/dev/sda", expectedRate: 1048576},
{doc: "zero", input: "/dev/sda:0", expectedPath: "/dev/sda", expectedRate: 0},
{doc: "missing colon", input: "/dev/sda", expectedErr: "bad format: /dev/sda"},
{doc: "empty device", input: ":1mb", expectedErr: "bad format: :1mb"},
{doc: "missing /dev/ prefix", input: "sda:1mb", expectedErr: "bad format for device path: sda:1mb"},
{doc: "non-numeric rate", input: "/dev/sda:foo", expectedErr: "invalid rate for device"},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
v, err := ValidateThrottleBpsDevice(tc.input)
if tc.expectedErr != "" {
assert.Check(t, is.ErrorContains(err, tc.expectedErr))
assert.Check(t, is.Nil(v))
return
}
assert.NilError(t, err)
assert.Check(t, is.Equal(v.Path, tc.expectedPath))
assert.Check(t, is.Equal(v.Rate, tc.expectedRate))
})
}
}
func TestValidateThrottleIOpsDevice(t *testing.T) {
tests := []struct {
doc string
input string
expectedErr string
expectedPath string
expectedRate uint64
}{
{doc: "valid integer", input: "/dev/sda:100", expectedPath: "/dev/sda", expectedRate: 100},
{doc: "fractional rejected", input: "/dev/sda:1.5", expectedErr: "invalid rate for device"},
{doc: "negative rejected", input: "/dev/sda:-5", expectedErr: "invalid rate for device"},
{doc: "unit suffix rejected (iops are integers)", input: "/dev/sda:1mb", expectedErr: "invalid rate for device"},
{doc: "missing /dev/ prefix", input: "sda:100", expectedErr: "bad format for device path: sda:100"},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
v, err := ValidateThrottleIOpsDevice(tc.input)
if tc.expectedErr != "" {
assert.Check(t, is.ErrorContains(err, tc.expectedErr))
assert.Check(t, is.Nil(v))
return
}
assert.NilError(t, err)
assert.Check(t, is.Equal(v.Path, tc.expectedPath))
assert.Check(t, is.Equal(v.Rate, tc.expectedRate))
})
}
}
func TestThrottledeviceOptSetGetList(t *testing.T) {
opt := NewThrottledeviceOpt(ValidateThrottleBpsDevice)
assert.NilError(t, opt.Set("/dev/sda:1mb"))
assert.NilError(t, opt.Set("/dev/sdb:2mb"))
list := opt.GetList()
assert.Assert(t, is.Len(list, 2))
assert.Check(t, is.Equal(list[0].Path, "/dev/sda"))
assert.Check(t, is.Equal(list[0].Rate, uint64(1048576)))
assert.Check(t, is.Equal(list[1].Path, "/dev/sdb"))
assert.Check(t, is.Equal(list[1].Rate, uint64(2097152)))
assert.Check(t, is.ErrorContains(opt.Set("/dev/sdc:bad"), "invalid rate for device"))
assert.Check(t, is.Equal(opt.Type(), "list"))
}
+57
View File
@@ -0,0 +1,57 @@
package opts
import (
"testing"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestValidateWeightDevice(t *testing.T) {
tests := []struct {
doc string
input string
expectedErr string
expectedPath string
expectedWeight uint16
}{
{doc: "valid minimum", input: "/dev/sda:10", expectedPath: "/dev/sda", expectedWeight: 10},
{doc: "valid maximum", input: "/dev/sda:1000", expectedPath: "/dev/sda", expectedWeight: 1000},
{doc: "zero is accepted (unset)", input: "/dev/sda:0", expectedPath: "/dev/sda", expectedWeight: 0},
{doc: "below minimum", input: "/dev/sda:9", expectedErr: "invalid weight for device: /dev/sda:9"},
{doc: "above maximum", input: "/dev/sda:1001", expectedErr: "invalid weight for device: /dev/sda:1001"},
{doc: "overflows uint16", input: "/dev/sda:70000", expectedErr: "invalid weight for device: /dev/sda:70000"},
{doc: "missing colon", input: "/dev/sda", expectedErr: "bad format: /dev/sda"},
{doc: "empty device", input: ":100", expectedErr: "bad format: :100"},
{doc: "missing /dev/ prefix", input: "sda:100", expectedErr: "bad format for device path: sda:100"},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
v, err := ValidateWeightDevice(tc.input)
if tc.expectedErr != "" {
assert.Check(t, is.Error(err, tc.expectedErr))
assert.Check(t, is.Nil(v))
return
}
assert.NilError(t, err)
assert.Check(t, is.Equal(v.Path, tc.expectedPath))
assert.Check(t, is.Equal(v.Weight, tc.expectedWeight))
})
}
}
func TestWeightdeviceOptSetGetList(t *testing.T) {
opt := NewWeightdeviceOpt(ValidateWeightDevice)
assert.NilError(t, opt.Set("/dev/sda:100"))
assert.NilError(t, opt.Set("/dev/sdb:200"))
list := opt.GetList()
assert.Assert(t, is.Len(list, 2))
assert.Check(t, is.Equal(list[0].Path, "/dev/sda"))
assert.Check(t, is.Equal(list[0].Weight, uint16(100)))
assert.Check(t, is.Equal(list[1].Path, "/dev/sdb"))
assert.Check(t, is.Equal(list[1].Weight, uint16(200)))
assert.Check(t, is.Error(opt.Set("/dev/sdc:1"), "invalid weight for device: /dev/sdc:1"))
assert.Check(t, is.Equal(opt.Type(), "list"))
}
+10
View File
@@ -98,6 +98,16 @@ fi
if [ "$CGO_ENABLED" = "1" ] && [ "$GO_LINKMODE" = "static" ] && [ "$(go env GOOS)" = "linux" ]; then
GO_LDFLAGS="$GO_LDFLAGS -linkmode external -extldflags -static"
fi
# We only import google.golang.org/grpc as an indirect dependency, and
# do not make gRPC connections.
#
# grpcnotrace avoids importing golang.org/x/net/trace, which in turn enables
# dead code elimination, which can reduce binary size when tracing is not needed.
#
# see https://github.com/grpc/grpc-go/blob/v1.81.1/trace_notrace.go#L23-L25
GO_BUILDTAGS="$GO_BUILDTAGS grpcnotrace"
if [ "$CGO_ENABLED" = "1" ] && [ "$GO_LINKMODE" = "static" ]; then
# compiling statically with CGO enabled requires osusergo and netgo to be set.
GO_BUILDTAGS="$GO_BUILDTAGS osusergo netgo"
+4 -3
View File
@@ -7,15 +7,16 @@ set -eu
if ! command -v "$GO_MD2MAN" > /dev/null; then
(
set -x
go build -mod=vendor -modfile=vendor.mod -o ./build/tools/go-md2man ./vendor/github.com/cpuguy83/go-md2man/v2
# note: this installs all tools defined in go.mod/vendor.mod
GOBIN="$(pwd)/build/tools" go install -mod=vendor tool
)
GO_MD2MAN=$(realpath ./build/tools/go-md2man)
GO_MD2MAN="$(pwd)/build/tools/go-md2man"
fi
mkdir -p man/man1
(
set -x
go run -mod=vendor -modfile=vendor.mod -tags manpages ./man/generate.go --source "./man/src" --target "./man/man1"
go run -mod=vendor -tags manpages ./man/generate.go --source "./man/src" --target "./man/man1"
)
(
+1 -1
View File
@@ -4,7 +4,7 @@ set -eu
(
set -x
go run -mod=vendor -modfile=vendor.mod -tags docsgen ./docs/generate/generate.go --formats md --source "./docs/reference/commandline" --target "./docs/reference/commandline"
go run -mod=vendor -tags docsgen ./docs/generate/generate.go --formats md --source "./docs/reference/commandline" --target "./docs/reference/commandline"
)
# remove generated help.md file
+1 -1
View File
@@ -4,4 +4,4 @@ set -eu
mkdir -p docs/yaml
set -x
go run -mod=vendor -modfile=vendor.mod -tags docsgen ./docs/generate/generate.go --formats yaml --source "./docs/reference/commandline" --target "./docs/yaml"
go run -mod=vendor -tags docsgen ./docs/generate/generate.go --formats yaml --source "./docs/reference/commandline" --target "./docs/yaml"
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env bash
# Merge the given release tags.
set -Eeuo pipefail
if [[ $# -lt 1 ]]; then
echo "usage: $0 TAG..." >&2
exit 1
fi
tags_to_sync=("$@")
for tag_to_sync in "${tags_to_sync[@]}"; do
if git merge --no-edit --no-ff "$tag_to_sync"; then
continue
fi
if ! git rev-parse --verify --quiet MERGE_HEAD > /dev/null || git diff --quiet --diff-filter=U; then
exit 1
fi
git checkout "$tag_to_sync" -- '.'
git add --all
# Can't use --no-edit with --continue
EDITOR=true git merge --continue
done
# Check that every tag is in the branch.
# This catches cases where a merge did not actually incorporate one of the
# requested release tags.
for tag_to_sync in "${tags_to_sync[@]}"; do
if ! git merge-base --is-ancestor "$tag_to_sync" HEAD; then
echo "tag $tag_to_sync is not contained in $(git rev-parse --abbrev-ref HEAD)" >&2
exit 1
fi
done
+47
View File
@@ -26,8 +26,55 @@ setup() {
export TEST_CONNHELPER_SSH_ID_RSA_PUB
file="${file}:./e2e/compose-env.connhelper-ssh.yaml"
fi
# Generate TLS certificates for the TLS-enabled private registry.
# The certs are baked into the tlsregistry and engine container images,
# so they must exist on disk before docker compose up --build.
# gen-certs.sh handles its own directory navigation.
certdir=e2e/testdata/registry/certs
missing=0
for f in ca.crt ca.key tlsregistry.crt tlsregistry.key; do
if [ ! -f "${certdir}/${f}" ]; then
missing=1
break
fi
done
if [ "$missing" -eq 1 ]; then
sh e2e/testdata/registry/certs/gen-certs.sh
fi
COMPOSE_PROJECT_NAME=$project COMPOSE_FILE=$file docker compose up --build -d >&2
# Ensure supporting services exist before running tests. If one fails to start,
# fail fast and surface logs instead of waiting on downstream DNS timeouts.
local deadline=$((SECONDS + 120))
while [ $SECONDS -lt $deadline ]; do
local ok=1
for svc in registry privateregistry tlsregistry engine; do
cid="$(COMPOSE_PROJECT_NAME=$project COMPOSE_FILE=$file docker compose ps -q "$svc" 2>/dev/null || true)"
if [ -z "$cid" ]; then
ok=0
break
fi
if ! docker inspect -f '{{.State.Running}}' "$cid" 2>/dev/null | grep -q true; then
ok=0
break
fi
done
if [ "$ok" -eq 1 ]; then
break
fi
sleep 1
done
if [ $SECONDS -ge $deadline ]; then
echo "Timed out waiting for e2e services to start" >&2
COMPOSE_PROJECT_NAME=$project COMPOSE_FILE=$file docker compose ps >&2 || true
for svc in registry privateregistry tlsregistry engine; do
echo "--- logs: $svc ---" >&2
COMPOSE_PROJECT_NAME=$project COMPOSE_FILE=$file docker compose logs --no-color --tail=200 "$svc" >&2 || true
done
exit 1
fi
local network="${project}_default"
# TODO: only run if inside a container
docker network connect "$network" "$(hostname)"
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
set -Eeuo pipefail
if [[ $# -ne 2 ]]; then
echo "usage: $0 <RELEASE_BRANCH> <TAG>" >&2
exit 1
fi
release_branch="$1"
tag="$2"
if [[ "$tag" == *"-rc."* ]]; then
echo "error: RC tags cannot be used as sync targets" >&2
exit 1
fi
if [[ "$tag" != v* ]]; then
echo "error: Tag must start with 'v' (e.g. v29.6.0)" >&2
exit 1
fi
if ! git rev-parse --verify --quiet "refs/tags/$tag" > /dev/null; then
echo "error: Tag $tag does not exist" >&2
exit 1
fi
# Return early the requested tag is already merged into release branch.
if git merge-base --is-ancestor "$tag" "$release_branch"; then
exit 0
fi
if git rev-parse --verify --quiet upstream/master > /dev/null 2>&1; then
master="upstream/master"
else
master="origin/master"
fi
if ! git merge-base --is-ancestor "$tag" "$master"; then
echo "error: Tag $tag is not in $master" >&2
exit 1
fi
# Get all docker release tags merged into master but not into release branch
git tag --merged "$master" --no-merged "$release_branch" \
| grep '^v' \
| grep -v -- "-rc." \
| sort -V \
| awk -v tag="$tag" '{print} $0==tag{exit}'
+2 -2
View File
@@ -14,7 +14,7 @@ if [ -z "$TYP" ]; then
fi
update() {
(set -x ; go mod tidy -modfile=vendor.mod; go mod vendor -modfile=vendor.mod)
(set -x ; go mod tidy; go mod vendor)
}
validate() {
@@ -31,7 +31,7 @@ outdated() {
echo "go-mod-outdated not found. Install with 'go install github.com/psampaz/go-mod-outdated@v0.8.0'"
exit 1
fi
(set -x ; go list -mod=vendor -mod=readonly -modfile=vendor.mod -u -m -json all | go-mod-outdated -update -direct)
(set -x ; go list -mod=readonly -u -m -json all | go-mod-outdated -update -direct)
}
case $TYP in
+23 -15
View File
@@ -6,28 +6,36 @@
# when the command is finished. This script should be dropped when this
# repository is a proper Go module with a permanent go.mod.
set -e
set -euo pipefail
SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOTDIR="$(cd "${SCRIPTDIR}/.." && pwd)"
if test -e "${ROOTDIR}/go.mod"; then
{
scriptname=$(basename "$0")
cat >&2 <<- EOF
$scriptname: WARN: go.mod exists in the repository root!
$scriptname: WARN: Using your go.mod instead of our generated version -- this may misbehave!
EOF
} >&2
else
cleanup_paths=()
create_symlink() {
local target="$1"
local link="$2"
if [ -e "$link" ]; then
# see https://superuser.com/a/196698
if ! [ "$link" -ef "${ROOTDIR}/${target}" ]; then
echo "$(basename "$0"): WARN: $link exists but is not the expected symlink!" >&2
echo "$(basename "$0"): WARN: Using your version instead of our generated version -- this may misbehave!" >&2
fi
return
fi
set -x
ln -s "$target" "$link"
cleanup_paths+=( "$link" )
}
tee "${ROOTDIR}/go.mod" >&2 <<- EOF
module github.com/docker/cli
create_symlink "vendor.mod" "${ROOTDIR}/go.mod"
create_symlink "vendor.sum" "${ROOTDIR}/go.sum"
go 1.24.0
EOF
trap 'rm -f "${ROOTDIR}/go.mod"' EXIT
if [ "${#cleanup_paths[@]}" -gt 0 ]; then
trap 'rm -f "${cleanup_paths[@]}"' EXIT
fi
GO111MODULE=on GOTOOLCHAIN=local "$@"
+36 -33
View File
@@ -6,20 +6,22 @@ module github.com/docker/cli
go 1.25.0
tool golang.org/x/mod/modfile // for module compatibility check
tool (
github.com/cpuguy83/go-md2man/v2 // for scripts/docs/generate-man.sh
golang.org/x/mod/modfile // for module compatibility check
)
require (
dario.cat/mergo v1.0.2
github.com/containerd/errdefs v1.0.0
github.com/containerd/log v0.1.0
github.com/containerd/platforms v1.0.0-rc.4
github.com/cpuguy83/go-md2man/v2 v2.0.7
github.com/creack/pty v1.1.24
github.com/distribution/reference v0.6.0
github.com/docker/cli-docs-tool v0.11.0
github.com/docker/distribution v2.8.3+incompatible
github.com/docker/docker-credential-helpers v0.9.7
github.com/docker/go-connections v0.7.0
github.com/docker/docker-credential-helpers v0.9.8
github.com/docker/go-connections v0.8.0
github.com/docker/go-units v0.5.0
github.com/fvbommel/sortorder v1.1.0
github.com/go-jose/go-jose/v4 v4.1.4
@@ -28,15 +30,15 @@ require (
github.com/google/go-cmp v0.7.0
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/google/uuid v1.6.0
github.com/mattn/go-runewidth v0.0.23
github.com/moby/go-archive v0.2.0
github.com/moby/moby/api v1.54.2
github.com/moby/moby/client v0.4.1
github.com/mattn/go-runewidth v0.0.24
github.com/moby/go-archive v0.3.3
github.com/moby/moby/api v1.55.0
github.com/moby/moby/client v0.5.1
github.com/moby/patternmatcher v0.6.1
github.com/moby/swarmkit/v2 v2.1.2
github.com/moby/sys/atomicwriter v0.1.0
github.com/moby/sys/capability v0.4.0
github.com/moby/sys/sequential v0.6.0
github.com/moby/sys/sequential v0.7.0
github.com/moby/sys/signal v0.7.1
github.com/moby/sys/symlink v0.3.0
github.com/moby/term v0.5.2
@@ -44,24 +46,24 @@ require (
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.1
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/sirupsen/logrus v1.9.4
github.com/sirupsen/logrus v1.10.1
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.10
github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346
github.com/xeipuuv/gojsonschema v1.2.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0
go.opentelemetry.io/otel v1.43.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0
go.opentelemetry.io/otel/metric v1.43.0
go.opentelemetry.io/otel/sdk v1.43.0
go.opentelemetry.io/otel/sdk/metric v1.43.0
go.opentelemetry.io/otel/trace v1.43.0
go.yaml.in/yaml/v3 v3.0.4
golang.org/x/sync v0.20.0
golang.org/x/sys v0.44.0
golang.org/x/term v0.43.0
golang.org/x/text v0.37.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0
go.opentelemetry.io/otel v1.44.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.44.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0
go.opentelemetry.io/otel/metric v1.44.0
go.opentelemetry.io/otel/sdk v1.44.0
go.opentelemetry.io/otel/sdk/metric v1.44.0
go.opentelemetry.io/otel/trace v1.44.0
go.yaml.in/yaml/v3 v3.0.5
golang.org/x/sync v0.22.0
golang.org/x/sys v0.47.0
golang.org/x/term v0.45.0
golang.org/x/text v0.40.0
gotest.tools/v3 v3.5.2
tags.cncf.io/container-device-interface v1.1.0
)
@@ -74,7 +76,8 @@ require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/docker/go-events v0.0.0-20250808211157-605354379745 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/docker/go-events v0.0.0-20260608200158-dbf6103125a4 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
@@ -82,11 +85,11 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/klauspost/compress v1.18.5 // indirect
github.com/klauspost/compress v1.18.7 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/sys/user v0.4.0 // indirect
github.com/moby/sys/user v0.4.1 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/prometheus/client_golang v1.22.0 // indirect
@@ -98,13 +101,13 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
go.etcd.io/raft/v3 v3.6.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 // indirect
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
golang.org/x/mod v0.36.0 // indirect
golang.org/x/net v0.54.0 // indirect
golang.org/x/mod v0.38.0 // indirect
golang.org/x/net v0.57.0 // indirect
golang.org/x/time v0.15.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/grpc v1.80.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
google.golang.org/grpc v1.82.1 // indirect
google.golang.org/protobuf v1.36.11 // indirect
)
+70 -73
View File
@@ -32,7 +32,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
@@ -40,12 +39,12 @@ github.com/docker/cli-docs-tool v0.11.0 h1:7d8QARFb7QEobizqxmEM7fOteZEHwH/zWgHQt
github.com/docker/cli-docs-tool v0.11.0/go.mod h1:ma8BKiisUo8D6W05XEYIh3oa1UbgrZhi1nowyKFJa8Q=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker-credential-helpers v0.9.7 h1:jaPIxEIDz5bQeghNAdzz0ETwMMnM4vzjZlxz3pWP4JA=
github.com/docker/docker-credential-helpers v0.9.7/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c=
github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q=
github.com/docker/go-events v0.0.0-20250808211157-605354379745 h1:yOn6Ze6IbYI/KAw2lw/83ELYvZh6hvsygTVkD0dzMC4=
github.com/docker/go-events v0.0.0-20250808211157-605354379745/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/docker-credential-helpers v0.9.8 h1:bIREROb7So6PRlq6KTtdS9MPEjC29OQRkFNlvK2OX8Q=
github.com/docker/docker-credential-helpers v0.9.8/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
github.com/docker/go-connections v0.8.0 h1:T9UlP76qPLA/HaLrcC+s4Doqqv5XsWMMUGPF5Aih/k0=
github.com/docker/go-connections v0.8.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q=
github.com/docker/go-events v0.0.0-20260608200158-dbf6103125a4 h1:Bj+mzWc7MJqqD0UzTaPmwszW3ttOVjSFi84ZU5l+2I0=
github.com/docker/go-events v0.0.0-20260608200158-dbf6103125a4/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@@ -87,8 +86,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@@ -96,27 +95,23 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/compress v1.18.7 h1:aUyZsS4kH3QTKurYhAOwAHxllVPnOthb3vPfnF1Ehjw=
github.com/klauspost/compress v1.18.7/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU=
github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8=
github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg=
github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
github.com/moby/moby/client v0.4.1 h1:DMQgisVoMkmMs7fp3ROSdiBnoAu8+vo3GggFl06M/wY=
github.com/moby/moby/client v0.4.1/go.mod h1:z52C9O2POPOsnxZAy//WtKcQ32P+jT/NGeXu/7nfjGQ=
github.com/moby/go-archive v0.3.3 h1:OxxR9paxsluYi+zDUEXTTaIxtkK3viymW+Ka7vRhhME=
github.com/moby/go-archive v0.3.3/go.mod h1:Npdv43fFqlhZW7Xo8fbm3ZMYFvAGNviUPqX21VERbcE=
github.com/moby/moby/api v1.55.0 h1:2/sexvQyqIWS8pRSCFddBfpW2qE7vR7FCL+vN8pxwMc=
github.com/moby/moby/api v1.55.0/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
github.com/moby/moby/client v0.5.1 h1:tYNaJno4c0HXz12y5BiqEDy0rVTYkWzI26lGvnTMiJw=
github.com/moby/moby/client v0.5.1/go.mod h1:odLstlZ6uSnfvAgVxMpvgmb8SUdd+siH2T0GBuxVAlM=
github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U=
github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/swarmkit/v2 v2.1.2 h1:1WDZAI6HVYNKdCG4zlXnTAPyLsLwuhRGWlHoOUf5Z6I=
@@ -125,14 +120,18 @@ github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk=
github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
github.com/moby/sys/mount v0.3.5 h1:eS3fsZTjHaBihwjp4/+5Z3jxqLXYsbwxqpVSfFv3M00=
github.com/moby/sys/mount v0.3.5/go.mod h1:WUQDO+/uCiCIkIztx8SrwIDVn2dtMFRBebRhpDFT71M=
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
github.com/moby/sys/sequential v0.7.0 h1:ASQNGNROJSuOO6LL6bPHbKvuZu6NU8P4ldPWk31zj/8=
github.com/moby/sys/sequential v0.7.0/go.mod h1:NfSTAp6V3fw4tmkD62PEcOKeZKquXT8VKCkf7aVR79o=
github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0=
github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8=
github.com/moby/sys/symlink v0.3.0 h1:GZX89mEZ9u53f97npBy4Rc3vJKj7JBDj/PN2I22GrNU=
github.com/moby/sys/symlink v0.3.0/go.mod h1:3eNdhduHmYPcgsJtZXW1W4XUJdZGBIkttZ8xKqPUJq0=
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/sys/user v0.4.1 h1:RgjRlaDKi/Xmyrz4t8lyzXT6v2ooFeO/7xtchmhVWE0=
github.com/moby/sys/user v0.4.1/go.mod h1:E9QsW5WRe1kUAf7kW8hXKwu1uhsZEAdPLYHYSDudF4Y=
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
@@ -153,7 +152,6 @@ github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgr
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
@@ -173,13 +171,11 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/sirupsen/logrus v1.10.1 h1:xi4336Zh11WpU14fXR6I67V3yaTPQYwRx2WEtHbRg4Q=
github.com/sirupsen/logrus v1.10.1/go.mod h1:vsQHnG7xzNsxk3NrwboUiWPnIC3dmbjcGPykD7+tiHk=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -189,8 +185,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/stretchr/testify v1.12.0 h1:K6Mr6jO9JICuend/5xzTM03ydSV3vdNRYAdPSukj8uI=
github.com/stretchr/testify v1.12.0/go.mod h1:bOYBZb5qJ00vPzWfIqBUZPaxK8jWiXc6d3ErP4Ca9Gw=
github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346 h1:TvtdmeYsYEij78hS4oxnwikoiLdIrgav3BA+CbhaDAI=
github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346/go.mod h1:xKQhd7snlzKFuUi1taTGWjpRE8iFTA06DeacYi3CVFQ=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -206,53 +202,56 @@ go.etcd.io/raft/v3 v3.6.0 h1:5NtvbDVYpnfZWcIHgGRk9DyzkBIXOi8j+DDp1IcnUWQ=
go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk=
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 h1:8tvICD4vSTOOsNrsI4Ljf6C+6UKvpTEH5XY3JMoyPoo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0/go.mod h1:z9+yiacE0IHRqM4qFfkbt/JYlmYXgss8GY/jXoNuPJI=
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.44.0 h1:SUplec5dp06reu1zaXmOXdvqH398taqrDXqUl99jxSc=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.44.0/go.mod h1:ho2g4N+ane+swq5I/VBkKWnRDY4kUINH3FuqyZqX/Ug=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 h1:4YsVu3B8+3qtWYYrsUYgn0OG78pN0rnNPRGX4SbokQI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0/go.mod h1:+wnlSn0mD1ADVMe3v9Z/WIaiz6q6gL2J/ejaAmdmv80=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 h1:qazEJlUOQzhCpzQpFETGby7EdqjI1wsd0W+6Gg1SCTU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0/go.mod h1:fOD2Yefuxixkx3ahVNf0O/PERb6r4OlbxfATVnYvzCo=
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA=
go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk=
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk=
golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -261,14 +260,14 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -281,18 +280,16 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8=
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE=
google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+37 -13
View File
@@ -1,21 +1,19 @@
package sockets
import (
"context"
"net"
"sync"
)
// dummyAddr is used to satisfy net.Addr for the in-mem socket
// it is just stored as a string and returns the string for all calls
type dummyAddr string
// inmemAddr is used to satisfy net.Addr for the in-memory socket.
type inmemAddr string
// Network returns the addr string, satisfies net.Addr
func (a dummyAddr) Network() string {
return string(a)
}
func (a inmemAddr) Network() string { return "inmem" }
// String returns the string form
func (a dummyAddr) String() string {
func (a inmemAddr) String() string {
return string(a)
}
@@ -23,7 +21,7 @@ func (a dummyAddr) String() string {
type InmemSocket struct {
chConn chan net.Conn
chClose chan struct{}
addr dummyAddr
addr inmemAddr
mu sync.Mutex
}
@@ -34,7 +32,7 @@ func NewInmemSocket(addr string, bufSize int) *InmemSocket {
return &InmemSocket{
chConn: make(chan net.Conn, bufSize),
chClose: make(chan struct{}),
addr: dummyAddr(addr),
addr: inmemAddr(addr),
}
}
@@ -67,15 +65,41 @@ func (s *InmemSocket) Close() error {
return nil
}
// Dial is used to establish a connection with the in-mem server.
// It returns a [net.ErrClosed] if the connection is already closed.
// Dial establishes a connection with the in-memory listener.
//
// The network and addr parameters are accepted for compatibility with
// conventional dialer APIs but are currently ignored.
//
// It is equivalent to calling DialContext with context.Background().
// It returns [net.ErrClosed] if the listener has already been closed.
func (s *InmemSocket) Dial(network, addr string) (net.Conn, error) {
return s.DialContext(context.Background(), network, addr)
}
// DialContext establishes a connection with the in-memory listener.
//
// The network and addr parameters are accepted for compatibility with
// conventional dialer APIs but are currently ignored.
//
// If ctx is canceled before the connection is established, DialContext
// returns the context error. It returns [net.ErrClosed] if the listener
// has already been closed.
func (s *InmemSocket) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
if err := ctx.Err(); err != nil {
return nil, err
}
srvConn, clientConn := net.Pipe()
select {
case s.chConn <- srvConn:
return clientConn, nil
case <-ctx.Done():
_ = srvConn.Close()
_ = clientConn.Close()
return nil, ctx.Err()
case <-s.chClose:
_ = srvConn.Close()
_ = clientConn.Close()
return nil, net.ErrClosed
}
return clientConn, nil
}
+50 -19
View File
@@ -47,38 +47,69 @@ For example:
package sockets
import (
"errors"
"fmt"
"net"
"os"
"runtime"
"syscall"
)
const supportsAbstractSockets = runtime.GOOS == "linux"
// SockOption sets up socket file's creating option
type SockOption func(string) error
// NewUnixSocketWithOpts creates a unix socket with the specified options.
// By default, socket permissions are 0000 (i.e.: no access for anyone); pass
// WithChmod() and WithChown() to set the desired ownership and permissions.
// NewUnixSocketWithOpts creates a Unix socket with the specified options.
//
// This function temporarily changes the system's "umask" to 0777 to work around
// a race condition between creating the socket and setting its permissions. While
// this should only be for a short duration, it may affect other processes that
// create files/directories during that period.
// On Unix platforms, socket permissions are 0000 by default, i.e. no access
// for anyone. Pass WithChmod() and WithChown() to set the desired permissions
// and ownership.
//
// On Windows, the socket uses Windows ACLs. Pass WithBasePermissions() to allow
// Administrators and LocalSystem full access, or WithAdditionalUsersAndGroups()
// to also grant generic read and write access to additional users or groups.
//
// Abstract Unix sockets (Go's Linux-specific "@" shorthand and the native
// leading-NUL representation) are supported only on Linux. On other platforms,
// attempts to use abstract socket addresses return an error. Because abstract
// sockets have no filesystem representation, filesystem-specific socket
// options are not supported.
//
// On platforms without abstract Unix socket support, attempts to use abstract
// socket addresses return an error wrapping [errors.ErrUnsupported].
func NewUnixSocketWithOpts(path string, opts ...SockOption) (net.Listener, error) {
if isAbstractSocket(path) {
if !supportsAbstractSockets {
return nil, fmt.Errorf("abstract Unix socket %q is not supported on %s: %w", path, runtime.GOOS, errors.ErrUnsupported)
}
for _, opt := range opts {
if err := opt(path); err != nil {
return nil, err
}
}
return net.Listen("unix", path)
}
if err := syscall.Unlink(path); err != nil && !os.IsNotExist(err) {
return nil, err
}
l, err := listenUnix(path)
if err != nil {
return nil, err
}
return listenUnix(path, opts...)
}
for _, op := range opts {
if err := op(path); err != nil {
_ = l.Close()
return nil, err
}
}
return l, nil
// isAbstractSocket reports whether path is an abstract Unix socket address.
//
// Go recognizes two representations of abstract socket addresses:
//
// - On Linux, a path beginning with '@' is translated by the standard library
// to the kernel's native leading-NUL representation.
// See https://pkg.go.dev/net@go1.27rc2#UnixAddr.
//
// - A path beginning with a NUL byte uses the kernel's native representation
// directly. See https://github.com/golang/go/issues/78615.
//
// The interpretation of these addresses is platform-dependent; this helper only
// recognizes the syntax.
func isAbstractSocket(path string) bool {
return len(path) > 0 && (path[0] == '@' || path[0] == 0)
}
+25
View File
@@ -0,0 +1,25 @@
package sockets
import (
"os"
"strconv"
"strings"
"syscall"
)
// maxListenerBacklog returns the maximum length of the queue of pending
// connections for a listening socket.
//
// It is similar to in stdlib, but without the fallbacks for Kernel < 4.1.0;
// https://github.com/golang/go/blob/go1.26.3/src/net/sock_linux.go#L33-L53
func maxListenerBacklog() int {
b, err := os.ReadFile("/proc/sys/net/core/somaxconn")
if err != nil {
return syscall.SOMAXCONN
}
n, err := strconv.Atoi(strings.TrimSpace(string(b)))
if err != nil || n <= 0 {
return syscall.SOMAXCONN
}
return n
}
+39
View File
@@ -0,0 +1,39 @@
//go:build !linux && !windows
package sockets
import (
"runtime"
"syscall"
)
// maxListenerBacklog is similar to the equivalent in stdlib;
// https://github.com/golang/go/blob/go1.26.3/src/net/sock_bsd.go#L14-L39
func maxListenerBacklog() int {
var (
n uint32
err error
)
switch runtime.GOOS {
case "darwin", "ios":
n, err = syscall.SysctlUint32("kern.ipc.somaxconn")
case "freebsd":
n, err = syscall.SysctlUint32("kern.ipc.soacceptqueue")
case "netbsd":
// NOTE: NetBSD has no somaxconn-like kernel state so far
case "openbsd":
n, err = syscall.SysctlUint32("kern.somaxconn")
default:
return syscall.SOMAXCONN
}
if n == 0 || err != nil {
return syscall.SOMAXCONN
}
// FreeBSD stores the backlog in a uint16, as does Linux.
// Assume the other BSDs do too. Truncate number to avoid wrapping.
// See issue 5030.
if n > 1<<16-1 {
n = 1<<16 - 1
}
return int(n)
}
+113 -18
View File
@@ -3,14 +3,35 @@
package sockets
import (
"errors"
"fmt"
"net"
"os"
"sync"
"syscall"
)
// WithChown modifies the socket file's uid and gid
// defaultSocketPerms is the default permission mode applied to newly created
// Unix sockets. Sockets are created inaccessible by default; callers can
// override this by passing [WithChmod].
//
// TODO(thaJeztah): Consider changing the default to 0o600, making the socket usable by its owner by default.
const defaultSocketPerms os.FileMode = 0o000
// WithChown modifies the socket file's uid and gid.
//
// Abstract Unix sockets have no filesystem representation, so this option
// returns an error wrapping [errors.ErrUnsupported] when used with an abstract
// socket.
func WithChown(uid, gid int) SockOption {
return func(path string) error {
if isAbstractSocket(path) {
return &os.PathError{
Op: "chown",
Path: path,
Err: fmt.Errorf("abstract Unix sockets do not support filesystem permissions: %w", errors.ErrUnsupported),
}
}
if err := os.Chown(path, uid, gid); err != nil {
return err
}
@@ -19,8 +40,19 @@ func WithChown(uid, gid int) SockOption {
}
// WithChmod modifies socket file's access mode.
//
// Abstract Unix sockets have no filesystem representation, so this option
// returns an error wrapping [errors.ErrUnsupported] when used with an abstract
// socket.
func WithChmod(mask os.FileMode) SockOption {
return func(path string) error {
if isAbstractSocket(path) {
return &os.PathError{
Op: "chmod",
Path: path,
Err: fmt.Errorf("abstract Unix sockets do not support filesystem permissions: %w", errors.ErrUnsupported),
}
}
if err := os.Chmod(path, mask); err != nil {
return err
}
@@ -28,27 +60,90 @@ func WithChmod(mask os.FileMode) SockOption {
}
}
// NewUnixSocket creates a unix socket with the specified path and group.
// NewUnixSocket creates a Unix socket with the specified path and group.
//
// On Unix platforms, the socket is owned by root:gid and has permissions 0660.
//
// Abstract Unix sockets are not supported by this helper. Use [NewUnixSocketWithOpts]
// without filesystem permission options instead.
func NewUnixSocket(path string, gid int) (net.Listener, error) {
return NewUnixSocketWithOpts(path, WithChown(0, gid), WithChmod(0o660))
}
func listenUnix(path string) (net.Listener, error) {
// net.Listen does not allow for permissions to be set. As a result, when
// specifying custom permissions ("WithChmod()"), there is a short time
// between creating the socket and applying the permissions, during which
// the socket permissions are Less restrictive than desired.
func listenUnix(path string, opts ...SockOption) (_ net.Listener, retErr error) {
// net.Listen does not allow permissions or ownership to be set between
// bind(2), which creates the socket path, and listen(2), which makes it
// possible for clients to connect.
//
// To work around this limitation of net.Listen(), we temporarily set the
// umask to 0777, which forces the socket to be created with 000 permissions
// (i.e.: no access for anyone). After that, WithChmod() must be used to set
// the desired permissions.
// Creating the socket manually lets us apply options after bind(2), but
// before listen(2). This avoids temporarily relaxing the process umask while
// still preventing a socket from becoming connectable before the requested
// permissions are applied.
//
// We don't use "defer" here, to reset the umask to its original value as soon
// as possible. Ideally we'd be able to detect if WithChmod() was passed as
// an option, and skip changing umask if default permissions are used.
origUmask := syscall.Umask(0o777)
l, err := net.Listen("unix", path)
syscall.Umask(origUmask)
return l, err
// See https://github.com/golang/go/issues/11822
// Similar to sysSocket in stdlib, but without the fast path for Linux.
// https://github.com/golang/go/blob/go1.26.3/src/net/sys_cloexec.go#L18-L36
syscall.ForkLock.RLock()
fd, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
if err == nil {
syscall.CloseOnExec(fd) // No syscall.SOCK_CLOEXEC on macOS.
}
syscall.ForkLock.RUnlock()
if err != nil {
return nil, os.NewSyscallError("socket", err)
}
defer func() {
if fd >= 0 {
_ = syscall.Close(fd)
}
}()
if err := syscall.Bind(fd, &syscall.SockaddrUnix{Name: path}); err != nil {
return nil, os.NewSyscallError("bind", err)
}
defer func() {
if retErr != nil {
_ = syscall.Unlink(path)
}
}()
// Secure by default: the socket is not accessible at all
// unless permission options are set through WithChmod.
if err := os.Chmod(path, defaultSocketPerms); err != nil {
return nil, err
}
for _, op := range opts {
if err := op(path); err != nil {
return nil, err
}
}
if err := syscall.Listen(fd, listenerBacklog()); err != nil {
return nil, os.NewSyscallError("listen", err)
}
f := os.NewFile(uintptr(fd), "unix:"+path)
fd = -1 // f now owns the original fd; prevent the defer from closing it.
// FileListener duplicates f, sets the duplicate close-on-exec and nonblocking,
// and returns a net.Listener backed by that duplicate. The temporary *os.File
// is no longer needed after this point.
l, err := net.FileListener(f)
_ = f.Close()
if err != nil {
return nil, err
}
if ul, ok := l.(*net.UnixListener); ok {
ul.SetUnlinkOnClose(true)
}
return l, nil
}
// listenerBacklog is a caching wrapper around maxListenerBacklog.
var listenerBacklog = sync.OnceValue(maxListenerBacklog)
+24 -10
View File
@@ -48,7 +48,7 @@ func WithAdditionalUsersAndGroups(additionalUsersAndGroups []string) SockOption
}
sd, err := getSecurityDescriptor(additionalUsersAndGroups...)
if err != nil {
return fmt.Errorf("looking up SID: %w", err)
return err
}
return withSDDL(sd)(path)
}
@@ -85,12 +85,15 @@ func withSDDL(sddl string) SockOption {
}
}
// NewUnixSocket creates a new unix socket.
// NewUnixSocket creates a new Unix socket.
//
// It sets [BasePermissions] on the socket path and grants the given additional
// users and groups to generic read (GR) and write (GW) access. It returns
// an error when failing to resolve any of the additional users and groups,
// or when failing to apply the ACL.
//
// Abstract Unix sockets are not supported by this helper. Attempts to use
// abstract socket addresses return an error wrapping [errors.ErrUnsupported].
func NewUnixSocket(path string, additionalUsersAndGroups []string) (net.Listener, error) {
var opts []SockOption
if len(additionalUsersAndGroups) > 0 {
@@ -103,27 +106,38 @@ func NewUnixSocket(path string, additionalUsersAndGroups []string) (net.Listener
// getSecurityDescriptor returns the DACL for the Unix socket.
//
// By default, it grants [BasePermissions], but allows for additional
// users and groups to get generic read (GR) and write (GW) access. It
// returns an error when failing to resolve any of the additional users
// and groups.
// By default, it grants [BasePermissions]. Additional users and groups
// are granted generic read (GR) and write (GW) access. It returns an
// error if any name cannot be resolved to a SID.
func getSecurityDescriptor(additionalUsersAndGroups ...string) (string, error) {
sddl := BasePermissions
// Grant generic read (GR) and write (GW) access to whatever
// additional users or groups were specified.
//
// TODO(thaJeztah): should we fail on, or remove duplicates?
// We keep duplicates; two identical allow ACEs are redundant,
// but they do not create conflicting permissions, so should not error.
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/20233ed8-a6c6-4097-aafa-dd545ed24428
for _, g := range additionalUsersAndGroups {
sid, err := winio.LookupSidByName(strings.TrimSpace(g))
if err != nil {
return "", fmt.Errorf("looking up SID: %w", err)
}
sddl += fmt.Sprintf("(A;;GRGW;;;%s)", sid)
sddl += "(A;;GRGW;;;" + sid + ")"
}
return sddl, nil
}
func listenUnix(path string) (net.Listener, error) {
return net.Listen("unix", path)
func listenUnix(path string, opts ...SockOption) (net.Listener, error) {
l, err := net.Listen("unix", path)
if err != nil {
return nil, err
}
for _, op := range opts {
if err := op(path); err != nil {
_ = l.Close()
return nil, err
}
}
return l, nil
}
+2 -1
View File
@@ -78,7 +78,8 @@ func (b *Broadcaster) configure(ch chan configureRequest, sink Sink) error {
select {
case ch <- configureRequest{
sink: sink,
response: response}:
response: response,
}:
ch = nil
case err := <-response:
return err
+4 -6
View File
@@ -2,9 +2,7 @@ package events
import "fmt"
var (
// ErrSinkClosed is returned if a write is issued to a sink that has been
// closed. If encountered, the error should be considered terminal and
// retries will not be successful.
ErrSinkClosed = fmt.Errorf("events: sink closed")
)
// ErrSinkClosed is returned if a write is issued to a sink that has been
// closed. If encountered, the error should be considered terminal and
// retries will not be successful.
var ErrSinkClosed = fmt.Errorf("events: sink closed")
+7 -9
View File
@@ -173,15 +173,13 @@ func (b *Breaker) Failure(event Event, err error) bool {
return false // never drop events.
}
var (
// DefaultExponentialBackoffConfig provides a default configuration for
// exponential backoff.
DefaultExponentialBackoffConfig = ExponentialBackoffConfig{
Base: time.Second,
Factor: time.Second,
Max: 20 * time.Second,
}
)
// DefaultExponentialBackoffConfig provides a default configuration for
// exponential backoff.
var DefaultExponentialBackoffConfig = ExponentialBackoffConfig{
Base: time.Second,
Factor: time.Second,
Max: 20 * time.Second,
}
// ExponentialBackoffConfig configures backoff parameters.
//
+17 -1
View File
@@ -71,6 +71,7 @@ type ServeMux struct {
streamErrorHandler StreamErrorHandlerFunc
routingErrorHandler RoutingErrorHandlerFunc
disablePathLengthFallback bool
disableHTTPMethodOverride bool
unescapingMode UnescapingMode
writeContentLength bool
disableChunkedEncoding bool
@@ -271,6 +272,19 @@ func WithDisablePathLengthFallback() ServeMuxOption {
}
}
// WithDisableHTTPMethodOverride returns a ServeMuxOption that disables the
// X-HTTP-Method-Override header handling.
//
// When this option is used, the mux will no longer allow POST requests with
// the X-HTTP-Method-Override header to override the HTTP method. The path
// length fallback (POST with application/x-www-form-urlencoded falling back
// to a matching GET handler) is not affected by this option.
func WithDisableHTTPMethodOverride() ServeMuxOption {
return func(serveMux *ServeMux) {
serveMux.disableHTTPMethodOverride = true
}
}
// WithWriteContentLength returns a ServeMuxOption to enable writing content length on non-streaming responses
func WithWriteContentLength() ServeMuxOption {
return func(serveMux *ServeMux) {
@@ -405,7 +419,7 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path = r.URL.RawPath
}
if override := r.Header.Get("X-HTTP-Method-Override"); override != "" && s.isPathLengthFallback(r) {
if override := r.Header.Get("X-HTTP-Method-Override"); override != "" && !s.disableHTTPMethodOverride && s.isPathLengthFallback(r) {
if err := r.ParseForm(); err != nil {
_, outboundMarshaler := MarshalerForRequest(s, r)
sterr := status.Error(codes.InvalidArgument, err.Error())
@@ -467,6 +481,7 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
HTTPStatus: http.StatusBadRequest,
Err: mse,
})
return
}
continue
}
@@ -509,6 +524,7 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
HTTPStatus: http.StatusBadRequest,
Err: mse,
})
return
}
continue
}
+1
View File
@@ -1,2 +1,3 @@
* -text
*.bin -text -diff
*.md text eol=lf
+700 -700
View File
File diff suppressed because it is too large Load Diff
+78 -78
View File
@@ -1,79 +1,79 @@
# Finite State Entropy
This package provides Finite State Entropy encoding and decoding.
Finite State Entropy (also referenced as [tANS](https://en.wikipedia.org/wiki/Asymmetric_numeral_systems#tANS))
encoding provides a fast near-optimal symbol encoding/decoding
for byte blocks as implemented in [zstandard](https://github.com/facebook/zstd).
This can be used for compressing input with a lot of similar input values to the smallest number of bytes.
This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders,
but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding.
* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/fse)
## News
* Feb 2018: First implementation released. Consider this beta software for now.
# Usage
This package provides a low level interface that allows to compress single independent blocks.
Each block is separate, and there is no built in integrity checks.
This means that the caller should keep track of block sizes and also do checksums if needed.
Compressing a block is done via the [`Compress`](https://godoc.org/github.com/klauspost/compress/fse#Compress) function.
You must provide input and will receive the output and maybe an error.
These error values can be returned:
| Error | Description |
|---------------------|-----------------------------------------------------------------------------|
| `<nil>` | Everything ok, output is returned |
| `ErrIncompressible` | Returned when input is judged to be too hard to compress |
| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated |
| `(error)` | An internal error occurred. |
As can be seen above there are errors that will be returned even under normal operation so it is important to handle these.
To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/fse#Scratch) object
that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same
object can be used for both.
Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this
you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output.
Decompressing is done by calling the [`Decompress`](https://godoc.org/github.com/klauspost/compress/fse#Decompress) function.
You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
your input was likely corrupted.
It is important to note that a successful decoding does *not* mean your output matches your original input.
There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid.
For more detailed usage, see examples in the [godoc documentation](https://godoc.org/github.com/klauspost/compress/fse#pkg-examples).
# Performance
A lot of factors are affecting speed. Block sizes and compressibility of the material are primary factors.
All compression functions are currently only running on the calling goroutine so only one core will be used per block.
The compressor is significantly faster if symbols are kept as small as possible. The highest byte value of the input
is used to reduce some of the processing, so if all your input is above byte value 64 for instance, it may be
beneficial to transpose all your input values down by 64.
With moderate block sizes around 64k speed are typically 200MB/s per core for compression and
around 300MB/s decompression speed.
The same hardware typically does Huffman (deflate) encoding at 125MB/s and decompression at 100MB/s.
# Plans
At one point, more internals will be exposed to facilitate more "expert" usage of the components.
A streaming interface is also likely to be implemented. Likely compatible with [FSE stream format](https://github.com/Cyan4973/FiniteStateEntropy/blob/dev/programs/fileio.c#L261).
# Contributing
Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
# Finite State Entropy
This package provides Finite State Entropy encoding and decoding.
Finite State Entropy (also referenced as [tANS](https://en.wikipedia.org/wiki/Asymmetric_numeral_systems#tANS))
encoding provides a fast near-optimal symbol encoding/decoding
for byte blocks as implemented in [zstandard](https://github.com/facebook/zstd).
This can be used for compressing input with a lot of similar input values to the smallest number of bytes.
This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders,
but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding.
* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/fse)
## News
* Feb 2018: First implementation released. Consider this beta software for now.
# Usage
This package provides a low level interface that allows to compress single independent blocks.
Each block is separate, and there is no built in integrity checks.
This means that the caller should keep track of block sizes and also do checksums if needed.
Compressing a block is done via the [`Compress`](https://godoc.org/github.com/klauspost/compress/fse#Compress) function.
You must provide input and will receive the output and maybe an error.
These error values can be returned:
| Error | Description |
|---------------------|-----------------------------------------------------------------------------|
| `<nil>` | Everything ok, output is returned |
| `ErrIncompressible` | Returned when input is judged to be too hard to compress |
| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated |
| `(error)` | An internal error occurred. |
As can be seen above there are errors that will be returned even under normal operation so it is important to handle these.
To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/fse#Scratch) object
that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same
object can be used for both.
Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this
you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output.
Decompressing is done by calling the [`Decompress`](https://godoc.org/github.com/klauspost/compress/fse#Decompress) function.
You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
your input was likely corrupted.
It is important to note that a successful decoding does *not* mean your output matches your original input.
There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid.
For more detailed usage, see examples in the [godoc documentation](https://godoc.org/github.com/klauspost/compress/fse#pkg-examples).
# Performance
A lot of factors are affecting speed. Block sizes and compressibility of the material are primary factors.
All compression functions are currently only running on the calling goroutine so only one core will be used per block.
The compressor is significantly faster if symbols are kept as small as possible. The highest byte value of the input
is used to reduce some of the processing, so if all your input is above byte value 64 for instance, it may be
beneficial to transpose all your input values down by 64.
With moderate block sizes around 64k speed are typically 200MB/s per core for compression and
around 300MB/s decompression speed.
The same hardware typically does Huffman (deflate) encoding at 125MB/s and decompression at 100MB/s.
# Plans
At one point, more internals will be exposed to facilitate more "expert" usage of the components.
A streaming interface is also likely to be implemented. Likely compatible with [FSE stream format](https://github.com/Cyan4973/FiniteStateEntropy/blob/dev/programs/fileio.c#L261).
# Contributing
Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
changes will likely not be accepted. If in doubt open an issue before writing the PR.
+89 -89
View File
@@ -1,89 +1,89 @@
# Huff0 entropy compression
This package provides Huff0 encoding and decoding as used in zstd.
[Huff0](https://github.com/Cyan4973/FiniteStateEntropy#new-generation-entropy-coders),
a Huffman codec designed for modern CPU, featuring OoO (Out of Order) operations on multiple ALU
(Arithmetic Logic Unit), achieving extremely fast compression and decompression speeds.
This can be used for compressing input with a lot of similar input values to the smallest number of bytes.
This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders,
but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding.
* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/huff0)
## News
This is used as part of the [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression package.
This ensures that most functionality is well tested.
# Usage
This package provides a low level interface that allows to compress single independent blocks.
Each block is separate, and there is no built in integrity checks.
This means that the caller should keep track of block sizes and also do checksums if needed.
Compressing a block is done via the [`Compress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress1X) and
[`Compress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress4X) functions.
You must provide input and will receive the output and maybe an error.
These error values can be returned:
| Error | Description |
|---------------------|-----------------------------------------------------------------------------|
| `<nil>` | Everything ok, output is returned |
| `ErrIncompressible` | Returned when input is judged to be too hard to compress |
| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated |
| `ErrTooBig` | Returned if the input block exceeds the maximum allowed size (128 Kib) |
| `(error)` | An internal error occurred. |
As can be seen above some of there are errors that will be returned even under normal operation so it is important to handle these.
To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object
that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same
object can be used for both.
Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this
you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output.
The `Scratch` object will retain state that allows to re-use previous tables for encoding and decoding.
## Tables and re-use
Huff0 allows for reusing tables from the previous block to save space if that is expected to give better/faster results.
The Scratch object allows you to set a [`ReusePolicy`](https://godoc.org/github.com/klauspost/compress/huff0#ReusePolicy)
that controls this behaviour. See the documentation for details. This can be altered between each block.
Do however note that this information is *not* stored in the output block and it is up to the users of the package to
record whether [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable) should be called,
based on the boolean reported back from the CompressXX call.
If you want to store the table separate from the data, you can access them as `OutData` and `OutTable` on the
[`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object.
## Decompressing
The first part of decoding is to initialize the decoding table through [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable).
This will initialize the decoding tables.
You can supply the complete block to `ReadTable` and it will return the data part of the block
which can be given to the decompressor.
Decompressing is done by calling the [`Decompress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress1X)
or [`Decompress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress4X) function.
For concurrently decompressing content with a fixed table a stateless [`Decoder`](https://godoc.org/github.com/klauspost/compress/huff0#Decoder) can be requested which will remain correct as long as the scratch is unchanged. The capacity of the provided slice indicates the expected output size.
You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
your input was likely corrupted.
It is important to note that a successful decoding does *not* mean your output matches your original input.
There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid.
# Contributing
Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
changes will likely not be accepted. If in doubt open an issue before writing the PR.
# Huff0 entropy compression
This package provides Huff0 encoding and decoding as used in zstd.
[Huff0](https://github.com/Cyan4973/FiniteStateEntropy#new-generation-entropy-coders),
a Huffman codec designed for modern CPU, featuring OoO (Out of Order) operations on multiple ALU
(Arithmetic Logic Unit), achieving extremely fast compression and decompression speeds.
This can be used for compressing input with a lot of similar input values to the smallest number of bytes.
This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders,
but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding.
* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/huff0)
## News
This is used as part of the [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression package.
This ensures that most functionality is well tested.
# Usage
This package provides a low level interface that allows to compress single independent blocks.
Each block is separate, and there is no built in integrity checks.
This means that the caller should keep track of block sizes and also do checksums if needed.
Compressing a block is done via the [`Compress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress1X) and
[`Compress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress4X) functions.
You must provide input and will receive the output and maybe an error.
These error values can be returned:
| Error | Description |
|---------------------|-----------------------------------------------------------------------------|
| `<nil>` | Everything ok, output is returned |
| `ErrIncompressible` | Returned when input is judged to be too hard to compress |
| `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated |
| `ErrTooBig` | Returned if the input block exceeds the maximum allowed size (128 Kib) |
| `(error)` | An internal error occurred. |
As can be seen above some of there are errors that will be returned even under normal operation so it is important to handle these.
To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object
that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same
object can be used for both.
Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this
you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output.
The `Scratch` object will retain state that allows to re-use previous tables for encoding and decoding.
## Tables and re-use
Huff0 allows for reusing tables from the previous block to save space if that is expected to give better/faster results.
The Scratch object allows you to set a [`ReusePolicy`](https://godoc.org/github.com/klauspost/compress/huff0#ReusePolicy)
that controls this behaviour. See the documentation for details. This can be altered between each block.
Do however note that this information is *not* stored in the output block and it is up to the users of the package to
record whether [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable) should be called,
based on the boolean reported back from the CompressXX call.
If you want to store the table separate from the data, you can access them as `OutData` and `OutTable` on the
[`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object.
## Decompressing
The first part of decoding is to initialize the decoding table through [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable).
This will initialize the decoding tables.
You can supply the complete block to `ReadTable` and it will return the data part of the block
which can be given to the decompressor.
Decompressing is done by calling the [`Decompress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress1X)
or [`Decompress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress4X) function.
For concurrently decompressing content with a fixed table a stateless [`Decoder`](https://godoc.org/github.com/klauspost/compress/huff0#Decoder) can be requested which will remain correct as long as the scratch is unchanged. The capacity of the provided slice indicates the expected output size.
You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
your input was likely corrupted.
It is important to note that a successful decoding does *not* mean your output matches your original input.
There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid.
# Contributing
Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
changes will likely not be accepted. If in doubt open an issue before writing the PR.
+25
View File
@@ -0,0 +1,25 @@
# Security Policy
## Supported Versions
The following versions of go-runewidth are currently supported with
security updates.
| Version | Supported |
| -------- | ------------------ |
| 0.0.23 | :white_check_mark: |
| < 0.0.23 | :x: |
## Reporting a Vulnerability
If you discover a security vulnerability in go-runewidth, please report it
privately via GitHub's "Report a vulnerability" feature on the Security tab
of the repository (https://github.com/mattn/go-runewidth/security), or by
emailing the maintainer at mattn.jp@gmail.com.
Please include a description of the issue, reproduction steps, and the
affected version. You can expect an initial response within one week. If
the vulnerability is accepted, a fix will be prepared and a new release
will be published; you will be credited in the release notes unless you
request otherwise. If the report is declined, you will receive an
explanation of the reasoning.
+118 -35
View File
@@ -2,6 +2,7 @@ package runewidth
import (
"os"
"sort"
"strings"
"unicode/utf8"
@@ -25,13 +26,19 @@ var (
)
var (
zerowidth table // combining + nonprint merged for faster zero-width lookup
widewidth table // ambiguous + doublewidth merged for EA path
zerowidth table // combining + nonprint merged for faster zero-width lookup
widewidth table // ambiguous + doublewidth merged for EA path
eastAsianWidth widthTable
eastAsianWidth0 [0x300]byte
)
func init() {
zerowidth = mergeIntervals(combining, nonprint)
widewidth = mergeIntervals(ambiguous, doublewidth)
eastAsianWidth = makeWidthTable(zerowidth, widewidth)
for r := range eastAsianWidth0 {
eastAsianWidth0[r] = byte(runeWidthEastAsian(rune(r)))
}
handleEnv()
}
@@ -90,6 +97,14 @@ type interval struct {
type table []interval
type widthInterval struct {
first rune
last rune
width byte
}
type widthTable []widthInterval
func inTable(r rune, t table) bool {
if r < t[0].first {
return false
@@ -116,6 +131,71 @@ func inTable(r rune, t table) bool {
return false
}
func makeWidthTable(zero, two table) widthTable {
wt := make(widthTable, 0, len(zero)+len(two))
zi := 0
for _, iv := range two {
start := iv.first
for zi < len(zero) && zero[zi].last < start {
zi++
}
for i := zi; i < len(zero) && zero[i].first <= iv.last; i++ {
if start < zero[i].first {
wt = append(wt, widthInterval{start, zero[i].first - 1, 2})
}
if start <= zero[i].last {
start = zero[i].last + 1
}
if start > iv.last {
break
}
}
if start <= iv.last {
wt = append(wt, widthInterval{start, iv.last, 2})
}
}
for _, iv := range zero {
wt = append(wt, widthInterval{iv.first, iv.last, 0})
}
sort.Slice(wt, func(i, j int) bool {
return wt[i].first < wt[j].first
})
return wt
}
func inWidthTable(r rune, t widthTable) (int, bool) {
if r < t[0].first {
return 0, false
}
if r > t[len(t)-1].last {
return 0, false
}
bot := 0
top := len(t) - 1
for top >= bot {
mid := (bot + top) >> 1
switch {
case t[mid].last < r:
bot = mid + 1
case t[mid].first > r:
top = mid - 1
default:
return int(t[mid].width), true
}
}
return 0, false
}
func runeWidthEastAsian(r rune) int {
if w, ok := inWidthTable(r, eastAsianWidth); ok {
return w
}
return 1
}
var private = table{
{0x00E000, 0x00F8FF}, {0x0F0000, 0x0FFFFD}, {0x100000, 0x10FFFD},
}
@@ -153,13 +233,16 @@ func (c *Condition) RuneWidth(r rune) int {
}
// optimized version, verified by TestRuneWidthChecksums()
if !c.EastAsianWidth {
switch {
case r < 0x20:
if r < 0x20 {
return 0
case (r >= 0x7F && r <= 0x9F) || r == 0xAD: // nonprint
}
if (r >= 0x7F && r <= 0x9F) || r == 0xAD { // nonprint
return 0
case r < 0x300:
}
if r < 0x300 {
return 1
}
switch {
case inTable(r, zerowidth):
return 0
case inTable(r, doublewidth):
@@ -167,20 +250,18 @@ func (c *Condition) RuneWidth(r rune) int {
default:
return 1
}
} else {
switch {
case inTable(r, zerowidth):
return 0
case inTable(r, narrow):
return 1
case inTable(r, widewidth):
return 2
case !c.StrictEmojiNeutral && inTable(r, emoji):
return 2
default:
return 1
}
}
if r < 0x300 {
return int(eastAsianWidth0[r])
}
if w, ok := inWidthTable(r, eastAsianWidth); ok {
return w
}
if !c.StrictEmojiNeutral && inTable(r, emoji) {
return 2
}
return 1
}
// CreateLUT will create an in-memory lookup table of 557056 bytes for faster operation.
@@ -206,6 +287,13 @@ func (c *Condition) CreateLUT() {
// StringWidth return width as you can see
func (c *Condition) StringWidth(s string) (width int) {
if len(s) == 1 {
b := s[0]
if b < 0x20 || b == 0x7F {
return 0
}
return 1
}
if len(s) > 0 && len(s) <= utf8.UTFMax {
r, size := utf8.DecodeRuneInString(s)
if size == len(s) {
@@ -213,15 +301,19 @@ func (c *Condition) StringWidth(s string) (width int) {
}
}
// ASCII fast path: no grapheme clustering needed for pure ASCII
if isAllASCII(s) {
for i := 0; i < len(s); i++ {
b := s[i]
if b >= 0x20 && b != 0x7F {
width++
}
for i := 0; i < len(s); i++ {
b := s[i]
if b >= 0x80 {
goto graphemes
}
if b >= 0x20 && b != 0x7F {
width++
}
return
}
return
graphemes:
width = 0
g := graphemes.FromString(s)
for g.Next() {
var chWidth int
@@ -236,15 +328,6 @@ func (c *Condition) StringWidth(s string) (width int) {
return
}
func isAllASCII(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] >= 0x80 {
return false
}
}
return true
}
// Truncate return string truncated with w cells
func (c *Condition) Truncate(s string, w int, tail string) string {
if c.StringWidth(s) <= w {
+12
View File
@@ -9,6 +9,7 @@ issues:
linters:
enable:
- errorlint
- gosec
- unconvert
- unparam
exclusions:
@@ -16,7 +17,18 @@ linters:
presets:
- comments
- std-error-handling
rules:
# Ignore "G204: Subprocess launched with a potential tainted input or cmd arguments"
- path: '(.+)_test\.go'
linters:
- gosec
text: 'G204: Subprocess launched'
settings:
gosec:
excludes:
- G301 # Expect directory permissions to be 0750 or less
- G304 # Potential file inclusion via variable
- G306 # Expect WriteFile permissions to be 0600 or less
staticcheck:
# Enable all options, with some exceptions.
# For defaults, see https://golangci-lint.run/usage/linters/#staticcheck
+410 -151
View File
@@ -8,13 +8,16 @@ import (
"fmt"
"io"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"sync"
"syscall"
"time"
"github.com/containerd/log"
"github.com/moby/go-archive/internal/archiveoptions"
"github.com/moby/patternmatcher"
"github.com/moby/sys/sequential"
"github.com/moby/sys/user"
@@ -46,9 +49,18 @@ type (
// TarOptions wraps the tar options.
TarOptions struct {
IncludeFiles []string
ExcludePatterns []string
Compression compression.Compression
// IncludeFiles lists archive-relative paths to include.
// Paths use POSIX ('/') separators.
IncludeFiles []string
// ExcludePatterns lists archive-relative exclude patterns.
// Patterns use POSIX ('/') separators, matching patternmatcher semantics.
ExcludePatterns []string
Compression compression.Compression
// NoLchown disables applying ownership from the archive to extracted files
// and directories. Despite its historical name, it applies to all ownership
// changes, leaving extracted filesystem objects owned by the user performing
// the extraction.
NoLchown bool
IDMap user.IdentityMapping
ChownOpts *ChownOpts
@@ -70,9 +82,22 @@ type (
// were probably in the archive for a reason, so set this option at
// your own peril.
BestEffortXattrs bool
// internalOptions contains options for use by packages within this module.
internalOptions *archiveoptions.Options
}
)
// WithProcSelfFD returns a copy of opts prepared for extraction in a
// filesystem context where /proc/self/fd may not be accessible by path.
//
// The caller must invoke the returned cleanup function after extraction
// completes. On platforms that do not use /proc/self/fd for extraction,
// the returned cleanup function is a no-op.
func WithProcSelfFD(opts *TarOptions) (*TarOptions, func(), error) {
return withProcSelfFD(opts)
}
// Archiver implements the Archiver interface and allows the reuse of most utility functions of
// this package with a pluggable Untar function. Also, to facilitate the passing of specific id
// mappings for untar, an Archiver can be created with maps which will then be passed to Untar operations.
@@ -86,10 +111,33 @@ func NewDefaultArchiver() *Archiver {
return &Archiver{Untar: Untar}
}
// breakoutError is used to differentiate errors related to breaking out
// When testing archive breakout in the unit tests, this error is expected
// in order for the test to pass.
type breakoutError error
// isPathEscapes reports whether err is os.Root's path-containment error.
//
// os.Root currently returns an unexported errPathEscapes sentinel, so callers
// cannot detect it with errors.Is. Keep the string comparison isolated here
// until Go exports the error; see https://go.dev/issue/74640.
func isPathEscapes(err error) bool {
// https://github.com/golang/go/blob/go1.26.5/src/os/file.go#L421
const errPathEscapes = "path escapes from parent"
for err != nil {
if errors.Unwrap(err) == nil {
return err.Error() == errPathEscapes
}
err = errors.Unwrap(err)
}
return false
}
// breakoutErr marks errors caused by archive breakout attempts.
// Unit tests use it to distinguish expected breakout failures from other
// errors.
type breakoutErr struct{ error }
func breakoutError(err error) error {
return &breakoutErr{error: err}
}
func (e *breakoutErr) Unwrap() error { return e.error }
const (
AUFSWhiteoutFormat WhiteoutFormat = 0 // AUFSWhiteoutFormat is the default format for whiteouts
@@ -98,17 +146,17 @@ const (
// IsArchivePath checks if the (possibly compressed) file at the given path
// starts with a tar file header.
func IsArchivePath(path string) bool {
file, err := os.Open(path)
func IsArchivePath(filePath string) bool {
file, err := os.Open(filePath)
if err != nil {
return false
}
defer file.Close()
defer func() { _ = file.Close() }()
rdr, err := compression.DecompressStream(file)
if err != nil {
return false
}
defer rdr.Close()
defer func() { _ = rdr.Close() }()
r := tar.NewReader(rdr)
_, err = r.Next()
return err == nil
@@ -129,8 +177,10 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModi
go func() {
tarReader := tar.NewReader(inputTarStream)
tarWriter := tar.NewWriter(pipeWriter)
defer inputTarStream.Close()
defer tarWriter.Close()
defer func() {
_ = tarWriter.Close()
_ = inputTarStream.Close()
}()
modify := func(name string, original *tar.Header, modifier TarModifierFunc, tarReader io.Reader) error {
header, data, err := modifier(name, original, tarReader)
@@ -164,7 +214,7 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModi
break
}
if err != nil {
pipeWriter.CloseWithError(err)
_ = pipeWriter.CloseWithError(err)
return
}
@@ -172,11 +222,11 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModi
if !ok {
// No modifiers for this file, copy the header and data
if err := tarWriter.WriteHeader(originalHeader); err != nil {
pipeWriter.CloseWithError(err)
_ = pipeWriter.CloseWithError(err)
return
}
if err := copyWithBuffer(tarWriter, tarReader); err != nil {
pipeWriter.CloseWithError(err)
_ = pipeWriter.CloseWithError(err)
return
}
continue
@@ -184,7 +234,7 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModi
delete(mods, originalHeader.Name)
if err := modify(originalHeader.Name, originalHeader, modifier, tarReader); err != nil {
pipeWriter.CloseWithError(err)
_ = pipeWriter.CloseWithError(err)
return
}
}
@@ -192,12 +242,12 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModi
// Apply the modifiers that haven't matched any files in the archive
for name, modifier := range mods {
if err := modify(name, nil, modifier, nil); err != nil {
pipeWriter.CloseWithError(err)
_ = pipeWriter.CloseWithError(err)
return
}
}
pipeWriter.Close()
_ = pipeWriter.Close()
}()
return pipeReader
}
@@ -218,7 +268,7 @@ func FileInfoHeader(name string, fi os.FileInfo, link string) (*tar.Header, erro
hdr.ModTime = hdr.ModTime.Truncate(time.Second)
hdr.AccessTime = time.Time{}
hdr.ChangeTime = time.Time{}
hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
hdr.Mode = chmodTarEntry(hdr.Mode)
hdr.Name = canonicalTarName(name, fi.IsDir())
return hdr, nil
}
@@ -227,7 +277,7 @@ const paxSchilyXattr = "SCHILY.xattr."
// ReadSecurityXattrToTarHeader reads security.capability xattr from filesystem
// to a tar header
func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error {
func ReadSecurityXattrToTarHeader(filePath string, hdr *tar.Header) error {
const (
// Values based on linux/include/uapi/linux/capability.h
xattrCapsSz2 = 20
@@ -235,7 +285,7 @@ func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error {
vfsCapRevision2 = 2
vfsCapRevision3 = 3
)
capability, _ := lgetxattr(path, "security.capability")
capability, _ := lgetxattr(filePath, "security.capability")
if capability != nil {
if capability[versionOffset] == vfsCapRevision3 {
// Convert VFS_CAP_REVISION_3 to VFS_CAP_REVISION_2 as root UID makes no
@@ -253,7 +303,7 @@ func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error {
type tarWhiteoutConverter interface {
ConvertWrite(*tar.Header, string, os.FileInfo) (*tar.Header, error)
ConvertRead(*tar.Header, string) (bool, error)
ConvertRead(*os.Root, *tar.Header, string) (bool, error)
}
type tarAppender struct {
@@ -292,9 +342,10 @@ func canonicalTarName(name string, isDir bool) string {
return name
}
// addTarFile adds to the tar archive a file from `path` as `name`
func (ta *tarAppender) addTarFile(path, name string) error {
fi, err := os.Lstat(path)
// addTarFile adds to the tar archive a file from `srcPath` as `name`
func (ta *tarAppender) addTarFile(srcPath, archivePath string) error {
archivePath = filepath.ToSlash(archivePath)
fi, err := os.Lstat(srcPath)
if err != nil {
return err
}
@@ -302,17 +353,17 @@ func (ta *tarAppender) addTarFile(path, name string) error {
var link string
if fi.Mode()&os.ModeSymlink != 0 {
var err error
link, err = os.Readlink(path)
link, err = os.Readlink(srcPath)
if err != nil {
return err
}
}
hdr, err := FileInfoHeader(name, fi, link)
hdr, err := FileInfoHeader(archivePath, fi, link)
if err != nil {
return err
}
if err := ReadSecurityXattrToTarHeader(path, hdr); err != nil {
if err := ReadSecurityXattrToTarHeader(srcPath, hdr); err != nil {
return err
}
@@ -321,7 +372,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
if !fi.IsDir() && hasHardlinks(fi) {
inode, err := getInodeFromStat(fi.Sys())
if err != nil {
return err
return fmt.Errorf("unexpected file info for %q: %w", srcPath, err)
}
// a link should have a name that it links too
// and that linked name should be first in the tar archive
@@ -330,7 +381,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
hdr.Linkname = oldpath
hdr.Size = 0 // This Must be here for the writer math to add up!
} else {
ta.SeenFiles[inode] = name
ta.SeenFiles[inode] = hdr.Name
}
}
@@ -341,7 +392,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
// handle re-mapping container ID mappings back to host ID mappings before
// writing tar headers/files. We skip whiteout files because they were written
// by the kernel and already have proper ownership relative to the host
if !isOverlayWhiteout && !strings.HasPrefix(filepath.Base(hdr.Name), WhiteoutPrefix) && !ta.IdentityMapping.Empty() {
if !isOverlayWhiteout && !strings.HasPrefix(path.Base(hdr.Name), WhiteoutPrefix) && !ta.IdentityMapping.Empty() {
uid, gid, err := getFileUIDGID(fi.Sys())
if err != nil {
return err
@@ -359,7 +410,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
}
if ta.WhiteoutConverter != nil {
wo, err := ta.WhiteoutConverter.ConvertWrite(hdr, path, fi)
wo, err := ta.WhiteoutConverter.ConvertWrite(hdr, srcPath, fi)
if err != nil {
return err
}
@@ -370,12 +421,12 @@ func (ta *tarAppender) addTarFile(path, name string) error {
// hdr may have been updated to be a whiteout with returning
// a whiteout header
if wo != nil {
if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 {
return fmt.Errorf("tar: cannot use whiteout for non-empty file %q", hdr.Name)
}
if err := ta.TarWriter.WriteHeader(hdr); err != nil {
return err
}
if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 {
return fmt.Errorf("tar: cannot use whiteout for non-empty file")
}
hdr = wo
}
}
@@ -387,13 +438,13 @@ func (ta *tarAppender) addTarFile(path, name string) error {
if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 {
// We use sequential file access to avoid depleting the standby list on
// Windows. On Linux, this equates to a regular os.Open.
file, err := sequential.Open(path)
file, err := sequential.Open(srcPath)
if err != nil {
return err
}
err = copyWithBuffer(ta.TarWriter, file)
file.Close()
_ = file.Close()
if err != nil {
return err
}
@@ -402,11 +453,99 @@ func (ta *tarAppender) addTarFile(path, name string) error {
return nil
}
func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, opts *TarOptions) error {
// resolveArchivePath resolves intermediate symlinks in name using chroot-like
// semantics when os.Root cannot traverse them. The final path component is
// intentionally preserved because archive extraction may create or replace it.
//
// This is a compatibility workaround rather than the preferred long-term
// implementation. It resolves the path separately before the actual operation,
// so a concurrent filesystem change may cause the operation to affect a
// different path within root. The subsequent os.Root operation still confines
// the operation to root and prevents such a change from escaping it.
//
// Paths with missing components are supported. Existing symlinks are resolved,
// and any remaining nonexistent components are retained for later creation.
//
// This helper should eventually be replaced by handle-relative resolution and
// operations with resolve-in-root semantics, avoiding the resolution/use race
// and repeated path traversal.
func resolveArchivePath(root *os.Root, name string) (string, error) {
parent, base := filepath.Split(name)
if parent == "" {
return name, nil
}
parent = filepath.Clean(parent)
// Follow the final parent component: it is an intermediate component of name,
// and an absolute symlink there must trigger the resolve-in-root fallback.
_, statErr := root.Stat(parent)
switch {
case statErr == nil:
return name, nil
case !os.IsNotExist(statErr) && !isPathEscapes(statErr):
return "", statErr
}
// Resolve the parent both to handle ENOENT from missing components or dangling
// symlinks, and to determine whether an os.Root breakout was caused by an
// absolute symlink. Relative symlink escapes preserve the original Stat error.
resolved, err := resolveFSRootPath(root.Name(), parent)
if err != nil {
return "", err
}
if isPathEscapes(statErr) && (!resolved.followedAbsoluteLink || resolved.relativeEscapeBeforeAbsolute) {
return "", statErr
}
relParent, err := filepath.Rel(root.Name(), resolved.path)
if err != nil {
return "", breakoutError(fmt.Errorf(
"could not make resolved parent %q relative to root %q: %w",
resolved.path,
root.Name(),
err,
))
}
if relParent != "." && !filepath.IsLocal(relParent) {
return "", breakoutError(fmt.Errorf(
"resolved parent %q escapes root %q",
resolved.path,
root.Name(),
))
}
return filepath.Join(relParent, base), nil
}
// resolveHardlinkTarget validates a POSIX hardlink target and resolves it to
// the native, root-relative filesystem path used for extraction.
func resolveHardlinkTarget(root *os.Root, linkname string) (string, error) {
cleaned := path.Clean(linkname)
if strings.HasPrefix(cleaned, "/") {
// Some image builders (e.g. kaniko) write hardlink targets as absolute
// paths. Resolve those relative to the extraction root, with chroot-like
// semantics matching absolute symlink targets. Strip the root from the
// original linkname rather than the cleaned one so that ".." components
// are not collapsed against "/" but instead rejected below.
cleaned = path.Clean(strings.TrimLeft(linkname, "/"))
}
if cleaned == "." || !filepath.IsLocal(cleaned) {
return "", breakoutError(fmt.Errorf("invalid hardlink target %q", linkname))
}
return resolveArchivePath(root, filepath.FromSlash(cleaned))
}
// createTarFile extracts a single tar entry into the given root. dstPath is the
// root-relative path of the entry being extracted, in native (host-separator)
// form so it can be passed directly to os.Root methods and fsRootPath.
func createTarFile(root *os.Root, dstPath string, hdr *tar.Header, reader io.Reader, opts *TarOptions) error {
var (
Lchown = true
inUserns, bestEffortXattrs bool
chownOpts *ChownOpts
internalOpts *archiveoptions.Options
)
// TODO(thaJeztah): make opts a required argument.
@@ -415,6 +554,7 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, o
inUserns = opts.InUserNS // TODO(thaJeztah): consider deprecating opts.InUserNS and detect locally.
chownOpts = opts.ChownOpts
bestEffortXattrs = opts.BestEffortXattrs
internalOpts = opts.internalOptions
}
// hdr.Mode is in linux format, which we can use for sycalls,
@@ -422,20 +562,35 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, o
// so use hdrInfo.Mode() (they differ for e.g. setuid bits)
hdrInfo := hdr.FileInfo()
var hardlinkTarget string
if hdr.Typeflag == tar.TypeLink {
var err error
hardlinkTarget, err = resolveHardlinkTarget(root, hdr.Linkname)
if err != nil {
return err
}
}
switch hdr.Typeflag {
case tar.TypeDir:
// Create directory unless it exists as a directory already.
// In that case we just want to merge the two
if fi, err := os.Lstat(path); err != nil || !fi.IsDir() {
if err := os.Mkdir(path, hdrInfo.Mode()); err != nil {
// Create directory unless it already exists as one; merge in that case.
// os.Root.Mkdir only accepts the nine least-significant permission
// bits; special bits (setuid, setgid, sticky) are applied afterward
// by handleLChmod via root.Chmod.
if fi, err := root.Lstat(dstPath); err != nil || !fi.IsDir() {
if err := root.Mkdir(dstPath, hdrInfo.Mode()&0o777); err != nil {
return err
}
}
case tar.TypeReg:
// Source is regular file. We use sequential file access to avoid depleting
// the standby list on Windows. On Linux, this equates to a regular os.OpenFile.
file, err := sequential.OpenFile(path, os.O_CREATE|os.O_WRONLY, hdrInfo.Mode())
// Source is a regular file. Use os.Root.OpenFile so that all
// path resolution is bounded within root using openat(2) semantics.
// os.Root.OpenFile only accepts the nine least-significant permission
// bits; special bits are applied afterward by handleLChmod.
// We use sequential file access to avoid depleting the standby list
// on Windows (go1.26). On Linux, this equates to a regular os.OpenFile.
file, err := root.OpenFile(dstPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|windows_O_FILE_FLAG_SEQUENTIAL_SCAN, hdrInfo.Mode()&0o777)
if err != nil {
return err
}
@@ -447,47 +602,41 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, o
case tar.TypeBlock, tar.TypeChar:
if inUserns { // cannot create devices in a userns
log.G(context.TODO()).WithFields(log.Fields{"path": path, "type": hdr.Typeflag}).Debug("skipping device nodes in a userns")
log.G(context.TODO()).WithFields(log.Fields{"path": dstPath, "type": hdr.Typeflag}).Debug("skipping device nodes in a userns")
return nil
}
// Handle this is an OS-specific way
if err := handleTarTypeBlockCharFifo(hdr, path); err != nil {
if err := handleTarTypeBlockCharFifo(root, hdr, dstPath); err != nil {
return err
}
case tar.TypeFifo:
// Handle this is an OS-specific way
if err := handleTarTypeBlockCharFifo(hdr, path); err != nil {
if err := handleTarTypeBlockCharFifo(root, hdr, dstPath); err != nil {
if inUserns && errors.Is(err, syscall.EPERM) {
// In most cases, cannot create a fifo if running in user namespace
log.G(context.TODO()).WithFields(log.Fields{"error": err, "path": path, "type": hdr.Typeflag}).Debug("creating fifo node in a userns")
log.G(context.TODO()).WithFields(log.Fields{"error": err, "path": dstPath, "type": hdr.Typeflag}).Debug("creating fifo node in a userns")
return nil
}
return err
}
case tar.TypeLink:
// #nosec G305 -- The target path is checked for path traversal.
targetPath := filepath.Join(extractDir, hdr.Linkname)
// check for hardlink breakout
if !strings.HasPrefix(targetPath, extractDir) {
return breakoutError(fmt.Errorf("invalid hardlink %q -> %q", targetPath, hdr.Linkname))
}
if err := os.Link(targetPath, path); err != nil {
if err := root.Link(hardlinkTarget, dstPath); err != nil {
return err
}
case tar.TypeSymlink:
// path -> hdr.Linkname = targetPath
// e.g. /extractDir/path/to/symlink -> ../2/file = /extractDir/path/2/file
targetPath := filepath.Join(filepath.Dir(path), hdr.Linkname) // #nosec G305 -- The target path is checked for path traversal.
// Symlink targets are archive data, not filesystem paths. Preserve the
// target verbatim rather than cleaning or converting it (filepath.FromSlash).
linkTarget := hdr.Linkname
// the reason we don't need to check symlinks in the path (with FollowSymlinkInScope) is because
// that symlink would first have to be created, which would be caught earlier, at this very check:
if !strings.HasPrefix(targetPath, extractDir) {
return breakoutError(fmt.Errorf("invalid symlink %q -> %q", path, hdr.Linkname))
}
if err := os.Symlink(hdr.Linkname, path); err != nil {
// os.Root.Symlink contains the symlink's location (newname) within
// root but stores the target (oldname) verbatim, so absolute targets
// such as /usr/lib -- common and legitimate in container images -- are
// preserved rather than rejected. The symlink node is therefore always
// created within root via openat(2) semantics, without resolving to an
// absolute path; containment applies when the symlink is followed, not
// at creation.
if err := root.Symlink(linkTarget, dstPath); err != nil {
return err
}
@@ -504,22 +653,31 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, o
if chownOpts == nil {
chownOpts = &ChownOpts{UID: hdr.Uid, GID: hdr.Gid}
}
if err := os.Lchown(path, chownOpts.UID, chownOpts.GID); err != nil {
if err := root.Lchown(dstPath, chownOpts.UID, chownOpts.GID); err != nil {
var msg string
if inUserns && errors.Is(err, syscall.EINVAL) {
msg = " (try increasing the number of subordinate IDs in /etc/subuid and /etc/subgid)"
}
return fmt.Errorf("failed to Lchown %q for UID %d, GID %d%s: %w", path, hdr.Uid, hdr.Gid, msg, err)
return fmt.Errorf("failed to Lchown %q for UID %d, GID %d%s: %w", dstPath, hdr.Uid, hdr.Gid, msg, err)
}
}
var xattrErrs []string
absPath := sync.OnceValues(func() (string, error) {
return fsRootPath(root.Name(), dstPath)
})
for key, value := range hdr.PAXRecords {
xattr, ok := strings.CutPrefix(key, paxSchilyXattr)
if !ok {
continue
}
if err := lsetxattr(path, xattr, []byte(value), 0); err != nil {
// os.Root has no xattr support; use the absolute path derived from
// the root so the path remains bounded.
ap, err := absPath()
if err != nil {
return err
}
if err := lsetxattr(ap, xattr, []byte(value), 0); err != nil {
if bestEffortXattrs && errors.Is(err, syscall.ENOTSUP) || errors.Is(err, syscall.EPERM) {
// EPERM occurs if modifying xattrs is not allowed. This can
// happen when running in userns with restrictions (ChromeOS).
@@ -538,39 +696,43 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, o
// There is no LChmod, so ignore mode for symlink. Also, this
// must happen after chown, as that can modify the file mode
if err := handleLChmod(hdr, path, hdrInfo); err != nil {
if err := handleLChmod(root, dstPath, hardlinkTarget, hdr, hdrInfo, internalOpts); err != nil {
return err
}
aTime := boundTime(latestTime(hdr.AccessTime, hdr.ModTime))
mTime := boundTime(hdr.ModTime)
// chtimes doesn't support a NOFOLLOW flag atm
if hdr.Typeflag == tar.TypeLink {
if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
if err := chtimes(path, aTime, mTime); err != nil {
switch hdr.Typeflag {
case tar.TypeSymlink:
// Apply timestamps to the symlink itself (AT_SYMLINK_NOFOLLOW).
if err := lchtimes(root, dstPath, aTime, mTime); err != nil {
return err
}
case tar.TypeLink:
// Follow the hardlink only when its target is not itself a symlink.
fi, err := root.Lstat(hardlinkTarget)
if err == nil && fi.Mode()&os.ModeSymlink == 0 {
if err := chtimes(root, dstPath, aTime, mTime); err != nil {
return err
}
}
} else if hdr.Typeflag != tar.TypeSymlink {
if err := chtimes(path, aTime, mTime); err != nil {
return err
}
} else {
if err := lchtimes(path, aTime, mTime); err != nil {
default:
// All other file types follow symlinks.
if err := chtimes(root, dstPath, aTime, mTime); err != nil {
return err
}
}
return nil
}
// Tar creates an archive from the directory at `path`, and returns it as a
// Tar creates an archive from the directory at `srcPath`, and returns it as a
// stream of bytes.
func Tar(path string, comp compression.Compression) (io.ReadCloser, error) {
return TarWithOptions(path, &TarOptions{Compression: comp})
func Tar(srcPath string, comp compression.Compression) (io.ReadCloser, error) {
return TarWithOptions(srcPath, &TarOptions{Compression: comp})
}
// TarWithOptions creates an archive from the directory at `path`, only including files whose relative
// TarWithOptions creates an archive from the directory at `srcPath`, only including files whose relative
// paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`.
func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) {
tb, err := NewTarballer(srcPath, options)
@@ -639,13 +801,13 @@ func (t *Tarballer) Do() {
defer func() {
// Make sure to check the error on Close.
if err := ta.TarWriter.Close(); err != nil {
if err := ta.TarWriter.Close(); err != nil && !errors.Is(err, io.ErrClosedPipe) {
log.G(context.TODO()).Errorf("Can't close tar writer: %s", err)
}
if err := t.compressWriter.Close(); err != nil {
if err := t.compressWriter.Close(); err != nil && !errors.Is(err, io.ErrClosedPipe) {
log.G(context.TODO()).Errorf("Can't close compress writer: %s", err)
}
if err := t.pipeWriter.Close(); err != nil {
if err := t.pipeWriter.Close(); err != nil && !errors.Is(err, io.ErrClosedPipe) {
log.G(context.TODO()).Errorf("Can't close pipe writer: %s", err)
}
}()
@@ -803,11 +965,28 @@ func (t *Tarballer) Do() {
}
}
// unpackedDir records a directory whose mtime must be restored after all
// entries are extracted, along with the root-relative entry name used during
// extraction.
type unpackedDir struct {
hdr *tar.Header
name string // root-relative entry name
}
// Unpack unpacks the decompressedArchive to dest with options.
func Unpack(decompressedArchive io.Reader, dest string, options *TarOptions) error {
if options == nil {
options = &TarOptions{}
}
root, err := os.OpenRoot(dest)
if err != nil {
return err
}
defer func() { _ = root.Close() }()
tr := tar.NewReader(decompressedArchive)
var dirs []*tar.Header
var dirs []unpackedDir
whiteoutConverter := getWhiteoutConverter(options.WhiteoutFormat)
// Iterate through the files in the archive.
@@ -828,48 +1007,53 @@ loop:
continue
}
// Normalize name, for safety and for a simple is-root check
// This keeps "../" as-is, but normalizes "/../" to "/". Or Windows:
// This keeps "..\" as-is, but normalizes "\..\" to "\".
hdr.Name = filepath.Clean(hdr.Name)
// Strip a leading "/" so absolute entries stay root-relative, and
// normalize the POSIX tar path. Skip entries referring to the extraction
// root and reject paths that escape it.
name := path.Clean(strings.TrimLeft(hdr.Name, "/"))
if name == "." {
continue
}
if !filepath.IsLocal(name) {
return breakoutError(fmt.Errorf("invalid entry name %q", hdr.Name))
}
for _, exclude := range options.ExcludePatterns {
if strings.HasPrefix(hdr.Name, exclude) {
if strings.HasPrefix(name, exclude) {
continue loop
}
}
// Ensure that the parent directory exists.
err = createImpliedDirectories(dest, hdr, options)
hdr.Name = name
// Skip entries whose name (or hardlink target) Windows cannot represent.
if err := unrepresentableOnWindows(hdr); err != nil {
log.G(context.TODO()).Warnf("Windows: ignoring entry: %v", err)
continue loop
}
// dstPath is the native (host-separator) form of the entry name,
// used at all filesystem boundaries (os.Root methods, fsRootPath).
// hdr.Name stays POSIX (forward-slash) for logical string checks.
dstPath, err := resolveArchivePath(root, filepath.FromSlash(hdr.Name))
if err != nil {
return err
}
// #nosec G305 -- The joined path is checked for path traversal.
path := filepath.Join(dest, hdr.Name)
rel, err := filepath.Rel(dest, path)
if err != nil {
return err
}
if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
return breakoutError(fmt.Errorf("%q is outside of %q", hdr.Name, dest))
}
// If path exits we almost always just want to remove and replace it
// If dstPath exists we almost always just want to remove and replace it.
// The only exception is when it is a directory *and* the file from
// the layer is also a directory. Then we want to merge them (i.e.
// just apply the metadata from the layer).
if fi, err := os.Lstat(path); err == nil {
if fi, err := root.Lstat(dstPath); err == nil {
if options.NoOverwriteDirNonDir && fi.IsDir() && hdr.Typeflag != tar.TypeDir {
// If NoOverwriteDirNonDir is true then we cannot replace
// an existing directory with a non-directory from the archive.
return fmt.Errorf("cannot overwrite directory %q with non-directory %q", path, dest)
return fmt.Errorf("cannot overwrite directory %q with non-directory %q", hdr.Name, dest)
}
if options.NoOverwriteDirNonDir && !fi.IsDir() && hdr.Typeflag == tar.TypeDir {
// If NoOverwriteDirNonDir is true then we cannot replace
// an existing non-directory with a directory from the archive.
return fmt.Errorf("cannot overwrite non-directory %q with directory %q", path, dest)
return fmt.Errorf("cannot overwrite non-directory %q with directory %q", hdr.Name, dest)
}
if fi.IsDir() && hdr.Name == "." {
@@ -877,7 +1061,7 @@ loop:
}
if !fi.IsDir() || hdr.Typeflag != tar.TypeDir {
if err := os.RemoveAll(path); err != nil {
if err := root.RemoveAll(dstPath); err != nil {
return err
}
}
@@ -887,8 +1071,16 @@ loop:
return err
}
// Ensure that the parent directory exists.
//
// This must be done before whiteoutConverter.ConvertRead, which
// may set xattrs on the directory or create whiteout files.
if err := createImpliedDirectories(root, dstPath, options); err != nil {
return err
}
if whiteoutConverter != nil {
writeFile, err := whiteoutConverter.ConvertRead(hdr, path)
writeFile, err := whiteoutConverter.ConvertRead(root, hdr, dstPath)
if err != nil {
return err
}
@@ -897,51 +1089,121 @@ loop:
}
}
if err := createTarFile(path, dest, hdr, tr, options); err != nil {
if err := createTarFile(root, dstPath, hdr, tr, options); err != nil {
return err
}
// Directory mtimes must be handled at the end to avoid further
// file creation in them to modify the directory mtime
if hdr.Typeflag == tar.TypeDir {
dirs = append(dirs, hdr)
dirs = append(dirs, unpackedDir{hdr: hdr, name: dstPath})
}
}
for _, hdr := range dirs {
// #nosec G305 -- The header was checked for path traversal before it was appended to the dirs slice.
path := filepath.Join(dest, hdr.Name)
if err := chtimes(path, boundTime(latestTime(hdr.AccessTime, hdr.ModTime)), boundTime(hdr.ModTime)); err != nil {
for _, d := range dirs {
aTime := boundTime(latestTime(d.hdr.AccessTime, d.hdr.ModTime))
if err := chtimes(root, d.name, aTime, boundTime(d.hdr.ModTime)); err != nil {
return err
}
}
return nil
}
// createImpliedDirectories will create all parent directories of the current path with default permissions, if they do
// not already exist. This is possible as the tar format supports 'implicit' directories, where their existence is
// defined by the paths of files in the tar, but there are no header entries for the directories themselves, and thus
// we most both create them and choose metadata like permissions.
//
// The caller should have performed filepath.Clean(hdr.Name), so hdr.Name will now be in the filepath format for the OS
// on which the daemon is running. This precondition is required because this function assumes a OS-specific path
// separator when checking that a path is not the root.
func createImpliedDirectories(dest string, hdr *tar.Header, options *TarOptions) error {
// Not the root directory, ensure that the parent directory exists
if !strings.HasSuffix(hdr.Name, string(os.PathSeparator)) {
parent := filepath.Dir(hdr.Name)
parentPath := filepath.Join(dest, parent)
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
// RootPair() is confined inside this loop as most cases will not require a call, so we can spend some
// unneeded function calls in the uncommon case to encapsulate logic -- implied directories are a niche
// usage that reduces the portability of an image.
uid, gid := options.IDMap.RootPair()
// unrepresentableOnWindows returns an error describing why a tar entry cannot
// be faithfully created on Windows, or nil if it can (always on non-Windows).
// On Windows ":" is illegal in a filename and "\" is a path separator, so a tar
// name or hardlink target containing them (they use POSIX semantics) would be
// misinterpreted by os.Root (e.g. "a\b" resolved as two components). Symlink
// targets are stored verbatim (not resolved at creation), so they are exempt.
func unrepresentableOnWindows(hdr *tar.Header) error {
if runtime.GOOS != "windows" {
return nil
}
if strings.ContainsAny(hdr.Name, `:\`) {
return fmt.Errorf("entry name %q contains a character Windows cannot represent in a path", hdr.Name)
}
// A hardlink target is resolved within the root by os.Root.Link; a symlink
// target is stored verbatim, so only hardlinks need the target checked.
if hdr.Typeflag == tar.TypeLink && strings.ContainsAny(hdr.Linkname, `:\`) {
return fmt.Errorf("hardlink target %q contains a character Windows cannot represent in a path", hdr.Linkname)
}
return nil
}
err = user.MkdirAllAndChown(parentPath, ImpliedDirectoryMode, uid, gid, user.WithOnlyNew)
// createImpliedDirectories creates all parent directories of dstPath with
// default permissions if they do not already exist. This is necessary because
// the tar format permits implicit directories whose existence is defined only
// by file paths, without corresponding directory headers from which metadata
// could be restored.
//
// The caller must pass a normalized, root-relative local path. Any archive-path
// conversion and resolve-in-root handling must already have been applied.
// Directory creation is performed through root, so it remains confined to the
// extraction destination even if the destination tree changes concurrently.
func createImpliedDirectories(root *os.Root, dstPath string, options *TarOptions) error {
parent := filepath.Dir(dstPath)
// Skip when the parent is the root itself; nothing to create.
if parent == "." || parent == "" {
return nil
}
if _, err := root.Lstat(parent); err == nil {
return nil
} else if !os.IsNotExist(err) {
return err
}
// RootPair() is confined inside this loop as most cases will not require a call, so we can spend some
// unneeded function calls in the uncommon case to encapsulate logic -- implied directories are a niche
// usage that reduces the portability of an image.
uid, gid := options.IDMap.RootPair()
// Similar to [user.MkdirAllAndChown]
//
// [user.MkdirAllAndChown]: https://pkg.go.dev/github.com/moby/sys/user#MkdirAllAndChown
var cur string
for c := range strings.SplitSeq(parent, string(os.PathSeparator)) {
if c == "" {
continue
}
cur = filepath.Join(cur, c)
if err := root.Mkdir(cur, ImpliedDirectoryMode); err != nil {
if !errors.Is(err, os.ErrExist) {
return err
}
fi, err := root.Stat(cur)
if err != nil {
return err
}
if fi.IsDir() {
continue
}
return &os.PathError{Op: "mkdir", Path: cur, Err: syscall.ENOTDIR}
}
if options.NoLchown {
continue
}
// Only the successful Mkdir case is newly-created.
dir, err := root.Open(cur)
if err != nil {
return err
}
if uid != 0 || gid != 0 {
if err := dir.Chown(uid, gid); err != nil {
_ = dir.Close()
return err
}
}
// root.Mkdir applies the mode subject to the process umask, so
// re-apply it with Chmod to guarantee ImpliedDirectoryMode
// independent of umask, matching the previous MkdirAllAndChown
// behavior.
if err := dir.Chmod(ImpliedDirectoryMode); err != nil {
_ = dir.Close()
return err
}
if err := dir.Close(); err != nil {
return err
}
}
@@ -974,9 +1236,6 @@ func untarHandler(tarArchive io.Reader, dest string, options *TarOptions, decomp
if options == nil {
options = &TarOptions{}
}
if options.ExcludePatterns == nil {
options.ExcludePatterns = []string{}
}
r := tarArchive
if decompress {
@@ -984,7 +1243,7 @@ func untarHandler(tarArchive io.Reader, dest string, options *TarOptions, decomp
if err != nil {
return err
}
defer decompressedArchive.Close()
defer func() { _ = decompressedArchive.Close() }()
r = decompressedArchive
}
@@ -998,7 +1257,7 @@ func (archiver *Archiver) TarUntar(src, dst string) error {
if err != nil {
return err
}
defer archive.Close()
defer func() { _ = archive.Close() }()
return archiver.Untar(archive, dst, &TarOptions{
IDMap: archiver.IDMapping,
})
@@ -1010,7 +1269,7 @@ func (archiver *Archiver) UntarPath(src, dst string) error {
if err != nil {
return err
}
defer archive.Close()
defer func() { _ = archive.Close() }()
return archiver.Untar(archive, dst, &TarOptions{
IDMap: archiver.IDMapping,
})
@@ -1070,13 +1329,13 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
defer close(errC)
errC <- func() error {
defer w.Close()
defer func() { _ = w.Close() }()
srcF, err := os.Open(src)
if err != nil {
return err
}
defer srcF.Close()
defer func() { _ = srcF.Close() }()
hdr, err := tarheader.FileInfoHeaderNoLookups(srcSt, "")
if err != nil {
@@ -1087,14 +1346,14 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
hdr.AccessTime = time.Time{}
hdr.ChangeTime = time.Time{}
hdr.Name = filepath.Base(dst)
hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
hdr.Mode = chmodTarEntry(hdr.Mode)
if err := remapIDs(archiver.IDMapping, hdr); err != nil {
return err
}
tw := tar.NewWriter(w)
defer tw.Close()
defer func() { _ = tw.Close() }()
if err := tw.WriteHeader(hdr); err != nil {
return err
}
@@ -1112,7 +1371,7 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
err = archiver.Untar(r, filepath.Dir(dst), nil)
if err != nil {
r.CloseWithError(err)
_ = r.CloseWithError(err)
}
return err
}
+95 -42
View File
@@ -4,45 +4,71 @@ import (
"archive/tar"
"fmt"
"os"
"path"
"path/filepath"
"strings"
"github.com/moby/go-archive/internal/archiveoptions"
"github.com/moby/sys/userns"
"golang.org/x/sys/unix"
)
func withProcSelfFD(opts *TarOptions) (*TarOptions, func(), error) {
procSelfFD, err := os.Open("/proc/self/fd")
if err != nil {
return nil, nil, err
}
var prepared TarOptions
if opts != nil {
prepared = *opts
}
prepared.internalOptions = &archiveoptions.Options{
ProcSelfFD: procSelfFD,
}
return &prepared, func() { _ = procSelfFD.Close() }, nil
}
func getWhiteoutConverter(format WhiteoutFormat) tarWhiteoutConverter {
if format == OverlayWhiteoutFormat {
return overlayWhiteoutConverter{}
return newOverlayWhiteoutConverter()
}
return nil
}
type overlayWhiteoutConverter struct{}
type overlayWhiteoutConverter struct {
opaqueXattr string
}
func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, _ error) {
func newOverlayWhiteoutConverter() overlayWhiteoutConverter {
opaqueXattr := "trusted.overlay.opaque"
if userns.RunningInUserNS() {
opaqueXattr = "user.overlay.opaque"
}
return overlayWhiteoutConverter{
opaqueXattr: opaqueXattr,
}
}
func (c overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, filePath string, fi os.FileInfo) (wo *tar.Header, _ error) {
// convert whiteouts to AUFS format
if fi.Mode()&os.ModeCharDevice != 0 && hdr.Devmajor == 0 && hdr.Devminor == 0 {
// we just rename the file and make it normal
dir, filename := filepath.Split(hdr.Name)
hdr.Name = filepath.Join(dir, WhiteoutPrefix+filename)
dir, filename := path.Split(hdr.Name)
hdr.Name = path.Join(dir, WhiteoutPrefix+filename)
hdr.Mode = 0o600
hdr.Typeflag = tar.TypeReg
hdr.Size = 0
}
if fi.Mode()&os.ModeDir == 0 {
if !fi.IsDir() {
// FIXME(thaJeztah): return a sentinel error instead of nil, nil
return nil, nil
}
opaqueXattrName := "trusted.overlay.opaque"
if userns.RunningInUserNS() {
opaqueXattrName = "user.overlay.opaque"
}
// convert opaque dirs to AUFS format by writing an empty file with the prefix
opaque, err := lgetxattr(path, opaqueXattrName)
opaque, err := lgetxattr(filePath, c.opaqueXattr)
if err != nil {
return nil, err
}
@@ -50,14 +76,14 @@ func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os
// FIXME(thaJeztah): return a sentinel error instead of nil, nil
return nil, nil
}
delete(hdr.PAXRecords, paxSchilyXattr+opaqueXattrName)
delete(hdr.PAXRecords, paxSchilyXattr+c.opaqueXattr)
// create a header for the whiteout file
// it should inherit some properties from the parent, but be a regular file
return &tar.Header{
Typeflag: tar.TypeReg,
Mode: hdr.Mode & int64(os.ModePerm),
Name: filepath.Join(hdr.Name, WhiteoutOpaqueDir), // #nosec G305 -- An archive is being created, not extracted.
Name: path.Join(hdr.Name, WhiteoutOpaqueDir), // #nosec G305 -- An archive is being created, not extracted.
Size: 0,
Uid: hdr.Uid,
Uname: hdr.Uname,
@@ -68,40 +94,67 @@ func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os
}, nil
}
func (c overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool, error) {
base := filepath.Base(path)
dir := filepath.Dir(path)
func (c overlayWhiteoutConverter) ConvertRead(root *os.Root, hdr *tar.Header, filePath string) (bool, error) {
base := filepath.Base(filePath)
dir := filepath.Dir(filePath)
// if a directory is marked as opaque by the AUFS special file, we need to translate that to overlay
if base == WhiteoutOpaqueDir {
opaqueXattrName := "trusted.overlay.opaque"
if userns.RunningInUserNS() {
opaqueXattrName = "user.overlay.opaque"
}
switch base {
case WhiteoutPrefix, WhiteoutPrefix + ".", WhiteoutPrefix + "..":
return false, fmt.Errorf("invalid whiteout entry %q", hdr.Name)
err := unix.Setxattr(dir, opaqueXattrName, []byte{'y'}, 0)
case WhiteoutOpaqueDir:
parent, err := root.Open(dir)
if err != nil {
return false, fmt.Errorf("setxattr('%s', %s=y): %w", dir, opaqueXattrName, err)
}
// don't write the file itself
return false, err
}
// if a file was deleted and we are using overlay, we need to create a character device
if strings.HasPrefix(base, WhiteoutPrefix) {
originalBase := base[len(WhiteoutPrefix):]
originalPath := filepath.Join(dir, originalBase)
if err := unix.Mknod(originalPath, unix.S_IFCHR, 0); err != nil {
return false, fmt.Errorf("failed to mknod('%s', S_IFCHR, 0): %w", originalPath, err)
}
if err := os.Chown(originalPath, hdr.Uid, hdr.Gid); err != nil {
return false, err
}
defer parent.Close()
// don't write the file itself
// If a directory is marked as opaque by the AUFS special file, we need to translate that to overlay.
if err := unix.Fsetxattr(int(parent.Fd()), c.opaqueXattr, []byte{'y'}, 0); err != nil {
return false, fmt.Errorf("fsetxattr('%s', %s=y): %w", dir, c.opaqueXattr, err)
}
// Don't write the whiteout file itself.
return false, nil
default:
originalBase, ok := strings.CutPrefix(base, WhiteoutPrefix)
if !ok {
// Regular file.
return true, nil
}
parent, err := root.Open(dir)
if err != nil {
return false, err
}
defer parent.Close()
// If a file was deleted, and we are using overlay, we need to create a character device.
originalPath := filepath.Join(dir, originalBase)
if err := unix.Mknodat(int(parent.Fd()), originalBase, unix.S_IFCHR, 0); err != nil {
return false, fmt.Errorf("failed to mknod('%s', S_IFCHR, 0): %w", originalPath, err)
}
// Header IDs have already been remapped. Optimize the common non-remapped
// root-owned (0:0) case by assuming the created whiteout has the expected
// ownership, rather than comparing against the effective UID/GID or stat'ing
// the created node to verify it.
if hdr.Uid != 0 || hdr.Gid != 0 {
// TODO(thaJeztah): Revisit whether whiteout ownership needs to be preserved.
//
// This was added in the original overlay whiteout implementation:
// https://github.com/moby/moby/pull/18560 / https://github.com/moby/moby/pull/22126
//
// OverlayFS documents whiteouts in terms of a character device with device
// number 0:0, not ownership: https://docs.kernel.org/filesystems/overlayfs.html#whiteouts-and-opaque-directories
//
// If ownership is not required, this Fchownat can be removed to avoid the remaining TOCTOU window.
if err := unix.Fchownat(int(parent.Fd()), originalBase, hdr.Uid, hdr.Gid, unix.AT_SYMLINK_NOFOLLOW); err != nil {
return false, &os.PathError{Op: "lchown", Path: originalPath, Err: err}
}
}
// Don't write the whiteout file itself.
return false, nil
}
return true, nil
}

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