Compare commits

..
114 Commits
Author SHA1 Message Date
Paweł GronowskiandGitHub c2be9ccfc3 Merge pull request #6887 from thaJeztah/29.x_backport_pin_actions
[29.x backport] ci: pin actions to digests
2026-03-25 15:22:32 +01:00
Sebastiaan van Stijn 0da6a5121f ci: pin actions to digests
As a follow-up, we should use the full version (major.minor.patch).

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 97b9e04a94)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-25 15:06:23 +01:00
Sebastiaan van StijnandGitHub 6b3ca8fc04 Merge pull request #6884 from thaJeztah/29.x_backport_missing_buildtags
[29.x backport] cli/command: add missing "go:build" comments
2026-03-25 15:04:52 +01:00
Paweł GronowskiandGitHub f47603c290 Merge pull request #6885 from thaJeztah/29.x_backport_update_actions
[29.x backport] update actions
2026-03-25 14:23:37 +01:00
dependabot[bot]andSebastiaan van Stijn 9709c8fe08 build(deps): bump docker/metadata-action from 5 to 6
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5 to 6.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](https://github.com/docker/metadata-action/compare/v5...v6)

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

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 39d676c72d)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-25 14:16:00 +01:00
dependabot[bot]andSebastiaan van Stijn af45a23fa9 build(deps): bump docker/bake-action from 6 to 7
Bumps [docker/bake-action](https://github.com/docker/bake-action) from 6 to 7.
- [Release notes](https://github.com/docker/bake-action/releases)
- [Commits](https://github.com/docker/bake-action/compare/v6...v7)

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

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 6453c4c3a7)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-25 14:15:48 +01:00
Paweł GronowskiandGitHub bc97f5ac19 Merge pull request #6883 from thaJeztah/29.x_backport_update_go1.25.8
[29.x backport] update to go1.25.8
2026-03-25 14:14:46 +01:00
Sebastiaan van Stijn 6d71967120 cli/command: add missing "go:build" comments
- commit e8dc2fce32 modernized loops to
  range over int, which requires go1.22 or later.
- commit 85ebca52fd modernized code to
  use stdlib min/max, which requires go1.21 or later.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 9a471180cb)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-25 14:11:15 +01:00
Sebastiaan van Stijn 9b51892b80 update to go1.25.8
go1.25.8 (released 2026-03-05) includes security fixes to the html/template,
net/url, and os packages, as well as bug fixes to the go command, the compiler,
and the os package. See the Go 1.25.8 milestone on our issue tracker for details.

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

---

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

These releases include 5 security fixes following the security policy:

crypto/x509: incorrect enforcement of email constraints

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

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

  This issue only affects Go 1.26.

  Thanks to Jakub Ciolek for reporting this issue.

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

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

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

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

  This issue only affects Go 1.26.

  Thanks to Jakub Ciolek for reporting this issue.

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

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

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

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

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

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

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

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

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

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

- os: FileInfo can escape from a Root

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

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

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

  The FileInfo is now populated using fstatat.

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

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

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit f7d83cbae8)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-25 14:08:46 +01:00
Paweł GronowskiandGitHub 5927d80c76 Merge pull request #6844 from vvoland/update-docker
vendor: github.com/moby/moby/api v1.54.0
2026-03-05 14:22:32 +00:00
Paweł Gronowski 206fc8c165 vendor: github.com/moby/moby/client v0.3.0
full diff: https://github.com/moby/moby/compare/client/v0.2.3-rc.1...client/v0.3.0

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-03-05 15:15:44 +01:00
Paweł Gronowski 874a8df0eb vendor: github.com/moby/moby/api v1.54.0
full diff: https://github.com/moby/moby/compare/api/v1.54.0-rc.1...api/v1.54.0

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-03-05 15:15:04 +01:00
Sebastiaan van StijnandGitHub 964a6d2e98 Merge pull request #6792 from vvoland/bind-create
container/opts: Add bind-create-src mount option
2026-03-05 12:39:04 +01:00
Sebastiaan van StijnandGitHub 210147d3f3 Merge pull request #6843 from docker/dependabot/github_actions/docker/setup-buildx-action-4
build(deps): bump docker/setup-buildx-action from 3 to 4
2026-03-05 11:40:17 +01:00
Sebastiaan van StijnandGitHub 847f547aa1 Merge pull request #6842 from docker/dependabot/github_actions/docker/login-action-4
build(deps): bump docker/login-action from 3 to 4
2026-03-05 11:39:15 +01:00
dependabot[bot]andGitHub 668b3671bd build(deps): bump docker/setup-buildx-action from 3 to 4
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3 to 4.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-05 08:42:54 +00:00
dependabot[bot]andGitHub 30a2ace7f2 build(deps): bump docker/login-action from 3 to 4
Bumps [docker/login-action](https://github.com/docker/login-action) from 3 to 4.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v3...v4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-05 08:42:47 +00:00
Nicolas De LoofandPaweł Gronowski 32aa575aff docs/service: Document bind-create-src
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-03-04 18:00:48 +01:00
Paweł Gronowski c747cff9ab container/opts: Add bind-create-src mount option
Add support for the `bind-create-src` option in bind mounts, which
instructs the daemon to create the source mountpoint on the host if it
doesn't exist.

This allows to replace the legacy `-v /src/dir:/dst` with the `--mount`.

Usage:
--mount type=bind,src=/host/path,dst=/container/path,bind-create-src
--mount type=bind,src=/host/path,dst=/container/path,bind-create-src=true

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-03-04 18:00:01 +01:00
Sebastiaan van StijnandGitHub ddb986472a Merge pull request #6794 from derekmisler/cli-hints-for-docker-ai-after-buildcompose-failur
Fix: run plugin hooks on command failure, not just success
2026-03-04 15:14:57 +01:00
Sebastiaan van StijnandGitHub 5348cf8461 Merge pull request #6836 from docker/dependabot/github_actions/docker/setup-qemu-action-4
build(deps): bump docker/setup-qemu-action from 3 to 4
2026-03-04 14:38:24 +01:00
Sebastiaan van StijnandGitHub 30fb480896 Merge pull request #6784 from thaJeztah/login_cleanups
cli/command/registry: preserve all whitespace in secrets
2026-03-04 12:53:19 +01:00
Sebastiaan van StijnandGitHub 6347345783 Merge pull request #6809 from thaJeztah/compose_rm_utils
cli/compose/loader: remove some wrapper utilities and use errors.Join
2026-03-04 12:52:36 +01:00
dependabot[bot]andGitHub ba349f5afd build(deps): bump docker/setup-qemu-action from 3 to 4
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3 to 4.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-04 08:42:48 +00:00
Sebastiaan van StijnandGitHub 0eda6db75b Merge pull request #6833 from docker/dependabot/go_modules/cmd/docker-trust/go_modules-6b971a9d7e
build(deps): bump go.opentelemetry.io/otel/sdk from 1.38.0 to 1.40.0 in /cmd/docker-trust in the go_modules group across 1 directory
2026-03-02 15:28:07 +01:00
Sebastiaan van Stijn e2cafd657e cli/command/registry: preserve all whitespace in secrets
Preserve all whitespace and treat the secret as an opaque value,
leaving it to the registry to (in)validate. We still check for
empty values in some places.

This partially reverts a21a5f4243,
but checks for empty (whitespace-only) passwords without mutating
the value.

This better aligns with [NIST SP 800-63B §5.1.1.2], which describes
that the value should be treated as opaque, preserving any other whitespace,
including newlines. Note that trimming whitespace may still happen elsewhere
(see [NIST SP 800-63B (revision 4) §3.1.1.2]);
> Verifiers **MAY** make limited allowances for mistyping (e.g., removing
> leading and trailing whitespace characters before verification, allowing
> the verification of passwords with differing cases for the leading character)

[NIST SP 800-63B §5.1.1.2]: https://pages.nist.gov/800-63-3/sp800-63b.html#memsecretver
[NIST SP 800-63B (revision 4) §3.1.1.2]: https://pages.nist.gov/800-63-4/sp800-63b.html#passwordver

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-03-02 15:13:16 +01:00
dependabot[bot]andGitHub 10ebb3b204 build(deps): bump go.opentelemetry.io/otel/sdk
Bumps the go_modules group with 1 update in the /cmd/docker-trust directory: [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go).


Updates `go.opentelemetry.io/otel/sdk` from 1.38.0 to 1.40.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.38.0...v1.40.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/sdk
  dependency-version: 1.40.0
  dependency-type: indirect
  dependency-group: go_modules
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-01 22:11:09 +00:00
Sebastiaan van StijnandGitHub 694d1a034f Merge pull request #6815 from luojiyin1987/fix-typos
Fix various typos in code and documentation
2026-02-28 20:04:24 +01:00
luojiyin 681f15674c Fix typos in code and documentation
Signed-off-by: luojiyin <luojiyin@hotmail.com>
2026-02-28 08:37:04 +08:00
Paweł GronowskiandGitHub 4e5bc6816d Merge pull request #6832 from vvoland/update-docker
vendor: github.com/moby/moby/api v1.54.0-rc.1 & client v0.2.3-rc.1
2026-02-27 19:43:43 +00:00
Paweł Gronowski 0bf060f777 vendor: github.com/moby/moby/client v0.2.3-rc.1
full diff: https://github.com/moby/moby/client/compare/52dc67c0df94...v0.2.3-rc.1

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-02-27 20:34:37 +01:00
Paweł Gronowski 139b58d7f4 vendor: github.com/moby/moby/api v1.54.0-rc.1
full diff: https://github.com/moby/moby/api/compare/52dc67c0df94...v1.54.0-rc.1

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-02-27 20:34:19 +01:00
Paweł GronowskiandGitHub 851b5b7f78 Merge pull request #6829 from thaJeztah/bump_modules
vendor: moby/api v1.54.0-dev, moby/client v0.2.3-dev
2026-02-27 18:00:07 +00:00
Sebastiaan van Stijn 8eedbdc6a8 vendor: moby/api v1.54.0-dev, moby/client v0.2.3-dev
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-27 18:16:02 +01:00
Paweł GronowskiandGitHub afb3212d5b Merge pull request #6827 from thaJeztah/bump_compose
Dockerfile: update compose to v5.1.0, buildx to v0.31.1, mvdan.cc/gofump to v0.9.2
2026-02-27 16:45:45 +00:00
Paweł GronowskiandGitHub 575793f52f Merge pull request #6828 from thaJeztah/bump_runewidth
vendor: github.com/mattn/go-runewidth v0.0.20
2026-02-27 16:45:32 +00:00
Sebastiaan van Stijn cd070a5ed5 vendor: github.com/mattn/go-runewidth v0.0.20
full diff: https://github.com/mattn/go-runewidth/compare/v0.0.19...v0.0.20

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-27 17:28:18 +01:00
Sebastiaan van Stijn 8c3d05398e Dockerfile: update mvdan.cc/gofump to v0.9.2
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-27 17:21:04 +01:00
Sebastiaan van Stijn b206927e0c Dockerfile: update buildx to v0.31.1
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-27 17:20:06 +01:00
Sebastiaan van Stijn 6b5acd3a6e Dockerfile: update compose to v5.1.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-27 17:18:48 +01:00
Paweł GronowskiandGitHub 9e10dc3dca Merge pull request #6824 from thaJeztah/bump_compress2
vendor: github.com/klauspost/compress v1.18.4
2026-02-27 15:39:27 +00:00
Sebastiaan van StijnandGitHub f8a2176b84 Merge pull request #6823 from thaJeztah/bump_otel
vendor: go.opentelemetry.io/otel v1.40.0, go.opentelemetry.io/contrib v0.65.0
2026-02-27 16:33:52 +01:00
Sebastiaan van StijnandGitHub 3169956851 Merge pull request #6822 from docker/dependabot/github_actions/actions/upload-artifact-7
build(deps): bump actions/upload-artifact from 6 to 7
2026-02-27 15:26:48 +01:00
Sebastiaan van Stijn caa8a50468 vendor: github.com/klauspost/compress v1.18.4
full diff: https://github.com/klauspost/compress/compare/v1.18.3...v1.18.4

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-27 15:23:55 +01:00
Sebastiaan van Stijn 5c498778ec vendor: go.opentelemetry.io/contrib v0.65.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-27 15:20:49 +01:00
Sebastiaan van Stijn fbd0e7f7c4 vendor: go.opentelemetry.io/otel v1.40.0
Includes fixes for [GHSA-9h8m-3fm2-qjrq] / [CVE-2026-24051] on macOS

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-27 15:16:34 +01:00
dependabot[bot]andGitHub 95a5a9e709 build(deps): bump actions/upload-artifact from 6 to 7
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-27 08:42:37 +00:00
Sebastiaan van StijnandGitHub 419e5d136c Merge pull request #6813 from dvdksn/fix-link-to-redirect
chore: use canonical url for buildx build cli doc
2026-02-23 17:58:13 +01:00
David Karlsson a7b95f228f chore: use canonical url for buildx build cli doc
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2026-02-23 17:39:19 +01:00
Sebastiaan van Stijn 13c993f101 cli/compose/loader: merge: use errors.Join
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-20 00:10:22 +01:00
Sebastiaan van Stijn 8b6f23d18b cli/compose/loader: remove some wrapper utilities
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-20 00:09:23 +01:00
Sebastiaan van StijnandGitHub d62b88939b Merge pull request #6803 from thaJeztah/compose_fixes
cli/compose: assorted fixes and cleanups
2026-02-19 23:00:44 +01:00
Sebastiaan van Stijn b35a2d0837 cli/compose/loader: remove getLoggingDriver
Inline it in mergeLoggingConfig and add some vars, which also
makes it more readable.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-19 22:48:39 +01:00
Sebastiaan van Stijn 42a211162c cli/compose/loader: mergeServices: inline mapByName
It's now only used once; let's inline it to remove some abstraction.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-19 22:48:38 +01:00
Sebastiaan van Stijn 6e1393089b cli/compose/loader: mergeServices: remove intermediate map for overrides
The code was using an intermediate map, indexed by name, for services
per file. Service-names should be unique per-file, so using an intermediate
map would not benefit us (we'd still have to loop over all of them to
produce the map, and again to iterate over the map)

Remove the intermediate map for overrides, and apply all overrides for
a service instead.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-19 22:45:41 +01:00
Sebastiaan van Stijn 78458e11e1 cli/compose/loader: mergeServices: tidy up and modernize
- construct merge-opts as a slice
- remove intermediate var for overrideServices
- use slices.SortFunc for sorting

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-19 22:45:08 +01:00
Sebastiaan van Stijn 09cf89e82e cli/compose/convert: convertEndpointSpec: fix sorting of ports
The existing code only sorted by PublishedPort (host port), and did
not account for multiple ports mapped to the same host-port, but
using a different protocol.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-19 22:45:08 +01:00
Sebastiaan van Stijn db28780976 cli/compose/convert: convertUlimits: modernize
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-19 22:45:05 +01:00
Derek Misler 830d05d16e error-hooks approach
Signed-off-by: Derek Misler <derek.misler@docker.com>
2026-02-19 10:07:57 -05:00
Derek Misler 9bc18993a4 Fix: run plugin hooks on command failure, not just success
Signed-off-by: Derek Misler <derek.misler@docker.com>
2026-02-19 10:07:57 -05:00
Sebastiaan van StijnandGitHub 44ca067062 Merge pull request #6807 from thaJeztah/less_streamformatter
remove redundant uses of streamformatter in tests
2026-02-19 14:32:57 +01:00
Sebastiaan van Stijn fdebf0afae cli/command/container: fix some unhandled errors in test
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-19 09:42:01 +01:00
Sebastiaan van Stijn e47a5c7734 remove redundant uses of streamformatter in tests
The output of this was not used in the tests, and shouldn't be
needed as part of it.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-19 09:41:17 +01:00
Sebastiaan van StijnandGitHub 63158f7ede Merge pull request #6801 from thaJeztah/no_no_netgo
scripts/build/.variables: don't use "netgo" when building Windows binaries
2026-02-16 20:52:31 +01:00
Sebastiaan van Stijn 2fa6b736d0 scripts/build/.variables: don't use "netgo" when building Windows binaries
commit 880ef756b7 fixed static builds with
CGO, which included setting the `netgo` build-tag for static builds.

Starting with go1.19, the Go runtime on Windows now supports the `netgo` build-
flag to use a native Go DNS resolver. Prior to that version, the build-flag
only had an effect on non-Windows platforms. From the go1.19 release notes:
https://go.dev/doc/go1.19#net

> Resolver.PreferGo is now implemented on Windows and Plan 9. It previously
> only worked on Unix platforms. Combined with Dialer.Resolver and Resolver.Dial,
> it's now possible to write portable programs and be in control of all DNS name
> lookups when dialing.
>
> The net package now has initial support for the netgo build tag on Windows.
> When used, the package uses the Go DNS client (as used by Resolver.PreferGo)
> instead of asking Windows for DNS results. The upstream DNS server it discovers
> from Windows may not yet be correct with complex system network configurations,
> however.

This originally caused issues in the daemon, because the pure-go implementation
did not respect file-based resolution (`C:\Windows\System32\Drivers\etc\hosts`),
resulting in `localhost` not being resolvable, and custom entries in `.etc/hosts`
not being used.

That specific problem was resolved in go1.22 (through [golang/go@33d4a51]), but
other limitations may still apply, and resolver ordering may not respect VPN
adaptors (such as Twingate) and queries sent through the local network adapter
instead of the VPN tunnel, resulting in DNS resolution failures;

    Get "https://example.com:2376/v1.52/containers/json": dial tcp: lookup example.com: i/o timeout

This patch unsets the `netgo` option when (cross-)compiling for Windows, similar
to the patch used for the daemon (see [moby/moby@53d1b12]).

[golang/go@33d4a51]: https://github.com/golang/go/commit/33d4a5105cf2b2d549922e909e9239a48b8cefcc
[moby/moby@53d1b12]: https://github.com/moby/moby/commit/53d1b12bc014b4243e9439fc2610eb4ef863659f

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-14 14:42:55 +01:00
Sebastiaan van StijnandGitHub 7b93d61673 Merge pull request #6790 from thaJeztah/bump_x_deps
vendor: update golang.org/x/* dependencies
2026-02-13 13:30:07 +01:00
Sebastiaan van StijnandGitHub fbb9cb3d73 Merge pull request #6797 from thaJeztah/login_stdin_refactor
cli/command/registry: refactor reading from stdin
2026-02-13 13:28:57 +01:00
Paweł GronowskiandGitHub 49eae5c613 Merge pull request #6798 from vvoland/issues-sbxs
github/issues: Add links for Docker Desktop and Sandboxes
2026-02-12 15:46:14 +00:00
Sebastiaan van Stijn 61f03db682 cli/command/registry: refactor reading from stdin
Extract the code as a utility function, and add some GoDoc to
describe the behavior.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-12 14:43:22 +01:00
Paweł Gronowski 74d4554ccd github/issues: Add emojis
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-02-12 14:38:16 +01:00
Paweł Gronowski 2ebd137abc github/issues: Add links for Docker Desktop and Sandboxes
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-02-12 14:38:07 +01:00
Sebastiaan van StijnandGitHub 63ba295ba4 Merge pull request #6788 from thaJeztah/bump_alpine
Dockerfile: update alpine to 3.23
2026-02-12 14:07:57 +01:00
Sebastiaan van StijnandGitHub 2d194dcf5a Merge pull request #6789 from thaJeztah/bump_golangci_lint
Dockerfile: update golangci-lint to v2.9.0 and fix linting
2026-02-12 14:07:29 +01:00
Sebastiaan van StijnandGitHub 9a41c733c4 Merge pull request #6796 from thaJeztah/login_cleanup_tests
cli/command/registry: remove uses of "gotest.tools/v3/fs"
2026-02-12 14:04:16 +01:00
Sebastiaan van Stijn f5b6055bd1 cli/command/registry: add unit test for --password-stdin
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-12 12:51:54 +01:00
Sebastiaan van Stijn b82e30e58d cli/command/registry: remove uses of "gotest.tools/v3/fs"
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-12 12:37:02 +01:00
Sebastiaan van StijnandGitHub e30ce84dfe Merge pull request #6793 from thaJeztah/update_e2e
e2e: use docker v29.x dind as default
2026-02-11 17:17:23 +01:00
Sebastiaan van StijnandGitHub 3d62a7c806 Merge pull request #6791 from thaJeztah/modernize
modernize: various cleanups
2026-02-11 16:41:36 +01:00
Sebastiaan van Stijn c4fd2406e0 e2e: use docker v29.x dind as default
Also remove groupadd which looks to be redundant now.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 15:52:01 +01:00
Sebastiaan van Stijn fddfe63ef9 modernize: fmtappendf
go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest
    modernize -fmtappendf -fix ./...

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 13:38:38 +01:00
Sebastiaan van Stijn 6d4b3b5f66 modernize: slicescontains
go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest
    modernize -slicescontains -fix ./...

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 13:35:26 +01:00
Sebastiaan van Stijn 835d510b78 modernize: stringsseq
go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest
    modernize -stringsseq -fix ./...

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 13:31:11 +01:00
Sebastiaan van Stijn dd73e2df77 modernize: reflecttypefor
go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest
    modernize -reflecttypefor -fix ./...

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 13:19:10 +01:00
Sebastiaan van Stijn 7f5bb1e99c modernize: testingcontext
go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest
    modernize -testingcontext -fix ./...

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 13:17:36 +01:00
Sebastiaan van Stijn 2875e48024 modernize: stringscut
go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest
    modernize -stringscut -fix ./...

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 13:16:13 +01:00
Sebastiaan van Stijn 4c7d40cf77 modernize: mapsloop
go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest
    modernize -mapsloop -fix ./...

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 13:14:37 +01:00
Sebastiaan van Stijn 85ebca52fd modernize: minmax
go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest
    modernize -minmax -fix ./...

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 13:08:46 +01:00
Sebastiaan van Stijn e8dc2fce32 modernize: rangeint
go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest
    modernize -rangeint -fix ./...

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 13:07:01 +01:00
Sebastiaan van Stijn e4f6019e62 vendor: golang.org/x/net v0.50.0
full diff: https://cs.opensource.google/go/x/net/+/refs/tags/v0.49.0...refs/tags/v0.50.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 12:38:03 +01:00
Sebastiaan van Stijn 464e14c68e vendor: golang.org/x/term v0.40.0
full diff: https://cs.opensource.google/go/x/term/+/refs/tags/v0.39.0...refs/tags/v0.40.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 12:36:59 +01:00
Sebastiaan van Stijn 1e31c2825e vendor: golang.org/x/text v0.34.0
full diff: https://cs.opensource.google/go/x/text/+/refs/tags/v0.33.0...refs/tags/v0.34.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 12:36:12 +01:00
Sebastiaan van Stijn 355f7bb602 vendor: golang.org/x/sys v0.41.0
full diff: https://cs.opensource.google/go/x/sys/+/refs/tags/v0.40.0...refs/tags/v0.41.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 12:35:22 +01:00
Sebastiaan van Stijn a934c75de7 Dockerfile: update golangci-lint to v2.9.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 11:30:26 +01:00
Sebastiaan van Stijn ab06aebd4b internal/volumespec: fix prealloc linting
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 11:30:26 +01:00
Sebastiaan van Stijn 9a0c78fdc0 cli-plugins/manager: fix prealloc linting
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 11:30:26 +01:00
Sebastiaan van Stijn 2e544d6308 cli/command: fix prealloc linting
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 11:30:26 +01:00
Sebastiaan van Stijn 12a0b0b7b9 cli/compose: fix prealloc linting
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 11:30:26 +01:00
Sebastiaan van Stijn 99cef6f700 opts: fix prealloc linting
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 11:30:25 +01:00
Sebastiaan van Stijn 8a8a3e1309 opts/swarmopts: fix prealloc linting
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 11:30:25 +01:00
Sebastiaan van Stijn 9d2816c8a5 remove outdated "nolint" comments
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 11:30:25 +01:00
Sebastiaan van Stijn eaba9ecf18 cli/connhelper/ssh: remove outdated "nolint" comment
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-11 11:30:25 +01:00
Paweł GronowskiandGitHub 3e466c88e1 Merge pull request #6787 from thaJeztah/fix_vol_prune_example
docs: fix docker volume prune example
2026-02-11 10:24:13 +00:00
Sebastiaan van Stijn 1f6b319d60 Dockerfile: update alpine to 3.23
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-10 20:41:08 +01:00
Sebastiaan van Stijn b598f8f0b8 docs: fix docker volume prune example
It doesn't remove named volumes by default.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-10 19:11:12 +01:00
Sebastiaan van StijnandGitHub df016a3a95 Merge pull request #6785 from thaJeztah/docs_fixes
docs: fix typos
2026-02-09 19:17:31 +01:00
Sebastiaan van Stijn 5eb91665d1 docs: fix typos
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-08 18:10:21 +01:00
Sebastiaan van StijnandGitHub 727bc3ea5b Merge pull request #6776 from whyvineet/fix-typo
docs: fix typo in dockerd.md for 'replacement'
2026-02-05 14:03:50 +01:00
Sebastiaan van StijnandGitHub b0e1e0995d Merge pull request #6781 from thaJeztah/bake_use_dockerfile_defaults
docker-bake.hcl: use default GO_VERSION from Dockerfile
2026-02-05 13:47:09 +01:00
Sebastiaan van Stijn d6c6bbf574 docker-bake.hcl: use default GO_VERSION from Dockerfile
Use the defaults as specified in the Dockerfile, unless set;
https://docs.docker.com/build/bake/reference/#variable

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-05 13:22:56 +01:00
Sebastiaan van StijnandGitHub 7de3e08796 Merge pull request #6780 from thaJeztah/bump_go1.25.7
update to go1.25.7
2026-02-05 13:08:23 +01:00
Sebastiaan van Stijn 2d5d0842c5 update to go1.25.7
go1.25.7 (released 2026-02-04) includes security fixes to the go command
and the crypto/tls package, as well as bug fixes to the compiler and the
crypto/x509 package. See the Go 1.25.7 milestone on our issue tracker for
details:
https://github.com/golang/go/issues?q=milestone%3AGo1.25.7+label%3ACherryPickApproved

full diff: https://github.com/golang/go/compare/go1.25.6...go1.25.7

From the security mailing list:

> Hello gophers,
>
> We have just released Go versions 1.25.7 and 1.24.13, minor point releases.
>
> These releases include 2 security fixes following the security policy:
>
> - cmd/cgo: remove user-content from doc strings in cgo ASTs
>
>   A discrepancy between how Go and C/C++ comments
>   were parsed allowed for code smuggling into the
>   resulting cgo binary.
>
>   To prevent this behavior, the cgo compiler
>   will no longer parse user-provided doc
>   comments.
>
>   Thank you to RyotaK (https://ryotak.net) of
>   GMO Flatt Security Inc. for reporting this issue.
>
>   This is CVE-2025-61732 and https://go.dev/issue/76697.
>
> - crypto/tls: unexpected session resumption when using Config.GetConfigForClient
>
>   Config.GetConfigForClient is documented to use the original Config's session
>   ticket keys unless explicitly overridden. This can cause unexpected behavior if
>   the returned Config modifies authentication parameters, like ClientCAs: a
>   connection initially established with the parent (or a sibling) Config can be
>   resumed, bypassing the modified authentication requirements.
>
>   If ClientAuth is VerifyClientCertIfGiven or RequireAndVerifyClientCert (on the
>   server) or InsecureSkipVerify is false (on the client), crypto/tls now checks
>   that the root of the previously-verified chain is still in ClientCAs/RootCAs
>   when resuming a connection.
>
>   Go 1.26 Release Candidate 2, Go 1.25.6, and Go 1.24.12 had fixed a similar issue
>   related to session ticket keys being implicitly shared by Config.Clone. Since
>   this fix is broader, the Config.Clone behavior change has been reverted.
>
>   Note that VerifyPeerCertificate still behaves as documented: it does not apply
>   to resumed connections. Applications that use Config.GetConfigForClient or
>   Config.Clone and do not wish to blindly resume connections established with the
>   original Config must use VerifyConnection instead (or SetSessionTicketKeys or
>   SessionTicketsDisabled).
>
>   Thanks to Coia Prant (github.com/rbqvq) for reporting this issue.
>
>   This updates CVE-2025-68121 and Go issue https://go.dev/issue/77217.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-05 12:42:11 +01:00
Vineet Kumar def41fe652 docs: fix typo in dockerd.md for 'replacement'
Signed-off-by: Vineet Kumar <vineetkumar17112004@gmail.com>
2026-02-04 23:20:34 +05:30
Sebastiaan van StijnandGitHub 769e75a0ee Merge pull request #6775 from thaJeztah/bump_xx
Dockerfile: update tonistiigi/xx to v1.9.0
2026-02-04 14:13:33 +01:00
Sebastiaan van Stijn 58413ca113 Dockerfile: update tonistiigi/xx to v1.9.0
full diff: https://github.com/tonistiigi/xx/compare/v1.7.0...v1.9.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-02-04 12:50:49 +01:00
347 changed files with 34228 additions and 55432 deletions
+9 -3
View File
@@ -1,11 +1,17 @@
blank_issues_enabled: false
contact_links:
- name: "Contributing to Docker"
- name: "🗃️ Docker Sandboxes: report an issue"
about: "Issues with Docker Sandboxes should be filed in the Docker Desktop feedback tracker (not docker/cli)."
url: "https://github.com/docker/desktop-feedback/issues"
- name: "🖥️ Docker Desktop: report an issue"
about: "General Docker Desktop issues (installation, upgrades, UI, networking on macOS/Windows, WSL2, etc.) should be filed in the Docker Desktop feedback tracker (not docker/cli)."
url: "https://github.com/docker/desktop-feedback/issues"
- name: "🧑‍💻 Contributing to Docker"
about: "Read guidelines and tips about contributing to Docker."
url: "https://github.com/docker/cli/blob/master/CONTRIBUTING.md"
- name: "Security and Vulnerabilities"
- name: "🔒 Security and Vulnerabilities"
about: "Report any security issues or vulnerabilities responsibly to the Docker security team. Do not use the public issue tracker."
url: "https://github.com/moby/moby/security/policy"
- name: "General Support"
- name: "💬 General Support"
about: "Get the help you need to build, share, and run your Docker applications"
url: "https://www.docker.com/support/"
+2
View File
@@ -7,3 +7,5 @@ updates:
labels:
- "area/testing"
- "status/2-code-review"
cooldown:
default-days: 7
+12 -12
View File
@@ -35,7 +35,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
-
name: Create matrix
id: platforms
@@ -63,10 +63,10 @@ jobs:
steps:
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
-
name: Build
uses: docker/bake-action@v6
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
with:
targets: ${{ matrix.target }}
set: |
@@ -88,7 +88,7 @@ jobs:
fi
-
name: Upload artifacts
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: ${{ env.ARTIFACT_NAME }}
path: /tmp/out/*
@@ -101,20 +101,20 @@ jobs:
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4
with:
username: ${{ secrets.DOCKERHUB_CLIBIN_USERNAME }}
password: ${{ secrets.DOCKERHUB_CLIBIN_TOKEN }}
-
name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
-
name: Docker meta
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
with:
images: dockereng/cli-bin
tags: |
@@ -125,7 +125,7 @@ jobs:
type=semver,pattern={{major}}.{{minor}}
-
name: Build and push image
uses: docker/bake-action@v6
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
with:
files: |
./docker-bake.hcl
@@ -143,7 +143,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
-
name: Create matrix
id: platforms
@@ -165,10 +165,10 @@ jobs:
steps:
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
-
name: Build
uses: docker/bake-action@v6
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
with:
targets: plugins-cross
set: |
+7 -6
View File
@@ -46,7 +46,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 2
# CodeQL 2.16.4's auto-build added support for multi-module repositories,
@@ -61,19 +61,20 @@ jobs:
ln -s vendor.sum go.sum
-
name: Update Go
uses: actions/setup-go@v6
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
with:
go-version: "1.25.6"
go-version: "1.25.8"
cache: false
-
name: Initialize CodeQL
uses: github/codeql-action/init@v4
uses: github/codeql-action/init@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
with:
languages: go
-
name: Autobuild
uses: github/codeql-action/autobuild@v4
uses: github/codeql-action/autobuild@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
-
name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
uses: github/codeql-action/analyze@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
with:
category: "/language:go"
+3 -3
View File
@@ -44,7 +44,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
-
name: Update daemon.json
run: |
@@ -63,7 +63,7 @@ jobs:
docker info
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
-
name: Run ${{ matrix.target }}
run: |
@@ -74,7 +74,7 @@ jobs:
TESTFLAGS: -coverprofile=/tmp/coverage/coverage.txt
-
name: Send to Codecov
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
with:
files: ./build/coverage/coverage.txt
token: ${{ secrets.CODECOV_TOKEN }}
+8 -7
View File
@@ -30,15 +30,15 @@ jobs:
steps:
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
-
name: Test
uses: docker/bake-action@v6
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
with:
targets: test-coverage
-
name: Send to Codecov
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
with:
files: ./build/coverage/coverage.txt
token: ${{ secrets.CODECOV_TOKEN }}
@@ -60,14 +60,15 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
path: ${{ env.GOPATH }}/src/github.com/docker/cli
-
name: Set up Go
uses: actions/setup-go@v6
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
with:
go-version: "1.25.6"
go-version: "1.25.8"
cache: false
-
name: Test
run: |
@@ -80,7 +81,7 @@ jobs:
shell: bash
-
name: Send to Codecov
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
with:
files: /tmp/coverage.txt
working-directory: ${{ env.GOPATH }}/src/github.com/docker/cli
+3 -3
View File
@@ -38,7 +38,7 @@ jobs:
steps:
-
name: Run
uses: docker/bake-action@v6
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
with:
targets: ${{ matrix.target }}
@@ -48,7 +48,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
-
name: Generate
shell: 'script --return --quiet --command "bash {0}"'
@@ -74,7 +74,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
-
name: Run
shell: 'script --return --quiet --command "bash {0}"'
+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.25.6"
go: "1.25.8"
timeout: 5m
+5 -5
View File
@@ -5,14 +5,14 @@ ARG BASE_VARIANT=alpine
# 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
# that's also available as alpine image variant for the Golang version used.
ARG ALPINE_VERSION=3.22
ARG ALPINE_VERSION=3.23
ARG BASE_DEBIAN_DISTRO=bookworm
ARG GO_VERSION=1.25.6
ARG GO_VERSION=1.25.8
# 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.
ARG XX_VERSION=1.7.0
ARG XX_VERSION=1.9.0
# GOVERSIONINFO_VERSION is the version of GoVersionInfo to install.
# It must be a valid tag from https://github.com/josephspurrier/goversioninfo
@@ -25,12 +25,12 @@ ARG GOTESTSUM_VERSION=v1.13.0
# BUILDX_VERSION sets the version of buildx to use for the e2e tests.
# It must be a tag in the docker.io/docker/buildx-bin image repository
# on Docker Hub.
ARG BUILDX_VERSION=0.29.1
ARG BUILDX_VERSION=0.31.1
# COMPOSE_VERSION is the version of compose to install in the dev container.
# It must be a tag in the docker.io/docker/compose-bin image repository
# on Docker Hub.
ARG COMPOSE_VERSION=v2.40.0
ARG COMPOSE_VERSION=v5.1.0
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
+1 -1
View File
@@ -151,7 +151,7 @@ func TestValidateCandidate(t *testing.T) {
assert.ErrorContains(t, err, tc.err)
case tc.invalid != "":
assert.NilError(t, err)
assert.Assert(t, is.ErrorType(p.Err, reflect.TypeOf(&pluginError{})))
assert.Assert(t, is.ErrorType(p.Err, reflect.TypeFor[*pluginError]()))
assert.ErrorContains(t, p.Err, tc.invalid)
default:
assert.NilError(t, err)
+1 -1
View File
@@ -57,7 +57,7 @@ func AddPluginCommandStubs(dockerCLI config.Provider, rootCmd *cobra.Command) (e
},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
// Delegate completion to plugin
cargs := []string{p.Path, cobra.ShellCompRequestCmd, p.Name}
cargs := []string{p.Path, cobra.ShellCompRequestCmd, p.Name} //nolint:prealloc // no need to over-complicate things.
cargs = append(cargs, args...)
cargs = append(cargs, toComplete)
os.Args = cargs
+1 -1
View File
@@ -28,7 +28,7 @@ func (e *pluginError) Unwrap() error {
return e.cause
}
// MarshalText marshalls the pluginError into a textual form.
// MarshalText marshals the pluginError into a textual form.
func (e *pluginError) MarshalText() (text []byte, err error) {
return []byte(e.cause.Error()), nil
}
+31 -8
View File
@@ -1,3 +1,6 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
package manager
import (
@@ -39,11 +42,11 @@ func RunCLICommandHooks(ctx context.Context, dockerCLI config.Provider, rootCmd,
// RunPluginHooks is the entrypoint for the hooks execution flow
// after a plugin command was just executed by the CLI.
func RunPluginHooks(ctx context.Context, dockerCLI config.Provider, rootCmd, subCommand *cobra.Command, args []string) {
func RunPluginHooks(ctx context.Context, dockerCLI config.Provider, rootCmd, subCommand *cobra.Command, args []string, cmdErrorMessage string) {
commandName := strings.Join(args, " ")
flags := getNaiveFlags(args)
runHooks(ctx, dockerCLI.ConfigFile(), rootCmd, subCommand, commandName, flags, "")
runHooks(ctx, dockerCLI.ConfigFile(), rootCmd, subCommand, commandName, flags, cmdErrorMessage)
}
func runHooks(ctx context.Context, cfg *configfile.ConfigFile, rootCmd, subCommand *cobra.Command, invokedCommand string, flags map[string]string, cmdErrorMessage string) {
@@ -67,7 +70,7 @@ func invokeAndCollectHooks(ctx context.Context, cfg *configfile.ConfigFile, root
pluginDirs := getPluginDirs(cfg)
nextSteps := make([]string, 0, len(pluginsCfg))
for pluginName, pluginCfg := range pluginsCfg {
match, ok := pluginMatch(pluginCfg, subCmdStr)
match, ok := pluginMatch(pluginCfg, subCmdStr, cmdErrorMessage)
if !ok {
continue
}
@@ -135,14 +138,34 @@ func appendNextSteps(nextSteps []string, processed []string) ([]string, bool) {
// command being executed (such as 'image ls' the root 'docker' is omitted)
// and, if the configuration includes a hook for the invoked command, returns
// the configured hook string.
func pluginMatch(pluginCfg map[string]string, subCmd string) (string, bool) {
configuredPluginHooks, ok := pluginCfg["hooks"]
if !ok || configuredPluginHooks == "" {
//
// Plugins can declare two types of hooks in their configuration:
// - "hooks": fires on every command invocation (success or failure)
// - "error-hooks": fires only when a command fails (cmdErrorMessage is non-empty)
func pluginMatch(pluginCfg map[string]string, subCmd string, cmdErrorMessage string) (string, bool) {
// Check "hooks" first — these always fire regardless of command outcome.
if match, ok := matchHookConfig(pluginCfg["hooks"], subCmd); ok {
return match, true
}
// Check "error-hooks" — these only fire when there was an error.
if cmdErrorMessage != "" {
if match, ok := matchHookConfig(pluginCfg["error-hooks"], subCmd); ok {
return match, true
}
}
return "", false
}
// matchHookConfig checks if a comma-separated hook configuration string
// contains a prefix match for the given subcommand.
func matchHookConfig(configuredHooks string, subCmd string) (string, bool) {
if configuredHooks == "" {
return "", false
}
commands := strings.Split(configuredPluginHooks, ",")
for _, hookCmd := range commands {
for hookCmd := range strings.SplitSeq(configuredHooks, ",") {
if hookMatch(hookCmd, subCmd) {
return hookCmd, true
}
+239 -7
View File
@@ -1,12 +1,23 @@
package manager
import (
"context"
"testing"
"github.com/docker/cli/cli/config/configfile"
"github.com/spf13/cobra"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
type fakeConfigProvider struct {
cfg *configfile.ConfigFile
}
func (f *fakeConfigProvider) ConfigFile() *configfile.ConfigFile {
return f.cfg
}
func TestGetNaiveFlags(t *testing.T) {
testCases := []struct {
args []string
@@ -40,12 +51,15 @@ func TestGetNaiveFlags(t *testing.T) {
func TestPluginMatch(t *testing.T) {
testCases := []struct {
commandString string
pluginConfig map[string]string
expectedMatch string
expectedOk bool
doc string
commandString string
pluginConfig map[string]string
cmdErrorMessage string
expectedMatch string
expectedOk bool
}{
{
doc: "hooks prefix match",
commandString: "image ls",
pluginConfig: map[string]string{
"hooks": "image",
@@ -54,6 +68,7 @@ func TestPluginMatch(t *testing.T) {
expectedOk: true,
},
{
doc: "hooks no match",
commandString: "context ls",
pluginConfig: map[string]string{
"hooks": "build",
@@ -62,6 +77,7 @@ func TestPluginMatch(t *testing.T) {
expectedOk: false,
},
{
doc: "hooks exact match",
commandString: "context ls",
pluginConfig: map[string]string{
"hooks": "context ls",
@@ -70,6 +86,7 @@ func TestPluginMatch(t *testing.T) {
expectedOk: true,
},
{
doc: "hooks first match wins",
commandString: "image ls",
pluginConfig: map[string]string{
"hooks": "image ls,image",
@@ -78,6 +95,7 @@ func TestPluginMatch(t *testing.T) {
expectedOk: true,
},
{
doc: "hooks empty string",
commandString: "image ls",
pluginConfig: map[string]string{
"hooks": "",
@@ -86,6 +104,7 @@ func TestPluginMatch(t *testing.T) {
expectedOk: false,
},
{
doc: "hooks partial token no match",
commandString: "image inspect",
pluginConfig: map[string]string{
"hooks": "image i",
@@ -94,6 +113,7 @@ func TestPluginMatch(t *testing.T) {
expectedOk: false,
},
{
doc: "hooks prefix token match",
commandString: "image inspect",
pluginConfig: map[string]string{
"hooks": "image",
@@ -101,12 +121,140 @@ func TestPluginMatch(t *testing.T) {
expectedMatch: "image",
expectedOk: true,
},
{
doc: "error-hooks match on error",
commandString: "build",
pluginConfig: map[string]string{
"error-hooks": "build",
},
cmdErrorMessage: "exit status 1",
expectedMatch: "build",
expectedOk: true,
},
{
doc: "error-hooks no match on success",
commandString: "build",
pluginConfig: map[string]string{
"error-hooks": "build",
},
cmdErrorMessage: "",
expectedMatch: "",
expectedOk: false,
},
{
doc: "error-hooks prefix match on error",
commandString: "compose up",
pluginConfig: map[string]string{
"error-hooks": "compose",
},
cmdErrorMessage: "exit status 1",
expectedMatch: "compose",
expectedOk: true,
},
{
doc: "error-hooks no match for wrong command",
commandString: "pull",
pluginConfig: map[string]string{
"error-hooks": "build",
},
cmdErrorMessage: "exit status 1",
expectedMatch: "",
expectedOk: false,
},
{
doc: "hooks takes precedence over error-hooks",
commandString: "build",
pluginConfig: map[string]string{
"hooks": "build",
"error-hooks": "build",
},
cmdErrorMessage: "exit status 1",
expectedMatch: "build",
expectedOk: true,
},
{
doc: "hooks fires on success even with error-hooks configured",
commandString: "build",
pluginConfig: map[string]string{
"hooks": "build",
"error-hooks": "build",
},
cmdErrorMessage: "",
expectedMatch: "build",
expectedOk: true,
},
{
doc: "error-hooks with multiple commands",
commandString: "compose up",
pluginConfig: map[string]string{
"error-hooks": "build,compose up,pull",
},
cmdErrorMessage: "exit status 1",
expectedMatch: "compose up",
expectedOk: true,
},
}
for _, tc := range testCases {
match, ok := pluginMatch(tc.pluginConfig, tc.commandString)
assert.Equal(t, ok, tc.expectedOk)
assert.Equal(t, match, tc.expectedMatch)
t.Run(tc.doc, func(t *testing.T) {
match, ok := pluginMatch(tc.pluginConfig, tc.commandString, tc.cmdErrorMessage)
assert.Equal(t, ok, tc.expectedOk)
assert.Equal(t, match, tc.expectedMatch)
})
}
}
func TestMatchHookConfig(t *testing.T) {
testCases := []struct {
doc string
configuredHooks string
subCmd string
expectedMatch string
expectedOk bool
}{
{
doc: "empty config",
configuredHooks: "",
subCmd: "build",
expectedMatch: "",
expectedOk: false,
},
{
doc: "exact match",
configuredHooks: "build",
subCmd: "build",
expectedMatch: "build",
expectedOk: true,
},
{
doc: "prefix match",
configuredHooks: "image",
subCmd: "image ls",
expectedMatch: "image",
expectedOk: true,
},
{
doc: "comma-separated match",
configuredHooks: "pull,build,push",
subCmd: "build",
expectedMatch: "build",
expectedOk: true,
},
{
doc: "no match",
configuredHooks: "pull,push",
subCmd: "build",
expectedMatch: "",
expectedOk: false,
},
}
for _, tc := range testCases {
t.Run(tc.doc, func(t *testing.T) {
match, ok := matchHookConfig(tc.configuredHooks, tc.subCmd)
assert.Equal(t, ok, tc.expectedOk)
assert.Equal(t, match, tc.expectedMatch)
})
}
}
@@ -141,3 +289,87 @@ func TestAppendNextSteps(t *testing.T) {
})
}
}
func TestRunPluginHooksPassesErrorMessage(t *testing.T) {
cfg := configfile.New("")
cfg.Plugins = map[string]map[string]string{
"test-plugin": {"hooks": "build"},
}
provider := &fakeConfigProvider{cfg: cfg}
root := &cobra.Command{Use: "docker"}
sub := &cobra.Command{Use: "build"}
root.AddCommand(sub)
// Should not panic with empty error message (success case)
RunPluginHooks(context.Background(), provider, root, sub, []string{"build"}, "")
// Should not panic with non-empty error message (failure case)
RunPluginHooks(context.Background(), provider, root, sub, []string{"build"}, "exit status 1")
}
func TestRunPluginHooksErrorHooks(t *testing.T) {
cfg := configfile.New("")
cfg.Plugins = map[string]map[string]string{
"test-plugin": {"error-hooks": "build"},
}
provider := &fakeConfigProvider{cfg: cfg}
root := &cobra.Command{Use: "docker"}
sub := &cobra.Command{Use: "build"}
root.AddCommand(sub)
// Should not panic — error-hooks with error message
RunPluginHooks(context.Background(), provider, root, sub, []string{"build"}, "exit status 1")
// Should not panic — error-hooks with no error (should be skipped)
RunPluginHooks(context.Background(), provider, root, sub, []string{"build"}, "")
}
func TestInvokeAndCollectHooksErrorHooksSkippedOnSuccess(t *testing.T) {
cfg := configfile.New("")
cfg.Plugins = map[string]map[string]string{
"nonexistent": {"error-hooks": "build"},
}
root := &cobra.Command{Use: "docker"}
sub := &cobra.Command{Use: "build"}
root.AddCommand(sub)
// On success, error-hooks should not match, so the plugin
// binary is never looked up and no results are returned.
result := invokeAndCollectHooks(
context.Background(), cfg, root, sub,
"build", map[string]string{}, "",
)
assert.Check(t, is.Len(result, 0))
}
func TestInvokeAndCollectHooksNoPlugins(t *testing.T) {
cfg := configfile.New("")
root := &cobra.Command{Use: "docker"}
sub := &cobra.Command{Use: "build"}
root.AddCommand(sub)
result := invokeAndCollectHooks(
context.Background(), cfg, root, sub,
"build", map[string]string{}, "some error",
)
assert.Check(t, is.Len(result, 0))
}
func TestInvokeAndCollectHooksCancelledContext(t *testing.T) {
cfg := configfile.New("")
cfg.Plugins = map[string]map[string]string{
"test-plugin": {"hooks": "build"},
}
root := &cobra.Command{Use: "docker"}
sub := &cobra.Command{Use: "build"}
root.AddCommand(sub)
ctx, cancel := context.WithCancel(context.Background())
cancel() // cancel immediately
result := invokeAndCollectHooks(
ctx, cfg, root, sub,
"build", map[string]string{}, "exit status 1",
)
assert.Check(t, is.Nil(result))
}
+1 -1
View File
@@ -172,7 +172,7 @@ func ListPlugins(dockerCli config.Provider, rootcmd *cobra.Command) ([]Plugin, e
}
// PluginRunCommand returns an [os/exec.Cmd] which when [os/exec.Cmd.Run] will execute the named plugin.
// The rootcmd argument is referenced to determine the set of builtin commands in order to detect conficts.
// The rootcmd argument is referenced to determine the set of builtin commands in order to detect conflicts.
// The error returned satisfies the [errdefs.IsNotFound] predicate if no plugin was found or if the first candidate plugin was invalid somehow.
func PluginRunCommand(dockerCli config.Provider, name string, rootcmd *cobra.Command) (*exec.Cmd, error) {
// This uses the full original args, not the args which may
+1 -1
View File
@@ -89,7 +89,7 @@ func TestListPluginCandidatesEmpty(t *testing.T) {
// Regression test for https://github.com/docker/cli/issues/5643.
// Check that inaccessible directories that come before accessible ones are ignored
// and do not prevent the latter from being processed.
func TestListPluginCandidatesInaccesibleDir(t *testing.T) {
func TestListPluginCandidatesInaccessibleDir(t *testing.T) {
dir := fs.NewDir(t, t.Name(),
fs.WithDir("no-perm", fs.WithMode(0)),
fs.WithDir("plugins",
+1 -1
View File
@@ -93,7 +93,7 @@ func (pl *PluginServer) Addr() net.Addr {
// Close ensures that the server is no longer accepting new connections and
// closes all existing connections. Existing connections will receive [io.EOF].
//
// The error value is that of the underlying [net.Listner.Close] call.
// The error value is that of the underlying [net.Listener.Close] call.
func (pl *PluginServer) Close() error {
if pl == nil {
return nil
+1 -1
View File
@@ -47,7 +47,7 @@ func TestPluginServer(t *testing.T) {
select {
case err := <-done:
if !errors.Is(err, io.EOF) {
t.Fatalf("exepcted EOF error, got: %v", err)
t.Fatalf("expected EOF error, got: %v", err)
}
case <-time.After(10 * time.Millisecond):
}
+2 -2
View File
@@ -1,4 +1,4 @@
// FIXME(vvoland): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
package container
@@ -902,7 +902,7 @@ func convertToStandardNotation(ports []string) ([]string, error) {
for _, publish := range ports {
if strings.Contains(publish, "=") {
params := map[string]string{"protocol": "tcp"}
for _, param := range strings.Split(publish, ",") {
for param := range strings.SplitSeq(publish, ",") {
k, v, ok := strings.Cut(param, "=")
if !ok || k == "" {
return optsList, fmt.Errorf("invalid publish opts format (should be name=value but got '%s')", param)
+3 -13
View File
@@ -16,8 +16,6 @@ import (
"github.com/moby/moby/api/types"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"github.com/moby/moby/client/pkg/progress"
"github.com/moby/moby/client/pkg/streamformatter"
"github.com/spf13/pflag"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
@@ -129,7 +127,7 @@ func TestRunAttach(t *testing.T) {
}
// end stream from "container" so that we'll detach
conn.Close()
assert.NilError(t, conn.Close())
select {
case cmdErr := <-cmdErrC:
@@ -209,7 +207,7 @@ func TestRunAttachTermination(t *testing.T) {
}
assert.NilError(t, syscall.Kill(syscall.Getpid(), syscall.SIGTERM))
conn.Close()
assert.NilError(t, conn.Close())
select {
case <-killCh:
@@ -245,20 +243,12 @@ func TestRunPullTermination(t *testing.T) {
_ = server.Close()
})
go func() {
id := test.RandomID()[:12] // short-ID
progressOutput := streamformatter.NewJSONProgressOutput(server, true)
for i := 0; i < 100; i++ {
for range 100 {
select {
case <-ctx.Done():
assert.NilError(t, server.Close(), "failed to close imageCreateFunc server")
return
default:
assert.NilError(t, progressOutput.WriteProgress(progress.Progress{
ID: id,
Message: "Downloading",
Current: int64(i),
Total: 100,
}))
time.Sleep(100 * time.Millisecond)
}
}
+1 -2
View File
@@ -11,8 +11,7 @@ import (
)
func TestForwardSignals(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ctx := t.Context()
called := make(chan struct{})
apiClient := &fakeClient{containerKillFunc: func(ctx context.Context, container string, options client.ContainerKillOptions) (client.ContainerKillResult, error) {
+4 -1
View File
@@ -1,3 +1,6 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
package container
import (
@@ -337,7 +340,7 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
return err
}
for _, line := range strings.Split(statsTextBuffer.String(), "\n") {
for line := range strings.SplitSeq(statsTextBuffer.String(), "\n") {
// In case the new text is shorter than the one we are writing over,
// we'll append the "erase line" escape sequence to clear the remaining text.
_, _ = fmt.Fprintln(&statsTextBuffer, line, "\033[K")
+4 -1
View File
@@ -1,3 +1,6 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
package container
import (
@@ -60,7 +63,7 @@ func initTtySize(ctx context.Context, cli command.Cli, id string, isExec bool, r
if err := rTTYfunc(ctx, cli, id, isExec); err != nil {
go func() {
var err error
for retry := 0; retry < 10; retry++ {
for retry := range 10 {
time.Sleep(time.Duration(retry+1) * 10 * time.Millisecond)
if err = rTTYfunc(ctx, cli, id, isExec); err == nil {
break
+2 -3
View File
@@ -6,6 +6,7 @@ package command
import (
"encoding/json"
"errors"
"maps"
"github.com/docker/cli/cli/context/store"
)
@@ -23,9 +24,7 @@ func (dc DockerContext) MarshalJSON() ([]byte, error) {
s["Description"] = dc.Description
}
if dc.AdditionalFields != nil {
for k, v := range dc.AdditionalFields {
s[k] = v
}
maps.Copy(s, dc.AdditionalFields)
}
return json.Marshal(s)
}
+1 -1
View File
@@ -350,7 +350,7 @@ func DisplayablePorts(ports []container.PortSummary) string {
last uint16
}
groupMap := make(map[string]*portGroup)
var result []string //nolint:prealloc
var result []string
var hostMappings []string
var groupMapKeys []string
sort.Slice(ports, func(i, j int) bool {
@@ -25,7 +25,7 @@ func (b *buffer) Write(buf []byte) (written int, err error) {
m := len(buf)
if n+m <= cap(b.a) {
b.a = b.a[0 : n+m]
for i := 0; i < m; i++ {
for i := range m {
b.a[n+i] = buf[i]
}
} else {
@@ -669,7 +669,7 @@ func BenchmarkTable(b *testing.B) {
for i := 0; i < b.N; i++ {
w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
// Write the line h times.
for j := 0; j < h; j++ {
for range h {
w.Write(line)
}
w.Flush()
@@ -681,7 +681,7 @@ func BenchmarkTable(b *testing.B) {
w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
for i := 0; i < b.N; i++ {
// Write the line h times.
for j := 0; j < h; j++ {
for range h {
w.Write(line)
}
w.Flush()
@@ -701,7 +701,7 @@ func BenchmarkPyramid(b *testing.B) {
for i := 0; i < b.N; i++ {
w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
// Write increasing prefixes of that line.
for j := 0; j < x; j++ {
for j := range x {
w.Write(line[:j*2])
w.Write([]byte{'\n'})
}
@@ -723,7 +723,7 @@ func BenchmarkRagged(b *testing.B) {
for i := 0; i < b.N; i++ {
w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
// Write the lines in turn h times.
for j := 0; j < h; j++ {
for j := range h {
w.Write(lines[j%len(lines)])
w.Write([]byte{'\n'})
}
+1 -1
View File
@@ -60,7 +60,7 @@ func TestResolveWithCache(t *testing.T) {
idResolver := New(apiClient, false)
ctx := context.Background()
for i := 0; i < 2; i++ {
for range 2 {
id, err := idResolver.Resolve(ctx, swarm.Node{}, "nodeID")
assert.NilError(t, err)
assert.Check(t, is.Equal("node-foo", id))
+4 -3
View File
@@ -190,7 +190,7 @@ func (f *fakeBuild) build(_ context.Context, buildContext io.Reader, options cli
func (f *fakeBuild) headers(t *testing.T) []*tar.Header {
t.Helper()
headers := []*tar.Header{}
var headers []*tar.Header
for {
hdr, err := f.context.Next()
switch err {
@@ -206,8 +206,9 @@ func (f *fakeBuild) headers(t *testing.T) []*tar.Header {
func (f *fakeBuild) filenames(t *testing.T) []string {
t.Helper()
names := []string{}
for _, header := range f.headers(t) {
h := f.headers(t)
names := make([]string, 0, len(h))
for _, header := range h {
names = append(names, header.Name)
}
sort.Strings(names)
+1 -4
View File
@@ -389,10 +389,7 @@ func generateLegend(out tui.Output, width uint) string {
}
legend += legendSb371.String()
r := int(width) - tui.Width(legend)
if r < 0 {
r = 0
}
r := max(int(width)-tui.Width(legend), 0)
legend = strings.Repeat(" ", r) + legend
return legend
}
+6 -4
View File
@@ -1,9 +1,13 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
package manifest
import (
"context"
"fmt"
"path/filepath"
"slices"
"github.com/containerd/errdefs"
"github.com/docker/cli/cli"
@@ -164,10 +168,8 @@ func runManifestAnnotate(dockerCLI command.Cli, opts annotateOptions) error {
}
func appendIfUnique(list []string, str string) []string {
for _, s := range list {
if s == str {
return list
}
if slices.Contains(list, str) {
return list
}
return append(list, str)
}
+5 -3
View File
@@ -1,9 +1,13 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
package node
import (
"context"
"errors"
"fmt"
"maps"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
@@ -99,9 +103,7 @@ func mergeNodeUpdate(flags *pflag.FlagSet) func(*swarm.Node) error {
}
if flags.Changed(flagLabelAdd) {
labels := flags.Lookup(flagLabelAdd).Value.(*opts.ListOpts).GetSlice()
for k, v := range opts.ConvertKVStringsToMap(labels) {
spec.Annotations.Labels[k] = v
}
maps.Copy(spec.Annotations.Labels, opts.ConvertKVStringsToMap(labels))
}
if flags.Changed(flagLabelRemove) {
keys := flags.Lookup(flagLabelRemove).Value.(*opts.ListOpts).GetSlice()
+2 -2
View File
@@ -144,8 +144,8 @@ func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword
}
}
argPassword = strings.TrimSpace(argPassword)
if argPassword == "" {
isEmpty := strings.TrimSpace(argPassword) == ""
if isEmpty {
restoreInput, err := prompt.DisableInputEcho(cli.In())
if err != nil {
return registrytypes.AuthConfig{}, err
+38 -5
View File
@@ -1,6 +1,7 @@
package registry
import (
"bytes"
"context"
"errors"
"fmt"
@@ -88,6 +89,38 @@ func verifyLoginFlags(flags *pflag.FlagSet, opts loginOptions) error {
return nil
}
// readSecretFromStdin reads the secret from r and returns it as a string.
// It trims terminal line-endings (LF, CRLF, or CR), which may be added when
// inputting interactively or piping input. The value is otherwise treated as
// opaque, preserving any other whitespace, including newlines, per [NIST SP 800-63B §5.1.1.2].
// Note that trimming whitespace may still happen elsewhere (see [NIST SP 800-63B (revision 4) §3.1.1.2]);
//
// > Verifiers **MAY** make limited allowances for mistyping (e.g., removing
// > leading and trailing whitespace characters before verification, allowing
// > the verification of passwords with differing cases for the leading character)
//
// [NIST SP 800-63B §5.1.1.2]: https://pages.nist.gov/800-63-3/sp800-63b.html#memsecretver
// [NIST SP 800-63B (revision 4) §3.1.1.2]: https://pages.nist.gov/800-63-4/sp800-63b.html#passwordver
func readSecretFromStdin(r io.Reader) (string, error) {
b, err := io.ReadAll(r)
if err != nil {
return "", err
}
if len(b) == 0 {
return "", nil
}
for _, eol := range [][]byte{[]byte("\r\n"), []byte("\n"), []byte("\r")} {
var ok bool
b, ok = bytes.CutSuffix(b, eol)
if ok {
break
}
}
return string(b), nil
}
func verifyLoginOptions(dockerCLI command.Streams, opts *loginOptions) error {
if opts.password != "" {
_, _ = fmt.Fprintln(dockerCLI.Err(), "WARNING! Using --password via the CLI is insecure. Use --password-stdin.")
@@ -97,14 +130,14 @@ func verifyLoginOptions(dockerCLI command.Streams, opts *loginOptions) error {
if opts.user == "" {
return errors.New("username is empty")
}
contents, err := io.ReadAll(dockerCLI.In())
p, err := readSecretFromStdin(dockerCLI.In())
if err != nil {
return err
}
opts.password = strings.TrimSuffix(string(contents), "\n")
opts.password = strings.TrimSuffix(opts.password, "\r")
if strings.TrimSpace(p) == "" {
return errors.New("password is empty")
}
opts.password = p
}
return nil
}
+128 -23
View File
@@ -1,15 +1,18 @@
package registry
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"path/filepath"
"testing"
"testing/iotest"
"time"
"github.com/creack/pty"
"github.com/docker/cli/cli/config/configfile"
configtypes "github.com/docker/cli/cli/config/types"
"github.com/docker/cli/cli/streams"
"github.com/docker/cli/internal/prompt"
@@ -19,7 +22,6 @@ import (
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/fs"
)
const (
@@ -71,8 +73,9 @@ func TestLoginWithCredStoreCreds(t *testing.T) {
},
}
ctx := context.Background()
tmpDir := t.TempDir()
cli := test.NewFakeCli(&fakeClient{})
cli.ConfigFile().Filename = filepath.Join(t.TempDir(), "config.json")
cli.SetConfigFile(configfile.New(filepath.Join(tmpDir, "config.json")))
for _, tc := range testCases {
_, err := loginWithStoredCredentials(ctx, cli, tc.inputAuthConfig)
if tc.expectedErrMsg != "" {
@@ -91,6 +94,7 @@ func TestRunLogin(t *testing.T) {
testCases := []struct {
doc string
priorCredentials map[string]configtypes.AuthConfig
stdIn string
input loginOptions
expectedCredentials map[string]configtypes.AuthConfig
expectedErr string
@@ -286,20 +290,125 @@ func TestRunLogin(t *testing.T) {
},
},
},
{
doc: "password stdin empty",
priorCredentials: map[string]configtypes.AuthConfig{},
input: loginOptions{
serverAddress: "reg1",
user: "my-username",
passwordStdin: true,
},
expectedErr: `password is empty`,
expectedCredentials: map[string]configtypes.AuthConfig{
"reg1": {
Username: "my-username",
ServerAddress: "reg1",
},
},
},
{
doc: "password stdin read error",
priorCredentials: map[string]configtypes.AuthConfig{},
input: loginOptions{
serverAddress: "reg1",
user: "my-username",
passwordStdin: true,
},
expectedErr: `TEST_READ_ERR`,
expectedCredentials: map[string]configtypes.AuthConfig{
"reg1": {
Username: "my-username",
ServerAddress: "reg1",
},
},
},
{
doc: "password stdin with line-endings",
priorCredentials: map[string]configtypes.AuthConfig{},
stdIn: "my password\r\n",
input: loginOptions{
serverAddress: "reg1",
user: "my-username",
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{},
input: loginOptions{
serverAddress: "reg1",
user: "my-username",
password: " my password with spaces ",
},
expectedCredentials: map[string]configtypes.AuthConfig{
"reg1": {
Username: "my-username",
Password: " my password with spaces ",
ServerAddress: "reg1",
},
},
},
{
doc: "password stdin with line-endings",
priorCredentials: map[string]configtypes.AuthConfig{},
stdIn: " my password with spaces \r\n",
input: loginOptions{
serverAddress: "reg1",
user: "my-username",
passwordStdin: true,
},
expectedCredentials: map[string]configtypes.AuthConfig{
"reg1": {
Username: "my-username",
Password: " my password with spaces ",
ServerAddress: "reg1",
},
},
},
{
doc: "password stdin with multiple line-endings",
priorCredentials: map[string]configtypes.AuthConfig{},
stdIn: " my password\nwith spaces \r\n\r\n",
input: loginOptions{
serverAddress: "reg1",
user: "my-username",
passwordStdin: true,
},
expectedCredentials: map[string]configtypes.AuthConfig{
"reg1": {
Username: "my-username",
Password: " my password\nwith spaces \r\n",
ServerAddress: "reg1",
},
},
},
}
for _, tc := range testCases {
t.Run(tc.doc, func(t *testing.T) {
tmpFile := fs.NewFile(t, "test-run-login")
defer tmpFile.Remove()
tmpDir := t.TempDir()
cfg := configfile.New(filepath.Join(tmpDir, "config.json"))
cli := test.NewFakeCli(&fakeClient{})
configfile := cli.ConfigFile()
configfile.Filename = tmpFile.Path()
cli.SetConfigFile(cfg)
if tc.input.passwordStdin {
if tc.expectedErr == "TEST_READ_ERR" {
cli.SetIn(streams.NewIn(io.NopCloser(iotest.ErrReader(errors.New(tc.expectedErr)))))
} else {
cli.SetIn(streams.NewIn(io.NopCloser(bytes.NewBufferString(tc.stdIn))))
}
}
for _, priorCred := range tc.priorCredentials {
assert.NilError(t, configfile.GetCredentialsStore(priorCred.ServerAddress).Store(priorCred))
assert.NilError(t, cfg.GetCredentialsStore(priorCred.ServerAddress).Store(priorCred))
}
storedCreds, err := configfile.GetAllCredentials()
storedCreds, err := cfg.GetAllCredentials()
assert.NilError(t, err)
assert.DeepEqual(t, storedCreds, tc.priorCredentials)
@@ -310,7 +419,7 @@ func TestRunLogin(t *testing.T) {
}
assert.NilError(t, loginErr)
outputCreds, err := configfile.GetAllCredentials()
outputCreds, err := cfg.GetAllCredentials()
assert.Check(t, err)
assert.DeepEqual(t, outputCreds, tc.expectedCredentials)
})
@@ -356,11 +465,10 @@ func TestLoginNonInteractive(t *testing.T) {
for _, registryAddr := range registries {
for _, tc := range testCases {
t.Run(tc.doc, func(t *testing.T) {
tmpFile := fs.NewFile(t, "test-run-login")
defer tmpFile.Remove()
tmpDir := t.TempDir()
cfg := configfile.New(filepath.Join(tmpDir, "config.json"))
cli := test.NewFakeCli(&fakeClient{})
cfg := cli.ConfigFile()
cfg.Filename = tmpFile.Path()
cli.SetConfigFile(cfg)
options := loginOptions{
serverAddress: registryAddr,
}
@@ -419,11 +527,10 @@ func TestLoginNonInteractive(t *testing.T) {
for _, registryAddr := range registries {
for _, tc := range testCases {
t.Run(tc.doc, func(t *testing.T) {
tmpFile := fs.NewFile(t, "test-run-login")
defer tmpFile.Remove()
tmpDir := t.TempDir()
cfg := configfile.New(filepath.Join(tmpDir, "config.json"))
cli := test.NewFakeCli(&fakeClient{})
cfg := cli.ConfigFile()
cfg.Filename = tmpFile.Path()
cli.SetConfigFile(cfg)
serverAddress := registryAddr
if serverAddress == "" {
serverAddress = "https://index.docker.io/v1/"
@@ -465,17 +572,15 @@ func TestLoginTermination(t *testing.T) {
_ = p.Close()
})
tmpDir := t.TempDir()
cfg := configfile.New(filepath.Join(tmpDir, "config.json"))
cli := test.NewFakeCli(&fakeClient{}, func(fc *test.FakeCli) {
fc.SetOut(streams.NewOut(tty))
fc.SetIn(streams.NewIn(tty))
})
tmpFile := fs.NewFile(t, "test-login-termination")
defer tmpFile.Remove()
cli.SetConfigFile(cfg)
configFile := cli.ConfigFile()
configFile.Filename = tmpFile.Path()
ctx, cancel := context.WithCancel(context.Background())
ctx, cancel := context.WithCancel(t.Context())
t.Cleanup(cancel)
runErr := make(chan error)
+3 -6
View File
@@ -108,7 +108,7 @@ func TestServiceListServiceStatus(t *testing.T) {
},
}
matrix := make([]testCase, 0)
matrix := make([]testCase, 0, len(tests))
for _, quiet := range []bool{false, true} {
for _, tc := range tests {
if quiet {
@@ -191,10 +191,7 @@ func generateServices(t *testing.T, opts clusterOpts) client.ServiceListResult {
t.Helper()
// Can't have more global tasks than nodes
globalTasks := opts.runningTasks
if globalTasks > opts.activeNodes {
globalTasks = opts.activeNodes
}
globalTasks := min(opts.runningTasks, opts.activeNodes)
return client.ServiceListResult{
Items: []swarm.Service{
*builders.Service(
@@ -279,7 +276,7 @@ func generateNodes(t *testing.T, activeNodes uint64) client.NodeListResult {
t.Helper()
nodes := client.NodeListResult{}
var i uint64
for i = 0; i < activeNodes; i++ {
for i = range activeNodes {
nodes.Items = append(nodes.Items, swarm.Node{
ID: fmt.Sprintf("node-ready-%d", i),
Status: swarm.NodeStatus{State: swarm.NodeStateReady},
+4 -4
View File
@@ -1,3 +1,6 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
package progress
import (
@@ -662,10 +665,7 @@ func (u *replicatedJobProgressUpdater) writeOverallProgress(active, completed in
})
// actualDesired is the lesser of MaxConcurrent, or the remaining tasks
actualDesired := u.total - completed
if actualDesired > u.concurrent {
actualDesired = u.concurrent
}
actualDesired := min(u.total-completed, u.concurrent)
_ = u.progressOut.WriteProgress(progress.Progress{
ID: "active tasks",
+12 -13
View File
@@ -80,7 +80,7 @@ func TestReplicatedProgressUpdaterOneReplica(t *testing.T) {
service: service,
}
tasks := []swarm.Task{}
var tasks []swarm.Task //nolint:prealloc // no need to over-complicate things.
ut.testUpdater(tasks, false,
[]progress.Progress{
@@ -193,7 +193,7 @@ func TestReplicatedProgressUpdaterManyReplicas(t *testing.T) {
service: service,
}
tasks := []swarm.Task{}
var tasks []swarm.Task
// No per-task progress bars because there are too many replicas
ut.testUpdater(tasks, false,
@@ -248,7 +248,7 @@ func TestGlobalProgressUpdaterOneNode(t *testing.T) {
service: service,
}
tasks := []swarm.Task{}
var tasks []swarm.Task //nolint:prealloc // no need to over-complicate things.
ut.testUpdater(tasks, false,
[]progress.Progress{
@@ -362,7 +362,7 @@ func TestGlobalProgressUpdaterManyNodes(t *testing.T) {
ut.activeNodes[strconv.Itoa(i)] = struct{}{}
}
tasks := []swarm.Task{}
var tasks []swarm.Task
ut.testUpdater(tasks, false,
[]progress.Progress{
@@ -459,9 +459,8 @@ func TestReplicatedJobProgressUpdaterSmall(t *testing.T) {
})
// wipe the old tasks out of the list
tasks = []swarm.Task{}
tasks = append(tasks,
swarm.Task{
tasks = []swarm.Task{ //nolint:prealloc // no need to over-complicate things.
{
ID: "task1",
Slot: 0,
NodeID: "",
@@ -469,7 +468,7 @@ func TestReplicatedJobProgressUpdaterSmall(t *testing.T) {
Status: swarm.TaskStatus{State: swarm.TaskStateNew},
JobIteration: &swarm.Version{Index: service.JobStatus.JobIteration.Index},
},
swarm.Task{
{
ID: "task2",
Slot: 1,
NodeID: "",
@@ -477,7 +476,7 @@ func TestReplicatedJobProgressUpdaterSmall(t *testing.T) {
Status: swarm.TaskStatus{State: swarm.TaskStateNew},
JobIteration: &swarm.Version{Index: service.JobStatus.JobIteration.Index},
},
)
}
ut.testUpdater(tasks, false, []progress.Progress{
{ID: "1/5", Action: "new ", Current: 1, Total: 10, HideCounts: true},
{ID: "2/5", Action: "new ", Current: 1, Total: 10, HideCounts: true},
@@ -644,7 +643,7 @@ func TestReplicatedJobProgressUpdaterLarge(t *testing.T) {
service: service,
}
tasks := []swarm.Task{}
var tasks []swarm.Task
// see the comments in TestReplicatedJobProgressUpdaterSmall for why
// we write this out twice.
@@ -741,7 +740,7 @@ func TestGlobalJobProgressUpdaterSmall(t *testing.T) {
service: service,
}
tasks := []swarm.Task{
tasks := []swarm.Task{ //nolint:prealloc // no need to over-complicate things.
{
ID: "oldtask1",
DesiredState: swarm.TaskStateComplete,
@@ -856,7 +855,7 @@ func TestGlobalJobProgressUpdaterLarge(t *testing.T) {
}
activeNodes := map[string]struct{}{}
for i := 0; i < 50; i++ {
for i := range 50 {
activeNodes[fmt.Sprintf("node%v", i)] = struct{}{}
}
@@ -871,7 +870,7 @@ func TestGlobalJobProgressUpdaterLarge(t *testing.T) {
service: service,
}
tasks := []swarm.Task{}
var tasks []swarm.Task //nolint:prealloc // no need to over-complicate things.
for nodeID := range activeNodes {
tasks = append(tasks, swarm.Task{
ID: "task" + nodeID,
+6 -16
View File
@@ -7,6 +7,7 @@ import (
"context"
"errors"
"fmt"
"maps"
"net/netip"
"slices"
"sort"
@@ -551,12 +552,7 @@ func updateStringToSlice(flags *pflag.FlagSet, flag string, field *[]string) {
}
func anyChanged(flags *pflag.FlagSet, fields ...string) bool {
for _, flag := range fields {
if flags.Changed(flag) {
return true
}
}
return false
return slices.ContainsFunc(fields, flags.Changed)
}
func addGenericResources(flags *pflag.FlagSet, spec *swarm.TaskSpec) error {
@@ -685,9 +681,7 @@ func updateContainerLabels(flags *pflag.FlagSet, field *map[string]string) {
}
values := flags.Lookup(flagContainerLabelAdd).Value.(*opts.ListOpts).GetSlice()
for key, value := range opts.ConvertKVStringsToMap(values) {
(*field)[key] = value
}
maps.Copy((*field), opts.ConvertKVStringsToMap(values))
}
}
@@ -704,9 +698,7 @@ func updateLabels(flags *pflag.FlagSet, field *map[string]string) {
}
values := flags.Lookup(flagLabelAdd).Value.(*opts.ListOpts).GetSlice()
for key, value := range opts.ConvertKVStringsToMap(values) {
(*field)[key] = value
}
maps.Copy((*field), opts.ConvertKVStringsToMap(values))
}
}
@@ -723,9 +715,7 @@ func updateSysCtls(flags *pflag.FlagSet, field *map[string]string) {
}
values := flags.Lookup(flagSysCtlAdd).Value.(*opts.ListOpts).GetSlice()
for key, value := range opts.ConvertKVStringsToMap(values) {
(*field)[key] = value
}
maps.Copy((*field), opts.ConvertKVStringsToMap(values))
}
}
@@ -1333,7 +1323,7 @@ func updateNetworks(ctx context.Context, apiClient client.NetworkAPIClient, flag
}
existingNetworks := make(map[string]struct{})
var newNetworks []swarm.NetworkAttachmentConfig //nolint:prealloc
var newNetworks []swarm.NetworkAttachmentConfig
for _, nw := range specNetworks {
if _, exists := idsToRemove[nw.Target]; exists {
continue
+2 -3
View File
@@ -27,7 +27,6 @@ func loadComposeFile(streams command.Streams, opts deployOptions) (*composetypes
return nil, err
}
dicts := getDictsFrom(configDetails.ConfigFiles)
config, err := loader.Load(configDetails)
if err != nil {
var fpe *loader.ForbiddenPropertiesError
@@ -39,6 +38,7 @@ func loadComposeFile(streams command.Streams, opts deployOptions) (*composetypes
return nil, err
}
dicts := getDictsFrom(configDetails.ConfigFiles)
unsupportedProperties := loader.GetUnsupportedProperties(dicts...)
if len(unsupportedProperties) > 0 {
_, _ = fmt.Fprintf(streams.Err(), "Ignoring unsupported options: %s\n\n",
@@ -65,8 +65,7 @@ func loadComposeFile(streams command.Streams, opts deployOptions) (*composetypes
}
func getDictsFrom(configFiles []composetypes.ConfigFile) []map[string]any {
dicts := []map[string]any{}
dicts := make([]map[string]any, 0, len(configFiles))
for _, configFile := range configFiles {
dicts = append(dicts, configFile.Config)
}
+1 -1
View File
@@ -110,7 +110,7 @@ func (*ExternalCAOption) Type() string {
// String returns a string repr of this option.
func (m *ExternalCAOption) String() string {
externalCAs := []string{}
externalCAs := make([]string, 0, len(m.values))
for _, externalCA := range m.values {
repr := fmt.Sprintf("%s: %s", externalCA.Protocol, externalCA.URL)
externalCAs = append(externalCAs, repr)
+2 -2
View File
@@ -133,12 +133,12 @@ func prettyPrintEvent(out io.Writer, event events.Message) error {
_, _ = fmt.Fprintf(out, "%s %s %s", event.Type, event.Action, event.Actor.ID)
if len(event.Actor.Attributes) > 0 {
var attrs []string
var keys []string
keys := make([]string, 0, len(event.Actor.Attributes))
for k := range event.Actor.Attributes {
keys = append(keys, k)
}
sort.Strings(keys)
attrs := make([]string, 0, len(keys))
for _, k := range keys {
v := event.Actor.Attributes[k]
attrs = append(attrs, k+"="+v)
+3 -3
View File
@@ -155,11 +155,11 @@ func (e statusError) Error() string {
// Note: The root command's name is excluded. If cmd is the root cmd, return ""
func getCommandName(cmd *cobra.Command) string {
fullCmdName := getFullCommandName(cmd)
i := strings.Index(fullCmdName, " ")
if i == -1 {
_, after, ok := strings.Cut(fullCmdName, " ")
if !ok {
return ""
}
return fullCmdName[i+1:]
return after
}
// getFullCommandName gets the full cobra command name in the format
+5 -2
View File
@@ -1,3 +1,6 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
package volume
import (
@@ -166,7 +169,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, options createOptions
// each topology takes the form segment=value,segment=value
// comma-separated list of equal separated maps
segments := map[string]string{}
for _, segment := range strings.Split(top, ",") {
for segment := range strings.SplitSeq(top, ",") {
// TODO(dperny): validate topology syntax
k, v, _ := strings.Cut(segment, "=")
segments[k] = v
@@ -181,7 +184,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, options createOptions
// each topology takes the form segment=value,segment=value
// comma-separated list of equal separated maps
segments := map[string]string{}
for _, segment := range strings.Split(top, ",") {
for segment := range strings.SplitSeq(top, ",") {
// TODO(dperny): validate topology syntax
k, v, _ := strings.Cut(segment, "=")
segments[k] = v
+29 -29
View File
@@ -1,11 +1,16 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
package convert
import (
"cmp"
"context"
"errors"
"fmt"
"net/netip"
"os"
"slices"
"sort"
"strings"
"time"
@@ -174,7 +179,11 @@ func Service(
}
func getPlacementPreference(preferences []composetypes.PlacementPreferences) []swarm.PlacementPreference {
result := []swarm.PlacementPreference{}
if len(preferences) == 0 {
return nil
}
result := make([]swarm.PlacementPreference, 0, len(preferences))
for _, preference := range preferences {
spreadDescriptor := preference.Spread
result = append(result, swarm.PlacementPreference{
@@ -198,7 +207,7 @@ func convertServiceNetworks(
}
}
nets := []swarm.NetworkAttachmentConfig{}
nets := make([]swarm.NetworkAttachmentConfig, 0, len(networks))
for networkName, nw := range networks {
networkConfig, ok := networkConfigs[networkName]
if !ok && networkName != defaultNetwork {
@@ -241,8 +250,6 @@ func convertServiceSecrets(
secrets []composetypes.ServiceSecretConfig,
secretSpecs map[string]composetypes.SecretConfig,
) ([]*swarm.SecretReference, error) {
refs := []*swarm.SecretReference{}
lookup := func(key string) (composetypes.FileObjectConfig, error) {
secretSpec, exists := secretSpecs[key]
if !exists {
@@ -250,6 +257,8 @@ func convertServiceSecrets(
}
return composetypes.FileObjectConfig(secretSpec), nil
}
refs := make([]*swarm.SecretReference, 0, len(secrets))
for _, secret := range secrets {
obj, err := convertFileObject(namespace, composetypes.FileReferenceConfig(secret), lookup)
if err != nil {
@@ -564,20 +573,17 @@ func convertResources(source composetypes.Resources) (*swarm.ResourceRequirement
}
func convertEndpointSpec(endpointMode string, source []composetypes.ServicePortConfig) *swarm.EndpointSpec {
portConfigs := []swarm.PortConfig{}
portConfigs := make([]swarm.PortConfig, 0, len(source))
for _, port := range source {
portConfig := swarm.PortConfig{
portConfigs = append(portConfigs, swarm.PortConfig{
Protocol: network.IPProtocol(port.Protocol),
TargetPort: port.Target,
PublishedPort: port.Published,
PublishMode: swarm.PortConfigPublishMode(port.Mode),
}
portConfigs = append(portConfigs, portConfig)
})
}
sort.Slice(portConfigs, func(i, j int) bool {
return portConfigs[i].PublishedPort < portConfigs[j].PublishedPort
})
slices.SortFunc(portConfigs, swarm.PortConfig.Compare)
return &swarm.EndpointSpec{
Mode: swarm.ResolutionMode(strings.ToLower(endpointMode)),
@@ -698,28 +704,22 @@ func convertCredentialSpec(namespace Namespace, spec composetypes.CredentialSpec
}
func convertUlimits(origUlimits map[string]*composetypes.UlimitsConfig) []*container.Ulimit {
newUlimits := make(map[string]*container.Ulimit)
ulimits := make([]*container.Ulimit, 0, len(origUlimits))
for name, u := range origUlimits {
soft, hard := int64(u.Soft), int64(u.Hard)
if u.Single != 0 {
newUlimits[name] = &container.Ulimit{
Name: name,
Soft: int64(u.Single),
Hard: int64(u.Single),
}
} else {
newUlimits[name] = &container.Ulimit{
Name: name,
Soft: int64(u.Soft),
Hard: int64(u.Hard),
}
soft, hard = int64(u.Single), int64(u.Single)
}
ulimits = append(ulimits, &container.Ulimit{
Name: name,
Soft: soft,
Hard: hard,
})
}
ulimits := make([]*container.Ulimit, 0, len(newUlimits))
for _, ulimit := range newUlimits {
ulimits = append(ulimits, ulimit)
}
sort.SliceStable(ulimits, func(i, j int) bool {
return ulimits[i].Name < ulimits[j].Name
slices.SortFunc(ulimits, func(a, b *container.Ulimit) int {
return cmp.Compare(a.Name, b.Name)
})
return ulimits
}
+2 -3
View File
@@ -6,6 +6,7 @@ package loader
import (
"errors"
"fmt"
"maps"
"path"
"path/filepath"
"reflect"
@@ -249,9 +250,7 @@ func GetDeprecatedProperties(configDicts ...map[string]any) map[string]string {
for _, configDict := range configDicts {
deprecatedProperties := getProperties(getServices(configDict), types.DeprecatedProperties)
for key, value := range deprecatedProperties {
deprecated[key] = value
}
maps.Copy(deprecated, deprecatedProperties)
}
return deprecated
+52 -68
View File
@@ -4,8 +4,11 @@
package loader
import (
"cmp"
"errors"
"fmt"
"reflect"
"slices"
"sort"
"dario.cat/mergo"
@@ -26,36 +29,36 @@ func (s *specials) Transformer(t reflect.Type) func(dst, src reflect.Value) erro
func merge(configs []*types.Config) (*types.Config, error) {
base := configs[0]
for _, override := range configs[1:] {
var err error
base.Services, err = mergeServices(base.Services, override.Services)
if err != nil {
return base, fmt.Errorf("cannot merge services from %s: %w", override.Filename, err)
var errs []error
if services, err := mergeServices(base.Services, override.Services); err != nil {
errs = append(errs, fmt.Errorf("cannot merge services: %w", err))
} else {
base.Services = services
}
base.Volumes, err = mergeVolumes(base.Volumes, override.Volumes)
if err != nil {
return base, fmt.Errorf("cannot merge volumes from %s: %w", override.Filename, err)
if err := mergo.Map(&base.Volumes, &override.Volumes, mergo.WithOverride); err != nil {
errs = append(errs, fmt.Errorf("cannot merge volumes: %w", err))
}
base.Networks, err = mergeNetworks(base.Networks, override.Networks)
if err != nil {
return base, fmt.Errorf("cannot merge networks from %s: %w", override.Filename, err)
if err := mergo.Map(&base.Networks, &override.Networks, mergo.WithOverride); err != nil {
errs = append(errs, fmt.Errorf("cannot merge networks: %w", err))
}
base.Secrets, err = mergeSecrets(base.Secrets, override.Secrets)
if err != nil {
return base, fmt.Errorf("cannot merge secrets from %s: %w", override.Filename, err)
if err := mergo.Map(&base.Secrets, &override.Secrets, mergo.WithOverride); err != nil {
errs = append(errs, fmt.Errorf("cannot merge secrets: %w", err))
}
base.Configs, err = mergeConfigs(base.Configs, override.Configs)
if err != nil {
return base, fmt.Errorf("cannot merge configs from %s: %w", override.Filename, err)
if err := mergo.Map(&base.Configs, &override.Configs, mergo.WithOverride); err != nil {
errs = append(errs, fmt.Errorf("cannot merge configs: %w", err))
}
if err := errors.Join(errs...); err != nil {
return nil, errors.Join(fmt.Errorf("failed to merge file %s", override.Filename), err)
}
}
return base, nil
}
func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig, error) {
baseServices := mapByName(base)
overrideServices := mapByName(override)
specials := &specials{
m: map[reflect.Type]func(dst, src reflect.Value) error{
mergeOpts := []func(*mergo.Config){
mergo.WithAppendSlice,
mergo.WithOverride,
mergo.WithTransformers(&specials{m: map[reflect.Type]func(dst, src reflect.Value) error{
reflect.PointerTo(reflect.TypeFor[types.LoggingConfig]()): safelyMerge(mergeLoggingConfig),
reflect.TypeFor[[]types.ServicePortConfig](): mergeSlice(toServicePortConfigsMap, toServicePortConfigsSlice),
reflect.TypeFor[[]types.ServiceSecretConfig](): mergeSlice(toServiceSecretConfigsMap, toServiceSecretConfigsSlice),
@@ -65,23 +68,34 @@ func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig,
reflect.TypeFor[types.ShellCommand](): mergeShellCommand,
reflect.PointerTo(reflect.TypeFor[types.ServiceNetworkConfig]()): mergeServiceNetworkConfig,
reflect.PointerTo(reflect.TypeFor[uint64]()): mergeUint64,
},
}}),
}
for name, overrideService := range overrideServices {
if baseService, ok := baseServices[name]; ok {
if err := mergo.Merge(&baseService, &overrideService, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(specials)); err != nil {
return base, fmt.Errorf("cannot merge service %s: %w", name, err)
baseServices := make(map[string]types.ServiceConfig, len(base))
for _, s := range base {
baseServices[s.Name] = s
}
for _, overrideService := range override {
if baseService, ok := baseServices[overrideService.Name]; ok {
if err := mergo.Merge(&baseService, &overrideService, mergeOpts...); err != nil {
return nil, fmt.Errorf("cannot merge service %s: %w", overrideService.Name, err)
}
baseServices[name] = baseService
baseServices[overrideService.Name] = baseService
continue
}
baseServices[name] = overrideService
baseServices[overrideService.Name] = overrideService
}
services := make([]types.ServiceConfig, 0, len(baseServices))
for _, baseService := range baseServices {
services = append(services, baseService)
}
sort.Slice(services, func(i, j int) bool { return services[i].Name < services[j].Name })
slices.SortFunc(services, func(a, b types.ServiceConfig) int {
return cmp.Compare(a.Name, b.Name)
})
return services, nil
}
@@ -134,7 +148,7 @@ func toServiceVolumeConfigsMap(s any) (map[any]any, error) {
}
func toServiceSecretConfigsSlice(dst reflect.Value, m map[any]any) error {
s := []types.ServiceSecretConfig{}
s := make([]types.ServiceSecretConfig, 0, len(m))
for _, v := range m {
s = append(s, v.(types.ServiceSecretConfig))
}
@@ -144,7 +158,7 @@ func toServiceSecretConfigsSlice(dst reflect.Value, m map[any]any) error {
}
func toSServiceConfigObjConfigsSlice(dst reflect.Value, m map[any]any) error {
s := []types.ServiceConfigObjConfig{}
s := make([]types.ServiceConfigObjConfig, 0, len(m))
for _, v := range m {
s = append(s, v.(types.ServiceConfigObjConfig))
}
@@ -154,7 +168,7 @@ func toSServiceConfigObjConfigsSlice(dst reflect.Value, m map[any]any) error {
}
func toServicePortConfigsSlice(dst reflect.Value, m map[any]any) error {
s := []types.ServicePortConfig{}
s := make([]types.ServicePortConfig, 0, len(m))
for _, v := range m {
s = append(s, v.(types.ServicePortConfig))
}
@@ -164,7 +178,7 @@ func toServicePortConfigsSlice(dst reflect.Value, m map[any]any) error {
}
func toServiceVolumeConfigsSlice(dst reflect.Value, m map[any]any) error {
s := []types.ServiceVolumeConfig{}
s := make([]types.ServiceVolumeConfig, 0, len(m))
for _, v := range m {
s = append(s, v.(types.ServiceVolumeConfig))
}
@@ -217,11 +231,13 @@ func sliceToMap(tomap tomapFn, v reflect.Value) (map[any]any, error) {
}
func mergeLoggingConfig(dst, src reflect.Value) error {
dstDriver := dst.Elem().FieldByName("Driver").String()
srcDriver := src.Elem().FieldByName("Driver").String()
// Same driver, merging options
if getLoggingDriver(dst.Elem()) == getLoggingDriver(src.Elem()) ||
getLoggingDriver(dst.Elem()) == "" || getLoggingDriver(src.Elem()) == "" {
if getLoggingDriver(dst.Elem()) == "" {
dst.Elem().FieldByName("Driver").SetString(getLoggingDriver(src.Elem()))
if dstDriver == srcDriver || dstDriver == "" || srcDriver == "" {
if dstDriver == "" {
dst.Elem().FieldByName("Driver").SetString(srcDriver)
}
dstOptions := dst.Elem().FieldByName("Options").Interface().(map[string]string)
srcOptions := src.Elem().FieldByName("Options").Interface().(map[string]string)
@@ -269,35 +285,3 @@ func mergeUint64(dst, src reflect.Value) error {
}
return nil
}
func getLoggingDriver(v reflect.Value) string {
return v.FieldByName("Driver").String()
}
func mapByName(services []types.ServiceConfig) map[string]types.ServiceConfig {
m := map[string]types.ServiceConfig{}
for _, service := range services {
m[service.Name] = service
}
return m
}
func mergeVolumes(base, override map[string]types.VolumeConfig) (map[string]types.VolumeConfig, error) {
err := mergo.Map(&base, &override, mergo.WithOverride)
return base, err
}
func mergeNetworks(base, override map[string]types.NetworkConfig) (map[string]types.NetworkConfig, error) {
err := mergo.Map(&base, &override, mergo.WithOverride)
return base, err
}
func mergeSecrets(base, override map[string]types.SecretConfig) (map[string]types.SecretConfig, error) {
err := mergo.Map(&base, &override, mergo.WithOverride)
return base, err
}
func mergeConfigs(base, override map[string]types.ConfigObjConfig) (map[string]types.ConfigObjConfig, error) {
err := mergo.Map(&base, &override, mergo.WithOverride)
return base, err
}
+2 -2
View File
@@ -1153,7 +1153,7 @@ func TestLoadMultipleServiceVolumes(t *testing.T) {
func TestMergeUlimitsConfig(t *testing.T) {
specials := &specials{
m: map[reflect.Type]func(dst, src reflect.Value) error{
reflect.TypeOf(&types.UlimitsConfig{}): mergeUlimitsConfig,
reflect.TypeFor[*types.UlimitsConfig](): mergeUlimitsConfig,
},
}
base := map[string]*types.UlimitsConfig{
@@ -1189,7 +1189,7 @@ func TestMergeUlimitsConfig(t *testing.T) {
func TestMergeServiceNetworkConfig(t *testing.T) {
specials := &specials{
m: map[reflect.Type]func(dst, src reflect.Value) error{
reflect.TypeOf(&types.ServiceNetworkConfig{}): mergeServiceNetworkConfig,
reflect.TypeFor[*types.ServiceNetworkConfig](): mergeServiceNetworkConfig,
},
}
base := map[string]*types.ServiceNetworkConfig{
+2 -3
View File
@@ -5,6 +5,7 @@ package template
import (
"fmt"
"maps"
"regexp"
"strings"
@@ -140,9 +141,7 @@ func recurseExtract(value any, pattern regexper) map[string]string {
case map[string]any:
for _, elem := range val {
submap := recurseExtract(elem, pattern)
for k, v := range submap {
m[k] = v
}
maps.Copy(m, submap)
}
case []any:
+4 -5
View File
@@ -6,6 +6,7 @@ package types
import (
"encoding/json"
"fmt"
"maps"
"strconv"
"time"
@@ -129,9 +130,7 @@ func (c Config) MarshalJSON() ([]byte, error) {
if len(c.Configs) > 0 {
m["configs"] = c.Configs
}
for k, v := range c.Extras {
m[k] = v
}
maps.Copy(m, c.Extras)
return json.Marshal(m)
}
@@ -352,7 +351,7 @@ func (u UnitBytes) MarshalYAML() (any, error) {
// MarshalJSON makes UnitBytes implement json.Marshaler
func (u UnitBytes) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`"%d"`, u)), nil
return fmt.Appendf(nil, `"%d"`, u), nil
}
// RestartPolicy the service restart policy
@@ -562,7 +561,7 @@ func (e External) MarshalJSON() ([]byte, error) {
if e.Name == "" {
return []byte(strconv.FormatBool(e.External)), nil
}
return []byte(fmt.Sprintf(`{"name": %q}`, e.Name)), nil
return fmt.Appendf(nil, `{"name": %q}`, e.Name), nil
}
// CredentialSpecConfig for credential spec on Windows
+5 -3
View File
@@ -1,3 +1,6 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
package configfile
import (
@@ -6,6 +9,7 @@ import (
"errors"
"fmt"
"io"
"maps"
"os"
"path/filepath"
"strings"
@@ -374,9 +378,7 @@ func getConfiguredCredentialStore(c *ConfigFile, registryHostname string) string
func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig, error) {
auths := make(map[string]types.AuthConfig)
addAll := func(from map[string]types.AuthConfig) {
for reg, ac := range from {
auths[reg] = ac
}
maps.Copy(auths, from)
}
defaultStore := configFile.GetCredentialsStore("")
+2 -2
View File
@@ -73,10 +73,10 @@ func (m *mockCommand) Output() ([]byte, error) {
return []byte("program failed"), errCommandExited
}
case "list":
return []byte(fmt.Sprintf(`{"%s": "%s", "%s": "%s"}`, validServerAddress, "foo", validServerAddress2, "<token>")), nil
return fmt.Appendf(nil, `{"%s": "%s", "%s": "%s"}`, validServerAddress, "foo", validServerAddress2, "<token>"), nil
}
return []byte(fmt.Sprintf("unknown argument %q with %q", m.arg, inS)), errCommandExited
return fmt.Appendf(nil, "unknown argument %q with %q", m.arg, inS), errCommandExited
}
// Input sets the input to send to a remote credentials helper.
+6 -4
View File
@@ -1,3 +1,6 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
// Package connhelper provides helpers for connecting to a remote daemon host with custom logic.
package connhelper
@@ -6,6 +9,7 @@ import (
"fmt"
"net"
"net/url"
"slices"
"strings"
"github.com/docker/cli/cli/connhelper/commandconn"
@@ -89,10 +93,8 @@ func addSSHTimeout(sshFlags []string) []string {
// disablePseudoTerminalAllocation disables pseudo-terminal allocation to
// prevent SSH from executing as a login shell
func disablePseudoTerminalAllocation(sshFlags []string) []string {
for _, flag := range sshFlags {
if flag == "-T" {
return sshFlags
}
if slices.Contains(sshFlags, "-T") {
return sshFlags
}
return append(sshFlags, "-T")
}
+1 -1
View File
@@ -175,7 +175,7 @@ func quoteCommand(commandAndArgs ...string) (string, error) {
quotedCmd = a
continue
}
quotedCmd += " " + a //nolint:perfsprint // ignore "concat-loop"; no need to use a string-builder for this.
quotedCmd += " " + a
}
// each part is quoted appropriately, so now we'll have a full
// shell command to pass off to "ssh"
+7 -7
View File
@@ -17,7 +17,7 @@ require (
github.com/spf13/cobra v1.10.1
github.com/spf13/pflag v1.0.10
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a
go.opentelemetry.io/otel v1.38.0
go.opentelemetry.io/otel v1.40.0
gotest.tools/v3 v3.5.2
)
@@ -57,19 +57,19 @@ require (
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.40.0 // indirect
go.opentelemetry.io/otel/sdk v1.40.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect
go.opentelemetry.io/otel/trace v1.40.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/sys v0.40.0 // indirect
golang.org/x/term v0.37.0 // indirect
golang.org/x/text v0.31.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
+16 -16
View File
@@ -213,8 +213,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
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/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@@ -252,26 +252,26 @@ github.com/zmap/zcrypto v0.0.0-20210511125630-18f1e0152cfc h1:zkGwegkOW709y0oiAr
github.com/zmap/zcrypto v0.0.0-20210511125630-18f1e0152cfc/go.mod h1:FM4U1E3NzlNMRnSUTU3P1UdukWhYGifqEsjk9fn7BCk=
github.com/zmap/zlint/v3 v3.1.0 h1:WjVytZo79m/L1+/Mlphl09WBob6YTGljN5IGWZFpAv0=
github.com/zmap/zlint/v3 v3.1.0/go.mod h1:L7t8s3sEKkb0A2BxGy1IWrxt1ZATa1R4QfJZaQOD3zU=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/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.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@@ -302,8 +302,8 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
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.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
+4 -8
View File
@@ -22,8 +22,7 @@ import (
var pluginFilename = "docker-buildx"
func TestBuildWithBuilder(t *testing.T) {
ctx, cancel := context.WithCancel(context.TODO())
defer cancel()
ctx := t.Context()
testcases := []struct {
name string
@@ -131,8 +130,7 @@ func (*fakeClient) Ping(context.Context, client.PingOptions) (client.PingResult,
}
func TestBuildkitDisabled(t *testing.T) {
ctx, cancel := context.WithCancel(context.TODO())
defer cancel()
ctx := t.Context()
t.Setenv("DOCKER_BUILDKIT", "0")
@@ -172,8 +170,7 @@ func TestBuildkitDisabled(t *testing.T) {
}
func TestBuilderBroken(t *testing.T) {
ctx, cancel := context.WithCancel(context.TODO())
defer cancel()
ctx := t.Context()
dir := fs.NewDir(t, t.Name(),
fs.WithFile(pluginFilename, `#!/bin/sh exit 1`, fs.WithMode(0o777)),
@@ -212,8 +209,7 @@ func TestBuilderBroken(t *testing.T) {
func TestBuilderBrokenEnforced(t *testing.T) {
t.Setenv("DOCKER_BUILDKIT", "1")
ctx, cancel := context.WithCancel(context.TODO())
defer cancel()
ctx := t.Context()
dir := fs.NewDir(t, t.Name(),
fs.WithFile(pluginFilename, `#!/bin/sh exit 1`, fs.WithMode(0o777)),
+26 -13
View File
@@ -1,3 +1,6 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
package main
import (
@@ -7,6 +10,7 @@ import (
"os"
"os/exec"
"os/signal"
"slices"
"strings"
"syscall"
@@ -115,6 +119,20 @@ func getExitCode(err error) int {
return 1
}
// cmdErrorMessage extracts an error message suitable for passing to plugin
// hooks. If the error's message is empty (e.g. a StatusError with only an
// exit code), it falls back to a generic message so that hooks can still
// detect that the command failed.
func cmdErrorMessage(err error) string {
if err == nil {
return ""
}
if msg := err.Error(); msg != "" {
return msg
}
return fmt.Sprintf("exited with code %d", getExitCode(err))
}
func newDockerCommand(dockerCli *command.DockerCli) *cli.TopLevelCommand {
var (
opts *cliflags.ClientOptions
@@ -410,7 +428,7 @@ func forceExitAfter3TerminationSignals(ctx context.Context, streams command.Stre
signal.Notify(sig, platformsignals.TerminationSignals...)
// once we have received a total of 3 signals we force exit the cli
for i := 0; i < 2; i++ {
for range 2 {
<-sig
}
_, _ = fmt.Fprint(streams.Err(), "\ngot 3 SIGTERM/SIGINTs, forcefully exiting\n")
@@ -476,10 +494,11 @@ func runDocker(ctx context.Context, dockerCli *command.DockerCli) error {
subCommand = ccmd
if err != nil || pluginmanager.IsPluginCommand(ccmd) {
err := tryPluginRun(ctx, dockerCli, cmd, args[0], envs)
if ccmd != nil && dockerCli.Out().IsTerminal() && dockerCli.HooksEnabled() && !errdefs.IsNotFound(err) {
errMessage := cmdErrorMessage(err)
pluginmanager.RunPluginHooks(ctx, dockerCli, cmd, ccmd, args, errMessage)
}
if err == nil {
if ccmd != nil && dockerCli.Out().IsTerminal() && dockerCli.HooksEnabled() {
pluginmanager.RunPluginHooks(ctx, dockerCli, cmd, ccmd, args)
}
return nil
}
if !errdefs.IsNotFound(err) {
@@ -503,11 +522,7 @@ func runDocker(ctx context.Context, dockerCli *command.DockerCli) error {
// If the command is being executed in an interactive terminal
// and hook are enabled, run the plugin hooks.
if subCommand != nil && dockerCli.Out().IsTerminal() && dockerCli.HooksEnabled() {
var errMessage string
if err != nil {
errMessage = err.Error()
}
pluginmanager.RunCLICommandHooks(ctx, dockerCli, cmd, subCommand, errMessage)
pluginmanager.RunCLICommandHooks(ctx, dockerCli, cmd, subCommand, cmdErrorMessage(err))
}
return err
@@ -617,10 +632,8 @@ func findCommand(cmd *cobra.Command, cmds []string) bool {
if cmd == nil {
return false
}
for _, c := range cmds {
if c == cmd.Name() {
return true
}
if slices.Contains(cmds, cmd.Name()) {
return true
}
return findCommand(cmd.Parent(), cmds)
}
+63 -2
View File
@@ -4,12 +4,14 @@ import (
"bytes"
"context"
"errors"
"fmt"
"io"
"os"
"syscall"
"testing"
"time"
dockercli "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/commands"
"github.com/docker/cli/cli/debug"
@@ -38,8 +40,7 @@ func TestDisableFlagsInUseLineIsSet(t *testing.T) {
func TestClientDebugEnabled(t *testing.T) {
defer debug.Disable()
ctx, cancel := context.WithCancel(context.TODO())
defer cancel()
ctx := t.Context()
cli, err := command.NewDockerCli(command.WithBaseContext(ctx))
assert.NilError(t, err)
@@ -127,6 +128,66 @@ func TestUserTerminatedError(t *testing.T) {
assert.Equal(t, getExitCode(context.Cause(notifyCtx)), 143)
}
func TestGetExitCode(t *testing.T) {
t.Run("nil error returns 0", func(t *testing.T) {
assert.Equal(t, getExitCode(nil), 0)
})
t.Run("generic error returns 1", func(t *testing.T) {
assert.Equal(t, getExitCode(errors.New("some failure")), 1)
})
t.Run("StatusError with code", func(t *testing.T) {
err := dockercli.StatusError{StatusCode: 42}
assert.Equal(t, getExitCode(err), 42)
})
t.Run("StatusError with zero code falls back to 1", func(t *testing.T) {
err := dockercli.StatusError{StatusCode: 0, Status: "something went wrong"}
assert.Equal(t, getExitCode(err), 1)
})
t.Run("wrapped StatusError", func(t *testing.T) {
err := fmt.Errorf("wrapper: %w", dockercli.StatusError{StatusCode: 99})
assert.Equal(t, getExitCode(err), 99)
})
t.Run("SIGINT returns 130", func(t *testing.T) {
err := errCtxSignalTerminated{signal: syscall.SIGINT}
assert.Equal(t, getExitCode(err), 130)
})
t.Run("SIGTERM returns 143", func(t *testing.T) {
err := errCtxSignalTerminated{signal: syscall.SIGTERM}
assert.Equal(t, getExitCode(err), 143)
})
}
func TestCmdErrorMessage(t *testing.T) {
t.Run("nil error returns empty string", func(t *testing.T) {
assert.Equal(t, cmdErrorMessage(nil), "")
})
t.Run("generic error returns error message", func(t *testing.T) {
assert.Equal(t, cmdErrorMessage(errors.New("something broke")), "something broke")
})
t.Run("StatusError with Status field", func(t *testing.T) {
err := dockercli.StatusError{Status: "build failed", StatusCode: 1}
assert.Equal(t, cmdErrorMessage(err), "build failed")
})
t.Run("StatusError with only exit code falls back to generic message", func(t *testing.T) {
err := dockercli.StatusError{StatusCode: 42}
assert.Equal(t, cmdErrorMessage(err), "exited with code 42")
})
t.Run("wrapped error preserves message", func(t *testing.T) {
err := fmt.Errorf("wrapper: %w", errors.New("inner failure"))
assert.Equal(t, cmdErrorMessage(err), "wrapper: inner failure")
})
}
func TestVisitAll(t *testing.T) {
root := &cobra.Command{Use: "root"}
sub1 := &cobra.Command{Use: "sub1"}
+1 -1
View File
@@ -1,5 +1,5 @@
variable "GO_VERSION" {
default = "1.25.6"
default = null
}
variable "VERSION" {
default = ""
+1 -1
View File
@@ -2,7 +2,7 @@
# ALPINE_VERSION sets the version of the alpine base image to use.
# It must be a supported tag in the docker.io/library/alpine image repository.
ARG ALPINE_VERSION=3.22
ARG ALPINE_VERSION=3.23
FROM alpine:${ALPINE_VERSION} AS gen
RUN apk add --no-cache bash git
+4 -4
View File
@@ -1,16 +1,16 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.25.6
ARG GO_VERSION=1.25.8
# 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
# that's also available as alpine image variant for the Golang version used.
ARG ALPINE_VERSION=3.22
ARG ALPINE_VERSION=3.23
# BUILDX_VERSION sets the version of buildx to install in the dev container.
# It must be a valid tag in the docker.io/docker/buildx-bin image repository
# on Docker Hub.
ARG BUILDX_VERSION=0.29.1
ARG BUILDX_VERSION=0.31.1
FROM docker/buildx-bin:${BUILDX_VERSION} AS buildx
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS golang
@@ -18,7 +18,7 @@ ENV GOTOOLCHAIN=local
ENV CGO_ENABLED=0
FROM golang AS gofumpt
ARG GOFUMPT_VERSION=v0.7.0
ARG GOFUMPT_VERSION=v0.9.2
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
--mount=type=tmpfs,target=/go/src/ \
+3 -3
View File
@@ -1,13 +1,13 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.25.6
ARG GO_VERSION=1.25.8
# 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
# that's also available as alpine image variant for the Golang version used.
ARG ALPINE_VERSION=3.22
ARG ALPINE_VERSION=3.23
# GOLANGCI_LINT_VERSION sets the version of the golangci/golangci-lint image to use.
ARG GOLANGCI_LINT_VERSION=v2.6.1
ARG GOLANGCI_LINT_VERSION=v2.9.0
FROM golangci/golangci-lint:${GOLANGCI_LINT_VERSION}-alpine AS golangci-lint
+2 -2
View File
@@ -1,11 +1,11 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.25.6
ARG GO_VERSION=1.25.8
# 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
# that's also available as alpine image variant for the Golang version used.
ARG ALPINE_VERSION=3.22
ARG ALPINE_VERSION=3.23
ARG MODOUTDATED_VERSION=v0.8.0
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base
+1 -1
View File
@@ -52,7 +52,7 @@ Build an image from a Dockerfile
> This configuration is only relevant if you're building Windows containers.
>
> For information about the default `docker build`, using Buildx,
> see [`docker buildx build`](https://docs.docker.com/reference/cli/docker/build/).
> see [`docker buildx build`](https://docs.docker.com/reference/cli/docker/buildx/build/).
When building with legacy builder, images are created from a Dockerfile by
running a sequence of [commits](./container_commit.md). This process is
+2 -2
View File
@@ -132,8 +132,8 @@ multiple times, or by passing a comma-separated list of platforms to remove.
The following uses of this option are equivalent;
```console
$ docker image rm --plaform linux/amd64 --platform linux/ppc64le myimage
$ docker image rm --plaform linux/amd64,linux/ppc64le myimage
$ docker image rm --platform linux/amd64 --platform linux/ppc64le myimage
$ docker image rm --platform linux/amd64,linux/ppc64le myimage
```
The following example removes the `linux/amd64` and `linux/ppc64le` variants
+19 -2
View File
@@ -454,7 +454,23 @@ The following options can only be used for bind mounts (`type=bind`):
Read-only mounts are made recursively read-only if kernel is v5.12 or later.
Otherwise the Engine raises an error.</li>
</ul>
When the option is not specified, the default behavior correponds to setting <tt>enabled</tt>.
When the option is not specified, the default behavior corresponds to setting <tt>enabled</tt>.
</td>
</tr>
<tr>
<td><b>bind-create-src</b></td>
<td>
By default, bind mounts require the source path to exist on the daemon host. This is a significant difference
from the <tt>-v</tt> flag, which creates the source path if it doesn't exist.<br />
<br />
Set <tt>bind-create-src</tt> to create the source path on the daemon host if it doesn't exist.<br />
<br />
A value is optional:<br />
<br />
<ul>
<li><tt>true</tt> or <tt>1</tt>: Create path on the daemon host if it doesn't exist.</li>
<li><tt>false</tt> or <tt>0</tt>: Default behavior. Produces an error if the source path doesn't exist on the daemon host.</li>
</ul>
</td>
</tr>
</table>
@@ -591,7 +607,8 @@ or `--volume` flag for `docker run`, with some important exceptions:
- When you use `--mount` with `type=bind`, the host-path must refer to an *existing*
path on the host. The path will not be created for you and the service will fail
with an error if the path does not exist.
with an error if the path does not exist. You can use `bind-create-src` to
create the host path if it doesn't exist.
- The `--mount` flag does not allow you to relabel a volume with `Z` or `z` flags,
which are used for `selinux` labeling.
+8 -8
View File
@@ -189,14 +189,14 @@ also shows the non-truncated task IDs, and error-messages, as can be seen below:
$ docker stack ps --no-trunc voting
ID NAME IMAGE NODE DESIRED STATE CURREN STATE ERROR PORTS
xim5bcqtgk1bxqz91jzo4a1s5 voting_worker.1 dockersamples/examplevotingapp_worker:latest@sha256:3e4ddf59c15f432280a2c0679c4fc5a2ee5a797023c8ef0d3baf7b1385e9fed node2 Running Runnin 32 minutes ago
q7yik0ks1in6kv32gg6y6yjf7 voting_result.1 dockersamples/examplevotingapp_result:before@sha256:83b56996e930c292a6ae5187fda84dd6568a19d97cdb933720be15c757b7463 node1 Running Runnin 32 minutes ago
rx5yo0866nfxc58zf4irsss6n voting_vote.1 dockersamples/examplevotingapp_vote:before@sha256:8e64b182c87de902f2b72321c89b4af4e2b942d76d0b772532ff27ec4c6ebf6 node3 Running Runnin 32 minutes ago
tz6j82jnwrx7n2offljp3mn03 voting_db.1 postgres:9.4@sha256:6046af499eae34d2074c0b53f9a8b404716d415e4a03e68bc1d2f8064f2b027 node1 Running Runnin 32 minutes ago
w48spazhbmxcmbjfi54gs7x90 voting_redis.1 redis:alpine@sha256:9cd405cd1ec1410eaab064a1383d0d8854d1ef74a54e1e4a92fb4ec7bdc3ee7 node2 Running Runnin 32 minutes ago
6jj1m02freg1n3z9n1evrzsbl voting_visualizer.1 dockersamples/visualizer:stable@sha256:f924ad66c8e94b10baaf7bdb9cd491ef4e982a1d048a56a17e02bf5945401e5 node1 Running Runnin 32 minutes ago
kqgdmededccbhz2wuc0e9hx7g voting_vote.2 dockersamples/examplevotingapp_vote:before@sha256:8e64b182c87de902f2b72321c89b4af4e2b942d76d0b772532ff27ec4c6ebf6 node2 Running Runnin 32 minutes ago
t72q3z038jehe1wbh9gdum076 voting_redis.2 redis:alpine@sha256:9cd405cd1ec1410eaab064a1383d0d8854d1ef74a54e1e4a92fb4ec7bdc3ee7 node3 Running Runnin 32 minutes ago
xim5bcqtgk1bxqz91jzo4a1s5 voting_worker.1 dockersamples/examplevotingapp_worker:latest@sha256:3e4ddf59c15f432280a2c0679c4fc5a2ee5a797023c8ef0d3baf7b1385e9fed node2 Running Running 32 minutes ago
q7yik0ks1in6kv32gg6y6yjf7 voting_result.1 dockersamples/examplevotingapp_result:before@sha256:83b56996e930c292a6ae5187fda84dd6568a19d97cdb933720be15c757b7463 node1 Running Running 32 minutes ago
rx5yo0866nfxc58zf4irsss6n voting_vote.1 dockersamples/examplevotingapp_vote:before@sha256:8e64b182c87de902f2b72321c89b4af4e2b942d76d0b772532ff27ec4c6ebf6 node3 Running Running 32 minutes ago
tz6j82jnwrx7n2offljp3mn03 voting_db.1 postgres:9.4@sha256:6046af499eae34d2074c0b53f9a8b404716d415e4a03e68bc1d2f8064f2b027 node1 Running Running 32 minutes ago
w48spazhbmxcmbjfi54gs7x90 voting_redis.1 redis:alpine@sha256:9cd405cd1ec1410eaab064a1383d0d8854d1ef74a54e1e4a92fb4ec7bdc3ee7 node2 Running Running 32 minutes ago
6jj1m02freg1n3z9n1evrzsbl voting_visualizer.1 dockersamples/visualizer:stable@sha256:f924ad66c8e94b10baaf7bdb9cd491ef4e982a1d048a56a17e02bf5945401e5 node1 Running Running 32 minutes ago
kqgdmededccbhz2wuc0e9hx7g voting_vote.2 dockersamples/examplevotingapp_vote:before@sha256:8e64b182c87de902f2b72321c89b4af4e2b942d76d0b772532ff27ec4c6ebf6 node2 Running Running 32 minutes ago
t72q3z038jehe1wbh9gdum076 voting_redis.2 redis:alpine@sha256:9cd405cd1ec1410eaab064a1383d0d8854d1ef74a54e1e4a92fb4ec7bdc3ee7 node3 Running Running 32 minutes ago
```
### <a name="quiet"></a> Only display task IDs (-q, --quiet)
@@ -28,7 +28,6 @@ WARNING! This will remove anonymous local volumes not used by at least one conta
Are you sure you want to continue? [y/N] y
Deleted Volumes:
07c7bdf3e34ab76d921894c2b834f073721fccfbbcba792aa7648e3a7a664c2e
my-named-vol
Total reclaimed space: 36 B
```
+1 -1
View File
@@ -610,7 +610,7 @@ $ sudo dockerd --add-runtime <runtime>=<path>
Defining runtime arguments via the command line is not supported.
For an example configuration for a runc drop-in replacment, see
For an example configuration for a runc drop-in replacement, see
[Alternative container runtimes > youki](https://docs.docker.com/engine/daemon/alternative-runtimes/#youki)
##### Configure the default container runtime
+8
View File
@@ -252,6 +252,14 @@ two paths. The `source` path is the location on the host that you want to
bind mount into the container. The `target` path is the mount destination
inside the container.
By default, bind mounts require the source path to exist on the daemon host. If the
source path doesn't exist, an error is returned. To create the source path on
the daemon host if it doesn't exist, use the `bind-create-src` option:
```console
$ docker run -it --mount type=bind,source=[PATH],target=[PATH],bind-create-src busybox
```
Bind mounts are read-write by default, meaning that you can both read and write
files to and from the mounted location from the container. Changes that you
make, such as adding or editing files, are reflected on the host filesystem:
+1 -1
View File
@@ -4,7 +4,7 @@ services:
image: 'registry:3'
engine:
image: 'docker:${ENGINE_VERSION:-28}-dind'
image: 'docker:${ENGINE_VERSION:-29}-dind'
privileged: true
command: ['--insecure-registry=registry:5000', '--experimental']
environment:
+31
View File
@@ -5,6 +5,7 @@ import (
"io"
"math/rand"
"os/exec"
"path/filepath"
"strings"
"syscall"
"testing"
@@ -160,6 +161,36 @@ func TestMountSubvolume(t *testing.T) {
}
}
func TestMountBindCreateMountpoint(t *testing.T) {
environment.SkipIfDaemonNotLinux(t)
for _, tc := range []struct {
name string
value string
expectSuccess bool
}{
{name: "flag only", value: "bind-create-src", expectSuccess: true},
{name: "true", value: "bind-create-src=true", expectSuccess: true},
{name: "1", value: "bind-create-src=1", expectSuccess: true},
{name: "false", value: "bind-create-src=false", expectSuccess: false},
{name: "0", value: "bind-create-src=0", expectSuccess: false},
} {
t.Run(tc.name, func(t *testing.T) {
srcPath := filepath.Join("/tmp", t.Name(), "does", "not", "exist")
result := icmd.RunCommand("docker", "run", "--rm",
"--mount", "type=bind,src="+srcPath+",dst=/mnt,"+tc.value,
fixtures.AlpineImage, "cat", "/proc/mounts")
if tc.expectSuccess {
result.Assert(t, icmd.Success)
assert.Check(t, is.Contains(result.Stdout(), "/mnt"))
} else {
result.Assert(t, icmd.Expected{ExitCode: 125})
assert.Check(t, is.Contains(result.Stderr(), srcPath))
}
})
}
}
func TestProcessTermination(t *testing.T) {
var out bytes.Buffer
cmd := icmd.Command("docker", "run", "--rm", "-i", fixtures.AlpineImage,
+1 -4
View File
@@ -2,16 +2,13 @@
# ENGINE_VERSION is the version of the (docker-in-docker) Docker Engine to
# test against.
ARG ENGINE_VERSION=28
ARG ENGINE_VERSION=29
FROM docker:${ENGINE_VERSION}-dind
# the openssh-client update is needed for security reasons when using docker:23.0-dind, currently maintained as an lts by mirantis
RUN apk --no-cache add openssl openssh-client openssh-server shadow && \
apk --no-cache upgrade openssl openssh-client openssh-server && \
# TODO(krissetto): `groupadd` can be removed once we only test against moby >= v24
# see https://github.com/docker-library/docker/pull/470
groupadd -f docker && \
useradd --create-home --shell /bin/sh --password $(head -c32 /dev/urandom | base64) penguin && \
usermod -aG docker penguin && \
ssh-keygen -A
+2 -17
View File
@@ -7,9 +7,6 @@ import (
"time"
"github.com/docker/cli/cli/streams"
"github.com/docker/cli/internal/test"
"github.com/moby/moby/client/pkg/progress"
"github.com/moby/moby/client/pkg/streamformatter"
"gotest.tools/v3/assert"
)
@@ -23,23 +20,12 @@ func TestDisplay(t *testing.T) {
})
go func() {
id := test.RandomID()[:12] // short-ID
progressOutput := streamformatter.NewJSONProgressOutput(server, true)
for i := 0; i < 100; i++ {
for range 100 {
select {
case <-ctx.Done():
assert.NilError(t, server.Close(), "failed to close jsonmessage server")
return
default:
err := progressOutput.WriteProgress(progress.Progress{
ID: id,
Message: "Downloading",
Current: int64(i),
Total: 100,
})
if err != nil {
break
}
time.Sleep(100 * time.Millisecond)
}
}
@@ -50,8 +36,7 @@ func TestDisplay(t *testing.T) {
done := make(chan error)
go func() {
out := streams.NewOut(io.Discard)
done <- Display(streamCtx, client, out)
done <- Display(streamCtx, client, streams.NewOut(io.Discard))
}()
cancelStream()
+5 -2
View File
@@ -1,3 +1,6 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
package test
import (
@@ -15,12 +18,12 @@ func CompareMultipleValues(t *testing.T, value, expected string) {
// be guaranteed to have the same order as our expected value
// We'll create maps and use reflect.DeepEquals to check instead:
entriesMap := make(map[string]string)
for _, entry := range strings.Split(value, ",") {
for entry := range strings.SplitSeq(value, ",") {
k, v, _ := strings.Cut(entry, "=")
entriesMap[k] = v
}
expMap := make(map[string]string)
for _, exp := range strings.Split(expected, ",") {
for exp := range strings.SplitSeq(expected, ",") {
k, v, _ := strings.Cut(exp, "=")
expMap[k] = v
}
+8 -9
View File
@@ -1,8 +1,12 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
package volumespec
import (
"errors"
"fmt"
"slices"
"strings"
"unicode"
"unicode/utf8"
@@ -25,7 +29,7 @@ func Parse(spec string) (VolumeConfig, error) {
return volume, nil
}
var buffer []rune
buffer := make([]rune, 0, len(spec))
for _, char := range spec + string(endOfSpec) {
switch {
case isWindowsDrive(buffer, char):
@@ -35,7 +39,7 @@ func Parse(spec string) (VolumeConfig, error) {
populateType(&volume)
return volume, fmt.Errorf("invalid spec: %s: %w", spec, err)
}
buffer = []rune{}
buffer = buffer[:0] // reset, but reuse capacity
default:
buffer = append(buffer, char)
}
@@ -67,7 +71,7 @@ func populateFieldFromBuffer(char rune, buffer []rune, volume *VolumeConfig) err
case char == ':':
return errors.New("too many colons")
}
for _, option := range strings.Split(strBuffer, ",") {
for option := range strings.SplitSeq(strBuffer, ",") {
switch option {
case "ro":
volume.ReadOnly = true
@@ -86,12 +90,7 @@ func populateFieldFromBuffer(char rune, buffer []rune, volume *VolumeConfig) err
}
func isBindOption(option string) bool {
for _, propagation := range mount.Propagations {
if mount.Propagation(option) == propagation {
return true
}
}
return false
return slices.Contains(mount.Propagations, mount.Propagation(option))
}
func populateType(volume *VolumeConfig) {
+2 -2
View File
@@ -86,7 +86,7 @@ Repository entries separated by a colon for all images:
docker image ls --format "{{.ID}}: {{.Repository}}"
77af4d6b9913: <none>
b6fa739cedf5: committ
b6fa739cedf5: commit
78a85c484bad: ipbabble
30557a29d5ab: docker
5ed6274db6ce: <none>
@@ -100,7 +100,7 @@ To list all images with their repository and tag in a table format you can use:
docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"
IMAGE ID REPOSITORY TAG
77af4d6b9913 <none> <none>
b6fa739cedf5 committ latest
b6fa739cedf5 commit latest
78a85c484bad ipbabble <none>
30557a29d5ab docker latest
5ed6274db6ce <none> <none>
+1 -1
View File
@@ -105,7 +105,7 @@ func (*GpuOpts) Type() string {
// String returns a string repr of this option
func (o *GpuOpts) String() string {
gpus := []string{}
gpus := make([]string, 0, len(o.values))
for _, gpu := range o.values {
gpus = append(gpus, fmt.Sprintf("%v", gpu))
}
+7 -2
View File
@@ -57,7 +57,7 @@ func (m *MountOpt) Set(value string) error {
if !hasValue {
switch key {
case "readonly", "ro", "volume-nocopy", "bind-nonrecursive":
case "readonly", "ro", "volume-nocopy", "bind-nonrecursive", "bind-create-src":
// boolean values
default:
return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
@@ -102,6 +102,11 @@ func (m *MountOpt) Set(value string) error {
default:
return fmt.Errorf(`invalid value for %s: %s (must be "enabled", "disabled", "writable", or "readonly")`, key, val)
}
case "bind-create-src":
ensureBindOptions(&mount).CreateMountpoint, err = parseBoolValue(key, val, hasValue)
if err != nil {
return err
}
case "volume-subpath":
ensureVolumeOptions(&mount).Subpath = val
case "volume-nocopy":
@@ -151,7 +156,7 @@ func (*MountOpt) Type() string {
// String returns a string repr of this option
func (m *MountOpt) String() string {
mounts := []string{}
mounts := make([]string, 0, len(m.values))
for _, mount := range m.values {
repr := fmt.Sprintf("%s %s %s", mount.Type, mount.Source, mount.Target)
mounts = append(mounts, repr)
+44
View File
@@ -475,6 +475,50 @@ func TestMountOptSetTmpfsNoError(t *testing.T) {
}
}
func TestMountOptSetBindCreateSrc(t *testing.T) {
tests := []struct {
value string
exp bool
expErr string
}{
{value: "", exp: false},
{value: "bind-create-src", exp: true},
{value: "bind-create-src=", expErr: `invalid value for 'bind-create-src': value is empty`},
{value: "bind-create-src= true", expErr: `invalid value for 'bind-create-src' in 'bind-create-src= true': value should not have whitespace`},
{value: "bind-create-src=no", expErr: `invalid value for 'bind-create-src': invalid boolean value ("no"): must be one of "true", "1", "false", or "0" (default "true")`},
{value: "bind-create-src=1", exp: true},
{value: "bind-create-src=true", exp: true},
{value: "bind-create-src=0", exp: false},
{value: "bind-create-src=false", exp: false},
}
for _, tc := range tests {
name := tc.value
if name == "" {
name = "not set"
}
t.Run(name, func(t *testing.T) {
val := "type=bind,target=/foo,source=/foo"
if tc.value != "" {
val += "," + tc.value
}
var m MountOpt
err := m.Set(val)
if tc.expErr != "" {
assert.Error(t, err, tc.expErr)
return
}
assert.NilError(t, err)
if tc.value == "" {
assert.Check(t, is.Nil(m.values[0].BindOptions))
} else {
assert.Check(t, m.values[0].BindOptions != nil)
assert.Check(t, is.Equal(m.values[0].BindOptions.CreateMountpoint, tc.exp))
}
})
}
}
func TestMountOptSetBindRecursive(t *testing.T) {
t.Run("enabled", func(t *testing.T) {
var m MountOpt
+5 -6
View File
@@ -1,3 +1,6 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
package opts
import (
@@ -7,6 +10,7 @@ import (
"math/big"
"net"
"path"
"slices"
"strings"
"github.com/docker/cli/internal/lazyregexp"
@@ -104,12 +108,7 @@ func (opts *ListOpts) GetAllOrEmpty() []string {
// Get checks the existence of the specified key.
func (opts *ListOpts) Get(key string) bool {
for _, k := range *opts.values {
if k == key {
return true
}
}
return false
return slices.Contains(*opts.values, key)
}
// Len returns the amount of element in the slice.
+1 -1
View File
@@ -86,7 +86,7 @@ func (*ConfigOpt) Type() string {
// String returns a string repr of this option
func (o *ConfigOpt) String() string {
configs := []string{}
configs := make([]string, 0, len(o.values))
for _, config := range o.values {
repr := fmt.Sprintf("%s -> %s", config.ConfigName, config.File.Name)
configs = append(configs, repr)
+5 -7
View File
@@ -1,8 +1,12 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
package swarmopts
import (
"bytes"
"os"
"slices"
"testing"
"github.com/docker/go-connections/nat"
@@ -371,13 +375,7 @@ func TestConvertPortToPortConfigWithIP(t *testing.T) {
func assertContains(t *testing.T, portConfigs []swarm.PortConfig, expected swarm.PortConfig) {
t.Helper()
contains := false
for _, portConfig := range portConfigs {
if portConfig == expected {
contains = true
break
}
}
contains := slices.Contains(portConfigs, expected)
if !contains {
t.Errorf("expected %v to contain %v, did not", portConfigs, expected)
}
+1 -1
View File
@@ -85,7 +85,7 @@ func (*SecretOpt) Type() string {
// String returns a string repr of this option
func (o *SecretOpt) String() string {
secrets := []string{}
secrets := make([]string, 0, len(o.values))
for _, secret := range o.values {
repr := fmt.Sprintf("%s -> %s", secret.SecretName, secret.File.Name)
secrets = append(secrets, repr)
+9
View File
@@ -102,6 +102,15 @@ 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"
fi
# XXX: Disable netgo on Windows and use Windows system resolver instead.
#
# go1.19 and newer added support for netgo on Windows (https://go.dev/doc/go1.19#net),
# which may not respect VPN adaptors (such as Twingate) due to resolver ordering,
# resulting in queries being sent through the local network adapter instead of the
# VPN tunnel. See https://github.com/docker/cli/issues/6665
if [ "$(go env GOOS)" = "windows" ]; then
GO_BUILDTAGS=$(echo "$GO_BUILDTAGS" | sed 's/\(^\| \)netgo\( \|$\)/\1/g')
fi
if [ -n "$GO_STRIP" ]; then
# if stripping enabled and building with llvm < 12 against darwin/amd64
# platform, it will fail with:
+23 -23
View File
@@ -26,10 +26,10 @@ 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.19
github.com/mattn/go-runewidth v0.0.20
github.com/moby/go-archive v0.2.0
github.com/moby/moby/api v1.53.0
github.com/moby/moby/client v0.2.2
github.com/moby/moby/api v1.54.0
github.com/moby/moby/client v0.3.0
github.com/moby/patternmatcher v0.6.0
github.com/moby/swarmkit/v2 v2.1.1
github.com/moby/sys/atomicwriter v0.1.0
@@ -47,19 +47,19 @@ require (
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.63.0
go.opentelemetry.io/otel v1.38.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0
go.opentelemetry.io/otel/metric v1.38.0
go.opentelemetry.io/otel/sdk v1.38.0
go.opentelemetry.io/otel/sdk/metric v1.38.0
go.opentelemetry.io/otel/trace v1.38.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0
go.opentelemetry.io/otel v1.40.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0
go.opentelemetry.io/otel/metric v1.40.0
go.opentelemetry.io/otel/sdk v1.40.0
go.opentelemetry.io/otel/sdk/metric v1.40.0
go.opentelemetry.io/otel/trace v1.40.0
go.yaml.in/yaml/v3 v3.0.4
golang.org/x/sync v0.19.0
golang.org/x/sys v0.40.0
golang.org/x/term v0.39.0
golang.org/x/text v0.33.0
golang.org/x/sys v0.41.0
golang.org/x/term v0.40.0
golang.org/x/text v0.34.0
gotest.tools/v3 v3.5.2
tags.cncf.io/container-device-interface v1.1.0
)
@@ -80,9 +80,9 @@ 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.27.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/klauspost/compress v1.18.3 // indirect
github.com/klauspost/compress v1.18.4 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/sys/user v0.4.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
@@ -96,12 +96,12 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
go.etcd.io/etcd/raft/v3 v3.5.16 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
golang.org/x/net v0.49.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
golang.org/x/net v0.50.0 // indirect
golang.org/x/time v0.14.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/grpc v1.75.0 // indirect
google.golang.org/protobuf v1.36.9 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
google.golang.org/grpc v1.78.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
)
+46 -46
View File
@@ -87,8 +87,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.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII=
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,8 +96,8 @@ 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.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw=
github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
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=
@@ -106,17 +106,17 @@ 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.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ=
github.com/mattn/go-runewidth v0.0.20/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.53.0 h1:PihqG1ncw4W+8mZs69jlwGXdaYBeb5brF6BL7mPIS/w=
github.com/moby/moby/api v1.53.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
github.com/moby/moby/client v0.2.2 h1:Pt4hRMCAIlyjL3cr8M5TrXCwKzguebPAc2do2ur7dEM=
github.com/moby/moby/client v0.2.2/go.mod h1:2EkIPVNCqR05CMIzL1mfA07t0HvVUUOl85pasRz/GmQ=
github.com/moby/moby/api v1.54.0 h1:7kbUgyiKcoBhm0UrWbdrMs7RX8dnwzURKVbZGy2GnL0=
github.com/moby/moby/api v1.54.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
github.com/moby/moby/client v0.3.0 h1:UUGL5okry+Aomj3WhGt9Aigl3ZOxZGqR7XPo+RLPlKs=
github.com/moby/moby/client v0.3.0/go.mod h1:HJgFbJRvogDQjbM8fqc1MCEm4mIAGMLjXbgwoZp6jCQ=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/swarmkit/v2 v2.1.1 h1:yvTJ8MMCc3f0qTA44J6R59EZ5yZawdYopkpuLk4+ICU=
@@ -206,26 +206,26 @@ go.etcd.io/etcd/raft/v3 v3.5.16 h1:zBXA3ZUpYs1AwiLGPafYAKKl/CORn/uaxYDwlNwndAk=
go.etcd.io/etcd/raft/v3 v3.5.16/go.mod h1:P4UP14AxofMJ/54boWilabqqWoW9eLodl6I5GdGzazI=
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.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0=
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0 h1:NOyNnS19BF2SUDApbOKbDtWZ0IK7b8FJ2uAGdIWOGb0=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0/go.mod h1:VL6EgVikRLcJa9ftukrHu/ZkkhFBSo1lzvdBC9CF1ss=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs=
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
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=
@@ -242,8 +242,8 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
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.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
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=
@@ -259,14 +259,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.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
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.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -279,14 +279,14 @@ 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.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
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=
+1
View File
@@ -27,6 +27,7 @@ go_library(
"//internal/httprule",
"//utilities",
"@org_golang_google_genproto_googleapis_api//httpbody",
"@org_golang_google_grpc//:grpc",
"@org_golang_google_grpc//codes",
"@org_golang_google_grpc//grpclog",
"@org_golang_google_grpc//health/grpc_health_v1",
+3 -3
View File
@@ -201,13 +201,13 @@ func annotateContext(ctx context.Context, mux *ServeMux, req *http.Request, rpcM
if timeout != 0 {
ctx, _ = context.WithTimeout(ctx, timeout)
}
if len(pairs) == 0 {
return ctx, nil, nil
}
md := metadata.Pairs(pairs...)
for _, mda := range mux.metadataAnnotators {
md = metadata.Join(md, mda(ctx, req))
}
if len(md) == 0 {
return ctx, nil, nil
}
return ctx, md, nil
}
@@ -66,7 +66,7 @@ func (j *JSONPb) marshalTo(w io.Writer, v interface{}) error {
var (
// protoMessageType is stored to prevent constant lookup of the same type at runtime.
protoMessageType = reflect.TypeOf((*proto.Message)(nil)).Elem()
protoMessageType = reflect.TypeFor[proto.Message]()
)
// marshalNonProto marshals a non-message field of a protobuf message.
@@ -325,9 +325,9 @@ type protoEnum interface {
EnumDescriptor() ([]byte, []int)
}
var typeProtoEnum = reflect.TypeOf((*protoEnum)(nil)).Elem()
var typeProtoEnum = reflect.TypeFor[protoEnum]()
var typeProtoMessage = reflect.TypeOf((*proto.Message)(nil)).Elem()
var typeProtoMessage = reflect.TypeFor[proto.Message]()
// Delimiter for newline encoded JSON streams.
func (j *JSONPb) Delimiter() []byte {
+13 -5
View File
@@ -10,6 +10,7 @@ import (
"strings"
"github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/health/grpc_health_v1"
@@ -281,15 +282,22 @@ func WithHealthEndpointAt(healthCheckClient grpc_health_v1.HealthClient, endpoin
http.MethodGet, endpointPath, func(w http.ResponseWriter, r *http.Request, _ map[string]string,
) {
_, outboundMarshaler := MarshalerForRequest(s, r)
resp, err := healthCheckClient.Check(r.Context(), &grpc_health_v1.HealthCheckRequest{
Service: r.URL.Query().Get("service"),
})
annotatedContext, err := AnnotateContext(r.Context(), s, r, grpc_health_v1.Health_Check_FullMethodName, WithHTTPPathPattern(endpointPath))
if err != nil {
s.errorHandler(r.Context(), s, outboundMarshaler, w, r, err)
return
}
var md ServerMetadata
resp, err := healthCheckClient.Check(annotatedContext, &grpc_health_v1.HealthCheckRequest{
Service: r.URL.Query().Get("service"),
}, grpc.Header(&md.HeaderMD), grpc.Trailer(&md.TrailerMD))
annotatedContext = NewServerMetadataContext(annotatedContext, md)
if err != nil {
s.errorHandler(annotatedContext, s, outboundMarshaler, w, r, err)
return
}
w.Header().Set("Content-Type", "application/json")
if resp.GetStatus() != grpc_health_v1.HealthCheckResponse_SERVING {
@@ -300,7 +308,7 @@ func WithHealthEndpointAt(healthCheckClient grpc_health_v1.HealthClient, endpoin
err = status.Error(codes.NotFound, resp.String())
}
s.errorHandler(r.Context(), s, outboundMarshaler, w, r, err)
s.errorHandler(annotatedContext, s, outboundMarshaler, w, r, err)
return
}
+15 -8
View File
@@ -7,7 +7,7 @@ This package provides various compression algorithms.
* Optimized [deflate](https://godoc.org/github.com/klauspost/compress/flate) packages which can be used as a dropin replacement for [gzip](https://godoc.org/github.com/klauspost/compress/gzip), [zip](https://godoc.org/github.com/klauspost/compress/zip) and [zlib](https://godoc.org/github.com/klauspost/compress/zlib).
* [snappy](https://github.com/klauspost/compress/tree/master/snappy) is a drop-in replacement for `github.com/golang/snappy` offering better compression and concurrent streams.
* [huff0](https://github.com/klauspost/compress/tree/master/huff0) and [FSE](https://github.com/klauspost/compress/tree/master/fse) implementations for raw entropy encoding.
* [gzhttp](https://github.com/klauspost/compress/tree/master/gzhttp) Provides client and server wrappers for handling gzipped requests efficiently.
* [gzhttp](https://github.com/klauspost/compress/tree/master/gzhttp) Provides client and server wrappers for handling gzipped/zstd HTTP requests efficiently.
* [pgzip](https://github.com/klauspost/pgzip) is a separate package that provides a very fast parallel gzip implementation.
[![Go Reference](https://pkg.go.dev/badge/klauspost/compress.svg)](https://pkg.go.dev/github.com/klauspost/compress?tab=subdirectories)
@@ -26,8 +26,14 @@ This package will support the current Go version and 2 versions back.
Use the links above for more information on each.
# changelog
* Jan 16th, 2026 [1.18.3](https://github.com/klauspost/compress/releases/tag/v1.18.3)
* Downstream CVE-2025-61728. See [golang/go#77102](https://github.com/golang/go/issues/77102).
* Oct 20, 2025 - [1.18.1](https://github.com/klauspost/compress/releases/tag/v1.18.1)
* Dec 1st, 2025 - [1.18.2](https://github.com/klauspost/compress/releases/tag/v1.18.2)
* flate: Fix invalid encoding on level 9 with single value input in https://github.com/klauspost/compress/pull/1115
* flate: reduce stateless allocations by @RXamzin in https://github.com/klauspost/compress/pull/1106
* Oct 20, 2025 - [1.18.1](https://github.com/klauspost/compress/releases/tag/v1.18.1) - RETRACTED
* zstd: Add simple zstd EncodeTo/DecodeTo functions https://github.com/klauspost/compress/pull/1079
* zstd: Fix incorrect buffer size in dictionary encodes https://github.com/klauspost/compress/pull/1059
* s2: check for cap, not len of buffer in EncodeBetter/Best by @vdarulis in https://github.com/klauspost/compress/pull/1080
@@ -603,7 +609,7 @@ While the release has been extensively tested, it is recommended to testing when
# deflate usage
The packages are drop-in replacements for standard libraries. Simply replace the import path to use them:
The packages are drop-in replacements for standard library [deflate](https://godoc.org/github.com/klauspost/compress/flate), [gzip](https://godoc.org/github.com/klauspost/compress/gzip), [zip](https://godoc.org/github.com/klauspost/compress/zip), and [zlib](https://godoc.org/github.com/klauspost/compress/zlib). Simply replace the import path to use them:
Typical speed is about 2x of the standard library packages.
@@ -614,17 +620,15 @@ Typical speed is about 2x of the standard library packages.
| `archive/zip` | `github.com/klauspost/compress/zip` | [zip](https://pkg.go.dev/github.com/klauspost/compress/zip?tab=doc) |
| `compress/flate` | `github.com/klauspost/compress/flate` | [flate](https://pkg.go.dev/github.com/klauspost/compress/flate?tab=doc) |
* Optimized [deflate](https://godoc.org/github.com/klauspost/compress/flate) packages which can be used as a dropin replacement for [gzip](https://godoc.org/github.com/klauspost/compress/gzip), [zip](https://godoc.org/github.com/klauspost/compress/zip) and [zlib](https://godoc.org/github.com/klauspost/compress/zlib).
You may also be interested in [pgzip](https://github.com/klauspost/pgzip), which is a drop-in replacement for gzip, which support multithreaded compression on big files and the optimized [crc32](https://github.com/klauspost/crc32) package used by these packages.
You may also be interested in [pgzip](https://github.com/klauspost/pgzip), which is a drop in replacement for gzip, which support multithreaded compression on big files and the optimized [crc32](https://github.com/klauspost/crc32) package used by these packages.
The packages contains the same as the standard library, so you can use the godoc for that: [gzip](http://golang.org/pkg/compress/gzip/), [zip](http://golang.org/pkg/archive/zip/), [zlib](http://golang.org/pkg/compress/zlib/), [flate](http://golang.org/pkg/compress/flate/).
The packages implement the same API as the standard library, so you can use the original godoc documentation: [gzip](http://golang.org/pkg/compress/gzip/), [zip](http://golang.org/pkg/archive/zip/), [zlib](http://golang.org/pkg/compress/zlib/), [flate](http://golang.org/pkg/compress/flate/).
Currently there is only minor speedup on decompression (mostly CRC32 calculation).
Memory usage is typically 1MB for a Writer. stdlib is in the same range.
If you expect to have a lot of concurrently allocated Writers consider using
the stateless compress described below.
the stateless compression described below.
For compression performance, see: [this spreadsheet](https://docs.google.com/spreadsheets/d/1nuNE2nPfuINCZJRMt6wFWhKpToF95I47XjSsc-1rbPQ/edit?usp=sharing).
@@ -684,3 +688,6 @@ Here are other packages of good quality and pure Go (no cgo wrappers or autoconv
This code is licensed under the same conditions as the original Go code. See LICENSE file.
+19 -9
View File
@@ -39,9 +39,6 @@ type Decoder struct {
frame *frameDec
// Custom dictionaries.
dicts map[uint32]*dict
// streamWg is the waitgroup for all streams
streamWg sync.WaitGroup
}
@@ -101,12 +98,10 @@ func NewReader(r io.Reader, opts ...DOption) (*Decoder, error) {
d.current.err = ErrDecoderNilInput
}
// Transfer option dicts.
d.dicts = make(map[uint32]*dict, len(d.o.dicts))
for _, dc := range d.o.dicts {
d.dicts[dc.id] = dc
// Initialize dict map if needed.
if d.o.dicts == nil {
d.o.dicts = make(map[uint32]*dict)
}
d.o.dicts = nil
// Create decoders
d.decoders = make(chan *blockDec, d.o.concurrent)
@@ -238,6 +233,21 @@ func (d *Decoder) Reset(r io.Reader) error {
return nil
}
// ResetWithOptions will reset the decoder and apply the given options
// for the next stream or DecodeAll operation.
// Options are applied on top of the existing options.
// Some options cannot be changed on reset and will return an error.
func (d *Decoder) ResetWithOptions(r io.Reader, opts ...DOption) error {
d.o.resetOpt = true
defer func() { d.o.resetOpt = false }()
for _, o := range opts {
if err := o(&d.o); err != nil {
return err
}
}
return d.Reset(r)
}
// drainOutput will drain the output until errEndOfStream is sent.
func (d *Decoder) drainOutput() {
if d.current.cancel != nil {
@@ -930,7 +940,7 @@ decodeStream:
}
func (d *Decoder) setDict(frame *frameDec) (err error) {
dict, ok := d.dicts[frame.DictionaryID]
dict, ok := d.o.dicts[frame.DictionaryID]
if ok {
if debugDecoder {
println("setting dict", frame.DictionaryID)
+52 -8
View File
@@ -20,10 +20,11 @@ type decoderOptions struct {
concurrent int
maxDecodedSize uint64
maxWindowSize uint64
dicts []*dict
dicts map[uint32]*dict
ignoreChecksum bool
limitToCap bool
decodeBufsBelow int
resetOpt bool
}
func (o *decoderOptions) setDefault() {
@@ -42,8 +43,15 @@ func (o *decoderOptions) setDefault() {
// WithDecoderLowmem will set whether to use a lower amount of memory,
// but possibly have to allocate more while running.
// Cannot be changed with ResetWithOptions.
func WithDecoderLowmem(b bool) DOption {
return func(o *decoderOptions) error { o.lowMem = b; return nil }
return func(o *decoderOptions) error {
if o.resetOpt && b != o.lowMem {
return errors.New("WithDecoderLowmem cannot be changed on Reset")
}
o.lowMem = b
return nil
}
}
// WithDecoderConcurrency sets the number of created decoders.
@@ -53,18 +61,23 @@ func WithDecoderLowmem(b bool) DOption {
// inflight blocks.
// When decoding streams and setting maximum to 1,
// no async decoding will be done.
// The value supplied must be at least 0.
// When a value of 0 is provided GOMAXPROCS will be used.
// By default this will be set to 4 or GOMAXPROCS, whatever is lower.
// Cannot be changed with ResetWithOptions.
func WithDecoderConcurrency(n int) DOption {
return func(o *decoderOptions) error {
if n < 0 {
return errors.New("concurrency must be at least 1")
return errors.New("concurrency must be at least 0")
}
newVal := n
if n == 0 {
o.concurrent = runtime.GOMAXPROCS(0)
} else {
o.concurrent = n
newVal = runtime.GOMAXPROCS(0)
}
if o.resetOpt && newVal != o.concurrent {
return errors.New("WithDecoderConcurrency cannot be changed on Reset")
}
o.concurrent = newVal
return nil
}
}
@@ -73,6 +86,7 @@ func WithDecoderConcurrency(n int) DOption {
// non-streaming operations or maximum window size for streaming operations.
// This can be used to control memory usage of potentially hostile content.
// Maximum is 1 << 63 bytes. Default is 64GiB.
// Can be changed with ResetWithOptions.
func WithDecoderMaxMemory(n uint64) DOption {
return func(o *decoderOptions) error {
if n == 0 {
@@ -92,16 +106,20 @@ func WithDecoderMaxMemory(n uint64) DOption {
// "zstd --train" from the Zstandard reference implementation.
//
// If several dictionaries with the same ID are provided, the last one will be used.
// Can be changed with ResetWithOptions.
//
// [dictionary format]: https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary-format
func WithDecoderDicts(dicts ...[]byte) DOption {
return func(o *decoderOptions) error {
if o.dicts == nil {
o.dicts = make(map[uint32]*dict)
}
for _, b := range dicts {
d, err := loadDict(b)
if err != nil {
return err
}
o.dicts = append(o.dicts, d)
o.dicts[d.id] = d
}
return nil
}
@@ -109,12 +127,16 @@ func WithDecoderDicts(dicts ...[]byte) DOption {
// WithDecoderDictRaw registers a dictionary that may be used by the decoder.
// The slice content can be arbitrary data.
// Can be changed with ResetWithOptions.
func WithDecoderDictRaw(id uint32, content []byte) DOption {
return func(o *decoderOptions) error {
if bits.UintSize > 32 && uint(len(content)) > dictMaxLength {
return fmt.Errorf("dictionary of size %d > 2GiB too large", len(content))
}
o.dicts = append(o.dicts, &dict{id: id, content: content, offsets: [3]int{1, 4, 8}})
if o.dicts == nil {
o.dicts = make(map[uint32]*dict)
}
o.dicts[id] = &dict{id: id, content: content, offsets: [3]int{1, 4, 8}}
return nil
}
}
@@ -124,6 +146,7 @@ func WithDecoderDictRaw(id uint32, content []byte) DOption {
// The Decoder will likely allocate more memory based on the WithDecoderLowmem setting.
// If WithDecoderMaxMemory is set to a lower value, that will be used.
// Default is 512MB, Maximum is ~3.75 TB as per zstandard spec.
// Can be changed with ResetWithOptions.
func WithDecoderMaxWindow(size uint64) DOption {
return func(o *decoderOptions) error {
if size < MinWindowSize {
@@ -141,6 +164,7 @@ func WithDecoderMaxWindow(size uint64) DOption {
// or any size set in WithDecoderMaxMemory.
// This can be used to limit decoding to a specific maximum output size.
// Disabled by default.
// Can be changed with ResetWithOptions.
func WithDecodeAllCapLimit(b bool) DOption {
return func(o *decoderOptions) error {
o.limitToCap = b
@@ -153,17 +177,37 @@ func WithDecodeAllCapLimit(b bool) DOption {
// This typically uses less allocations but will have the full decompressed object in memory.
// Note that DecodeAllCapLimit will disable this, as well as giving a size of 0 or less.
// Default is 128KiB.
// Cannot be changed with ResetWithOptions.
func WithDecodeBuffersBelow(size int) DOption {
return func(o *decoderOptions) error {
if o.resetOpt && size != o.decodeBufsBelow {
return errors.New("WithDecodeBuffersBelow cannot be changed on Reset")
}
o.decodeBufsBelow = size
return nil
}
}
// IgnoreChecksum allows to forcibly ignore checksum checking.
// Can be changed with ResetWithOptions.
func IgnoreChecksum(b bool) DOption {
return func(o *decoderOptions) error {
o.ignoreChecksum = b
return nil
}
}
// WithDecoderDictDelete removes dictionaries by ID.
// If no ids are passed, all dictionaries are deleted.
// Should be used with ResetWithOptions.
func WithDecoderDictDelete(ids ...uint32) DOption {
return func(o *decoderOptions) error {
if len(ids) == 0 {
clear(o.dicts)
}
for _, id := range ids {
delete(o.dicts, id)
}
return nil
}
}
+16
View File
@@ -131,6 +131,22 @@ func (e *Encoder) Reset(w io.Writer) {
s.frameContentSize = 0
}
// ResetWithOptions will re-initialize the writer and apply the given options
// as a new, independent stream.
// Options are applied on top of the existing options.
// Some options cannot be changed on reset and will return an error.
func (e *Encoder) ResetWithOptions(w io.Writer, opts ...EOption) error {
e.o.resetOpt = true
defer func() { e.o.resetOpt = false }()
for _, o := range opts {
if err := o(&e.o); err != nil {
return err
}
}
e.Reset(w)
return nil
}
// ResetContentSize will reset and set a content size for the next stream.
// If the bytes written does not match the size given an error will be returned
// when calling Close().
+41 -3
View File
@@ -14,6 +14,7 @@ type EOption func(*encoderOptions) error
// options retains accumulated state of multiple options.
type encoderOptions struct {
resetOpt bool
concurrent int
level EncoderLevel
single *bool
@@ -71,19 +72,28 @@ func (o encoderOptions) encoder() encoder {
// WithEncoderCRC will add CRC value to output.
// Output will be 4 bytes larger.
// Can be changed with ResetWithOptions.
func WithEncoderCRC(b bool) EOption {
return func(o *encoderOptions) error { o.crc = b; return nil }
}
// WithEncoderConcurrency will set the concurrency,
// meaning the maximum number of encoders to run concurrently.
// The value supplied must be at least 1.
// The value supplied must be at least 0.
// When a value of 0 is provided GOMAXPROCS will be used.
// For streams, setting a value of 1 will disable async compression.
// By default this will be set to GOMAXPROCS.
// Cannot be changed with ResetWithOptions.
func WithEncoderConcurrency(n int) EOption {
return func(o *encoderOptions) error {
if n <= 0 {
return fmt.Errorf("concurrency must be at least 1")
if n < 0 {
return errors.New("concurrency must at least 0")
}
if n == 0 {
n = runtime.GOMAXPROCS(0)
}
if o.resetOpt && n != o.concurrent {
return errors.New("WithEncoderConcurrency cannot be changed on Reset")
}
o.concurrent = n
return nil
@@ -95,6 +105,7 @@ func WithEncoderConcurrency(n int) EOption {
// A larger value will enable better compression but allocate more memory and,
// for above-default values, take considerably longer.
// The default value is determined by the compression level and max 8MB.
// Cannot be changed with ResetWithOptions.
func WithWindowSize(n int) EOption {
return func(o *encoderOptions) error {
switch {
@@ -105,6 +116,9 @@ func WithWindowSize(n int) EOption {
case (n & (n - 1)) != 0:
return errors.New("window size must be a power of 2")
}
if o.resetOpt && n != o.windowSize {
return errors.New("WithWindowSize cannot be changed on Reset")
}
o.windowSize = n
o.customWindow = true
@@ -122,6 +136,7 @@ func WithWindowSize(n int) EOption {
// n must be > 0 and <= 1GB, 1<<30 bytes.
// The padded area will be filled with data from crypto/rand.Reader.
// If `EncodeAll` is used with data already in the destination, the total size will be multiple of this.
// Can be changed with ResetWithOptions.
func WithEncoderPadding(n int) EOption {
return func(o *encoderOptions) error {
if n <= 0 {
@@ -215,12 +230,16 @@ func (e EncoderLevel) String() string {
}
// WithEncoderLevel specifies a predefined compression level.
// Cannot be changed with ResetWithOptions.
func WithEncoderLevel(l EncoderLevel) EOption {
return func(o *encoderOptions) error {
switch {
case l <= speedNotSet || l >= speedLast:
return fmt.Errorf("unknown encoder level")
}
if o.resetOpt && l != o.level {
return errors.New("WithEncoderLevel cannot be changed on Reset")
}
o.level = l
if !o.customWindow {
switch o.level {
@@ -248,6 +267,7 @@ func WithEncoderLevel(l EncoderLevel) EOption {
// WithZeroFrames will encode 0 length input as full frames.
// This can be needed for compatibility with zstandard usage,
// but is not needed for this package.
// Can be changed with ResetWithOptions.
func WithZeroFrames(b bool) EOption {
return func(o *encoderOptions) error {
o.fullZero = b
@@ -259,6 +279,7 @@ func WithZeroFrames(b bool) EOption {
// Disabling this will skip incompressible data faster, but in cases with no matches but
// skewed character distribution compression is lost.
// Default value depends on the compression level selected.
// Can be changed with ResetWithOptions.
func WithAllLitEntropyCompression(b bool) EOption {
return func(o *encoderOptions) error {
o.customALEntropy = true
@@ -270,6 +291,7 @@ func WithAllLitEntropyCompression(b bool) EOption {
// WithNoEntropyCompression will always skip entropy compression of literals.
// This can be useful if content has matches, but unlikely to benefit from entropy
// compression. Usually the slight speed improvement is not worth enabling this.
// Can be changed with ResetWithOptions.
func WithNoEntropyCompression(b bool) EOption {
return func(o *encoderOptions) error {
o.noEntropy = b
@@ -287,6 +309,7 @@ func WithNoEntropyCompression(b bool) EOption {
// This is only a recommendation, each decoder is free to support higher or lower limits, depending on local limitations.
// If this is not specified, block encodes will automatically choose this based on the input size and the window size.
// This setting has no effect on streamed encodes.
// Can be changed with ResetWithOptions.
func WithSingleSegment(b bool) EOption {
return func(o *encoderOptions) error {
o.single = &b
@@ -298,8 +321,12 @@ func WithSingleSegment(b bool) EOption {
// slower encoding speed.
// This will not change the window size which is the primary function for reducing
// memory usage. See WithWindowSize.
// Cannot be changed with ResetWithOptions.
func WithLowerEncoderMem(b bool) EOption {
return func(o *encoderOptions) error {
if o.resetOpt && b != o.lowMem {
return errors.New("WithLowerEncoderMem cannot be changed on Reset")
}
o.lowMem = b
return nil
}
@@ -311,6 +338,7 @@ func WithLowerEncoderMem(b bool) EOption {
// "zstd --train" from the Zstandard reference implementation.
//
// The encoder *may* choose to use no dictionary instead for certain payloads.
// Can be changed with ResetWithOptions.
//
// [dictionary format]: https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary-format
func WithEncoderDict(dict []byte) EOption {
@@ -328,6 +356,7 @@ func WithEncoderDict(dict []byte) EOption {
//
// The slice content may contain arbitrary data. It will be used as an initial
// history.
// Can be changed with ResetWithOptions.
func WithEncoderDictRaw(id uint32, content []byte) EOption {
return func(o *encoderOptions) error {
if bits.UintSize > 32 && uint(len(content)) > dictMaxLength {
@@ -337,3 +366,12 @@ func WithEncoderDictRaw(id uint32, content []byte) EOption {
return nil
}
}
// WithEncoderDictDelete clears the dictionary, so no dictionary will be used.
// Should be used with ResetWithOptions.
func WithEncoderDictDelete() EOption {
return func(o *encoderOptions) error {
o.dict = nil
return nil
}
}
+318 -281
View File
@@ -6,46 +6,47 @@ var combining = table{
{0x0300, 0x036F}, {0x0483, 0x0489}, {0x07EB, 0x07F3},
{0x0C00, 0x0C00}, {0x0C04, 0x0C04}, {0x0CF3, 0x0CF3},
{0x0D00, 0x0D01}, {0x135D, 0x135F}, {0x1A7F, 0x1A7F},
{0x1AB0, 0x1ACE}, {0x1B6B, 0x1B73}, {0x1DC0, 0x1DFF},
{0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2DE0, 0x2DFF},
{0x3099, 0x309A}, {0xA66F, 0xA672}, {0xA674, 0xA67D},
{0xA69E, 0xA69F}, {0xA6F0, 0xA6F1}, {0xA8E0, 0xA8F1},
{0xFE20, 0xFE2F}, {0x101FD, 0x101FD}, {0x10376, 0x1037A},
{0x10EAB, 0x10EAC}, {0x10F46, 0x10F50}, {0x10F82, 0x10F85},
{0x11300, 0x11301}, {0x1133B, 0x1133C}, {0x11366, 0x1136C},
{0x11370, 0x11374}, {0x16AF0, 0x16AF4}, {0x1CF00, 0x1CF2D},
{0x1CF30, 0x1CF46}, {0x1D165, 0x1D169}, {0x1D16D, 0x1D172},
{0x1D17B, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD},
{0x1D242, 0x1D244}, {0x1E000, 0x1E006}, {0x1E008, 0x1E018},
{0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, {0x1E026, 0x1E02A},
{0x1E08F, 0x1E08F}, {0x1E8D0, 0x1E8D6},
{0x1AB0, 0x1ADD}, {0x1AE0, 0x1AEB}, {0x1B6B, 0x1B73},
{0x1DC0, 0x1DFF}, {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1},
{0x2DE0, 0x2DFF}, {0x3099, 0x309A}, {0xA66F, 0xA672},
{0xA674, 0xA67D}, {0xA69E, 0xA69F}, {0xA6F0, 0xA6F1},
{0xA8E0, 0xA8F1}, {0xFE20, 0xFE2F}, {0x101FD, 0x101FD},
{0x10376, 0x1037A}, {0x10EAB, 0x10EAC}, {0x10F46, 0x10F50},
{0x10F82, 0x10F85}, {0x11300, 0x11301}, {0x1133B, 0x1133C},
{0x11366, 0x1136C}, {0x11370, 0x11374}, {0x16AF0, 0x16AF4},
{0x1CF00, 0x1CF2D}, {0x1CF30, 0x1CF46}, {0x1D165, 0x1D169},
{0x1D16D, 0x1D172}, {0x1D17B, 0x1D182}, {0x1D185, 0x1D18B},
{0x1D1AA, 0x1D1AD}, {0x1D242, 0x1D244}, {0x1E000, 0x1E006},
{0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024},
{0x1E026, 0x1E02A}, {0x1E08F, 0x1E08F}, {0x1E8D0, 0x1E8D6},
}
var doublewidth = table{
{0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A},
{0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3},
{0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2648, 0x2653},
{0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1},
{0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5},
{0x26CE, 0x26CE}, {0x26D4, 0x26D4}, {0x26EA, 0x26EA},
{0x26F2, 0x26F3}, {0x26F5, 0x26F5}, {0x26FA, 0x26FA},
{0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B},
{0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E},
{0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797},
{0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C},
{0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x2E99},
{0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x303E},
{0x3041, 0x3096}, {0x3099, 0x30FF}, {0x3105, 0x312F},
{0x3131, 0x318E}, {0x3190, 0x31E3}, {0x31EF, 0x321E},
{0x3220, 0x3247}, {0x3250, 0x4DBF}, {0x4E00, 0xA48C},
{0xA490, 0xA4C6}, {0xA960, 0xA97C}, {0xAC00, 0xD7A3},
{0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52},
{0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60},
{0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE4}, {0x16FF0, 0x16FF1},
{0x17000, 0x187F7}, {0x18800, 0x18CD5}, {0x18D00, 0x18D08},
{0x1AFF0, 0x1AFF3}, {0x1AFF5, 0x1AFFB}, {0x1AFFD, 0x1AFFE},
{0x1B000, 0x1B122}, {0x1B132, 0x1B132}, {0x1B150, 0x1B152},
{0x1B155, 0x1B155}, {0x1B164, 0x1B167}, {0x1B170, 0x1B2FB},
{0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2630, 0x2637},
{0x2648, 0x2653}, {0x267F, 0x267F}, {0x268A, 0x268F},
{0x2693, 0x2693}, {0x26A1, 0x26A1}, {0x26AA, 0x26AB},
{0x26BD, 0x26BE}, {0x26C4, 0x26C5}, {0x26CE, 0x26CE},
{0x26D4, 0x26D4}, {0x26EA, 0x26EA}, {0x26F2, 0x26F3},
{0x26F5, 0x26F5}, {0x26FA, 0x26FA}, {0x26FD, 0x26FD},
{0x2705, 0x2705}, {0x270A, 0x270B}, {0x2728, 0x2728},
{0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755},
{0x2757, 0x2757}, {0x2795, 0x2797}, {0x27B0, 0x27B0},
{0x27BF, 0x27BF}, {0x2B1B, 0x2B1C}, {0x2B50, 0x2B50},
{0x2B55, 0x2B55}, {0x2E80, 0x2E99}, {0x2E9B, 0x2EF3},
{0x2F00, 0x2FD5}, {0x2FF0, 0x303E}, {0x3041, 0x3096},
{0x3099, 0x30FF}, {0x3105, 0x312F}, {0x3131, 0x318E},
{0x3190, 0x31E5}, {0x31EF, 0x321E}, {0x3220, 0x3247},
{0x3250, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C},
{0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19},
{0xFE30, 0xFE52}, {0xFE54, 0xFE66}, {0xFE68, 0xFE6B},
{0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE4},
{0x16FF0, 0x16FF6}, {0x17000, 0x18CD5}, {0x18CFF, 0x18D1E},
{0x18D80, 0x18DF2}, {0x1AFF0, 0x1AFF3}, {0x1AFF5, 0x1AFFB},
{0x1AFFD, 0x1AFFE}, {0x1B000, 0x1B122}, {0x1B132, 0x1B132},
{0x1B150, 0x1B152}, {0x1B155, 0x1B155}, {0x1B164, 0x1B167},
{0x1B170, 0x1B2FB}, {0x1D300, 0x1D356}, {0x1D360, 0x1D376},
{0x1F004, 0x1F004}, {0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E},
{0x1F191, 0x1F19A}, {0x1F200, 0x1F202}, {0x1F210, 0x1F23B},
{0x1F240, 0x1F248}, {0x1F250, 0x1F251}, {0x1F260, 0x1F265},
@@ -56,12 +57,12 @@ var doublewidth = table{
{0x1F54B, 0x1F54E}, {0x1F550, 0x1F567}, {0x1F57A, 0x1F57A},
{0x1F595, 0x1F596}, {0x1F5A4, 0x1F5A4}, {0x1F5FB, 0x1F64F},
{0x1F680, 0x1F6C5}, {0x1F6CC, 0x1F6CC}, {0x1F6D0, 0x1F6D2},
{0x1F6D5, 0x1F6D7}, {0x1F6DC, 0x1F6DF}, {0x1F6EB, 0x1F6EC},
{0x1F6D5, 0x1F6D8}, {0x1F6DC, 0x1F6DF}, {0x1F6EB, 0x1F6EC},
{0x1F6F4, 0x1F6FC}, {0x1F7E0, 0x1F7EB}, {0x1F7F0, 0x1F7F0},
{0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1F9FF},
{0x1FA70, 0x1FA7C}, {0x1FA80, 0x1FA88}, {0x1FA90, 0x1FABD},
{0x1FABF, 0x1FAC5}, {0x1FACE, 0x1FADB}, {0x1FAE0, 0x1FAE8},
{0x1FAF0, 0x1FAF8}, {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD},
{0x1FA70, 0x1FA7C}, {0x1FA80, 0x1FA8A}, {0x1FA8E, 0x1FAC6},
{0x1FAC8, 0x1FAC8}, {0x1FACD, 0x1FADC}, {0x1FADF, 0x1FAEA},
{0x1FAEF, 0x1FAF8}, {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD},
}
var ambiguous = table{
@@ -159,164 +160,162 @@ var neutral = table{
{0x0600, 0x070D}, {0x070F, 0x074A}, {0x074D, 0x07B1},
{0x07C0, 0x07FA}, {0x07FD, 0x082D}, {0x0830, 0x083E},
{0x0840, 0x085B}, {0x085E, 0x085E}, {0x0860, 0x086A},
{0x0870, 0x088E}, {0x0890, 0x0891}, {0x0898, 0x0983},
{0x0985, 0x098C}, {0x098F, 0x0990}, {0x0993, 0x09A8},
{0x09AA, 0x09B0}, {0x09B2, 0x09B2}, {0x09B6, 0x09B9},
{0x09BC, 0x09C4}, {0x09C7, 0x09C8}, {0x09CB, 0x09CE},
{0x09D7, 0x09D7}, {0x09DC, 0x09DD}, {0x09DF, 0x09E3},
{0x09E6, 0x09FE}, {0x0A01, 0x0A03}, {0x0A05, 0x0A0A},
{0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, {0x0A2A, 0x0A30},
{0x0A32, 0x0A33}, {0x0A35, 0x0A36}, {0x0A38, 0x0A39},
{0x0A3C, 0x0A3C}, {0x0A3E, 0x0A42}, {0x0A47, 0x0A48},
{0x0A4B, 0x0A4D}, {0x0A51, 0x0A51}, {0x0A59, 0x0A5C},
{0x0A5E, 0x0A5E}, {0x0A66, 0x0A76}, {0x0A81, 0x0A83},
{0x0A85, 0x0A8D}, {0x0A8F, 0x0A91}, {0x0A93, 0x0AA8},
{0x0AAA, 0x0AB0}, {0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9},
{0x0ABC, 0x0AC5}, {0x0AC7, 0x0AC9}, {0x0ACB, 0x0ACD},
{0x0AD0, 0x0AD0}, {0x0AE0, 0x0AE3}, {0x0AE6, 0x0AF1},
{0x0AF9, 0x0AFF}, {0x0B01, 0x0B03}, {0x0B05, 0x0B0C},
{0x0B0F, 0x0B10}, {0x0B13, 0x0B28}, {0x0B2A, 0x0B30},
{0x0B32, 0x0B33}, {0x0B35, 0x0B39}, {0x0B3C, 0x0B44},
{0x0B47, 0x0B48}, {0x0B4B, 0x0B4D}, {0x0B55, 0x0B57},
{0x0B5C, 0x0B5D}, {0x0B5F, 0x0B63}, {0x0B66, 0x0B77},
{0x0B82, 0x0B83}, {0x0B85, 0x0B8A}, {0x0B8E, 0x0B90},
{0x0B92, 0x0B95}, {0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C},
{0x0B9E, 0x0B9F}, {0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA},
{0x0BAE, 0x0BB9}, {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8},
{0x0BCA, 0x0BCD}, {0x0BD0, 0x0BD0}, {0x0BD7, 0x0BD7},
{0x0BE6, 0x0BFA}, {0x0C00, 0x0C0C}, {0x0C0E, 0x0C10},
{0x0C12, 0x0C28}, {0x0C2A, 0x0C39}, {0x0C3C, 0x0C44},
{0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56},
{0x0C58, 0x0C5A}, {0x0C5D, 0x0C5D}, {0x0C60, 0x0C63},
{0x0C66, 0x0C6F}, {0x0C77, 0x0C8C}, {0x0C8E, 0x0C90},
{0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9},
{0x0CBC, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD},
{0x0CD5, 0x0CD6}, {0x0CDD, 0x0CDE}, {0x0CE0, 0x0CE3},
{0x0CE6, 0x0CEF}, {0x0CF1, 0x0CF3}, {0x0D00, 0x0D0C},
{0x0D0E, 0x0D10}, {0x0D12, 0x0D44}, {0x0D46, 0x0D48},
{0x0D4A, 0x0D4F}, {0x0D54, 0x0D63}, {0x0D66, 0x0D7F},
{0x0D81, 0x0D83}, {0x0D85, 0x0D96}, {0x0D9A, 0x0DB1},
{0x0DB3, 0x0DBB}, {0x0DBD, 0x0DBD}, {0x0DC0, 0x0DC6},
{0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD4}, {0x0DD6, 0x0DD6},
{0x0DD8, 0x0DDF}, {0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF4},
{0x0E01, 0x0E3A}, {0x0E3F, 0x0E5B}, {0x0E81, 0x0E82},
{0x0E84, 0x0E84}, {0x0E86, 0x0E8A}, {0x0E8C, 0x0EA3},
{0x0EA5, 0x0EA5}, {0x0EA7, 0x0EBD}, {0x0EC0, 0x0EC4},
{0x0EC6, 0x0EC6}, {0x0EC8, 0x0ECE}, {0x0ED0, 0x0ED9},
{0x0EDC, 0x0EDF}, {0x0F00, 0x0F47}, {0x0F49, 0x0F6C},
{0x0F71, 0x0F97}, {0x0F99, 0x0FBC}, {0x0FBE, 0x0FCC},
{0x0FCE, 0x0FDA}, {0x1000, 0x10C5}, {0x10C7, 0x10C7},
{0x10CD, 0x10CD}, {0x10D0, 0x10FF}, {0x1160, 0x1248},
{0x124A, 0x124D}, {0x1250, 0x1256}, {0x1258, 0x1258},
{0x125A, 0x125D}, {0x1260, 0x1288}, {0x128A, 0x128D},
{0x1290, 0x12B0}, {0x12B2, 0x12B5}, {0x12B8, 0x12BE},
{0x12C0, 0x12C0}, {0x12C2, 0x12C5}, {0x12C8, 0x12D6},
{0x12D8, 0x1310}, {0x1312, 0x1315}, {0x1318, 0x135A},
{0x135D, 0x137C}, {0x1380, 0x1399}, {0x13A0, 0x13F5},
{0x13F8, 0x13FD}, {0x1400, 0x169C}, {0x16A0, 0x16F8},
{0x1700, 0x1715}, {0x171F, 0x1736}, {0x1740, 0x1753},
{0x1760, 0x176C}, {0x176E, 0x1770}, {0x1772, 0x1773},
{0x1780, 0x17DD}, {0x17E0, 0x17E9}, {0x17F0, 0x17F9},
{0x1800, 0x1819}, {0x1820, 0x1878}, {0x1880, 0x18AA},
{0x18B0, 0x18F5}, {0x1900, 0x191E}, {0x1920, 0x192B},
{0x1930, 0x193B}, {0x1940, 0x1940}, {0x1944, 0x196D},
{0x1970, 0x1974}, {0x1980, 0x19AB}, {0x19B0, 0x19C9},
{0x19D0, 0x19DA}, {0x19DE, 0x1A1B}, {0x1A1E, 0x1A5E},
{0x1A60, 0x1A7C}, {0x1A7F, 0x1A89}, {0x1A90, 0x1A99},
{0x1AA0, 0x1AAD}, {0x1AB0, 0x1ACE}, {0x1B00, 0x1B4C},
{0x1B50, 0x1B7E}, {0x1B80, 0x1BF3}, {0x1BFC, 0x1C37},
{0x1C3B, 0x1C49}, {0x1C4D, 0x1C88}, {0x1C90, 0x1CBA},
{0x1CBD, 0x1CC7}, {0x1CD0, 0x1CFA}, {0x1D00, 0x1F15},
{0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D},
{0x1F50, 0x1F57}, {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B},
{0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4},
{0x1FB6, 0x1FC4}, {0x1FC6, 0x1FD3}, {0x1FD6, 0x1FDB},
{0x1FDD, 0x1FEF}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFE},
{0x2000, 0x200F}, {0x2011, 0x2012}, {0x2017, 0x2017},
{0x201A, 0x201B}, {0x201E, 0x201F}, {0x2023, 0x2023},
{0x2028, 0x202F}, {0x2031, 0x2031}, {0x2034, 0x2034},
{0x2036, 0x203A}, {0x203C, 0x203D}, {0x203F, 0x2064},
{0x2066, 0x2071}, {0x2075, 0x207E}, {0x2080, 0x2080},
{0x2085, 0x208E}, {0x2090, 0x209C}, {0x20A0, 0x20A8},
{0x20AA, 0x20AB}, {0x20AD, 0x20C0}, {0x20D0, 0x20F0},
{0x2100, 0x2102}, {0x2104, 0x2104}, {0x2106, 0x2108},
{0x210A, 0x2112}, {0x2114, 0x2115}, {0x2117, 0x2120},
{0x2123, 0x2125}, {0x2127, 0x212A}, {0x212C, 0x2152},
{0x2155, 0x215A}, {0x215F, 0x215F}, {0x216C, 0x216F},
{0x217A, 0x2188}, {0x218A, 0x218B}, {0x219A, 0x21B7},
{0x21BA, 0x21D1}, {0x21D3, 0x21D3}, {0x21D5, 0x21E6},
{0x21E8, 0x21FF}, {0x2201, 0x2201}, {0x2204, 0x2206},
{0x2209, 0x220A}, {0x220C, 0x220E}, {0x2210, 0x2210},
{0x2212, 0x2214}, {0x2216, 0x2219}, {0x221B, 0x221C},
{0x2221, 0x2222}, {0x2224, 0x2224}, {0x2226, 0x2226},
{0x222D, 0x222D}, {0x222F, 0x2233}, {0x2238, 0x223B},
{0x223E, 0x2247}, {0x2249, 0x224B}, {0x224D, 0x2251},
{0x2253, 0x225F}, {0x2262, 0x2263}, {0x2268, 0x2269},
{0x226C, 0x226D}, {0x2270, 0x2281}, {0x2284, 0x2285},
{0x2288, 0x2294}, {0x2296, 0x2298}, {0x229A, 0x22A4},
{0x22A6, 0x22BE}, {0x22C0, 0x2311}, {0x2313, 0x2319},
{0x231C, 0x2328}, {0x232B, 0x23E8}, {0x23ED, 0x23EF},
{0x23F1, 0x23F2}, {0x23F4, 0x2426}, {0x2440, 0x244A},
{0x24EA, 0x24EA}, {0x254C, 0x254F}, {0x2574, 0x257F},
{0x2590, 0x2591}, {0x2596, 0x259F}, {0x25A2, 0x25A2},
{0x25AA, 0x25B1}, {0x25B4, 0x25B5}, {0x25B8, 0x25BB},
{0x25BE, 0x25BF}, {0x25C2, 0x25C5}, {0x25C9, 0x25CA},
{0x25CC, 0x25CD}, {0x25D2, 0x25E1}, {0x25E6, 0x25EE},
{0x25F0, 0x25FC}, {0x25FF, 0x2604}, {0x2607, 0x2608},
{0x260A, 0x260D}, {0x2610, 0x2613}, {0x2616, 0x261B},
{0x261D, 0x261D}, {0x261F, 0x263F}, {0x2641, 0x2641},
{0x0870, 0x0891}, {0x0897, 0x0983}, {0x0985, 0x098C},
{0x098F, 0x0990}, {0x0993, 0x09A8}, {0x09AA, 0x09B0},
{0x09B2, 0x09B2}, {0x09B6, 0x09B9}, {0x09BC, 0x09C4},
{0x09C7, 0x09C8}, {0x09CB, 0x09CE}, {0x09D7, 0x09D7},
{0x09DC, 0x09DD}, {0x09DF, 0x09E3}, {0x09E6, 0x09FE},
{0x0A01, 0x0A03}, {0x0A05, 0x0A0A}, {0x0A0F, 0x0A10},
{0x0A13, 0x0A28}, {0x0A2A, 0x0A30}, {0x0A32, 0x0A33},
{0x0A35, 0x0A36}, {0x0A38, 0x0A39}, {0x0A3C, 0x0A3C},
{0x0A3E, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D},
{0x0A51, 0x0A51}, {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E},
{0x0A66, 0x0A76}, {0x0A81, 0x0A83}, {0x0A85, 0x0A8D},
{0x0A8F, 0x0A91}, {0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0},
{0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, {0x0ABC, 0x0AC5},
{0x0AC7, 0x0AC9}, {0x0ACB, 0x0ACD}, {0x0AD0, 0x0AD0},
{0x0AE0, 0x0AE3}, {0x0AE6, 0x0AF1}, {0x0AF9, 0x0AFF},
{0x0B01, 0x0B03}, {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10},
{0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, {0x0B32, 0x0B33},
{0x0B35, 0x0B39}, {0x0B3C, 0x0B44}, {0x0B47, 0x0B48},
{0x0B4B, 0x0B4D}, {0x0B55, 0x0B57}, {0x0B5C, 0x0B5D},
{0x0B5F, 0x0B63}, {0x0B66, 0x0B77}, {0x0B82, 0x0B83},
{0x0B85, 0x0B8A}, {0x0B8E, 0x0B90}, {0x0B92, 0x0B95},
{0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F},
{0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB9},
{0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD},
{0x0BD0, 0x0BD0}, {0x0BD7, 0x0BD7}, {0x0BE6, 0x0BFA},
{0x0C00, 0x0C0C}, {0x0C0E, 0x0C10}, {0x0C12, 0x0C28},
{0x0C2A, 0x0C39}, {0x0C3C, 0x0C44}, {0x0C46, 0x0C48},
{0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, {0x0C58, 0x0C5A},
{0x0C5C, 0x0C5D}, {0x0C60, 0x0C63}, {0x0C66, 0x0C6F},
{0x0C77, 0x0C8C}, {0x0C8E, 0x0C90}, {0x0C92, 0x0CA8},
{0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, {0x0CBC, 0x0CC4},
{0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD}, {0x0CD5, 0x0CD6},
{0x0CDC, 0x0CDE}, {0x0CE0, 0x0CE3}, {0x0CE6, 0x0CEF},
{0x0CF1, 0x0CF3}, {0x0D00, 0x0D0C}, {0x0D0E, 0x0D10},
{0x0D12, 0x0D44}, {0x0D46, 0x0D48}, {0x0D4A, 0x0D4F},
{0x0D54, 0x0D63}, {0x0D66, 0x0D7F}, {0x0D81, 0x0D83},
{0x0D85, 0x0D96}, {0x0D9A, 0x0DB1}, {0x0DB3, 0x0DBB},
{0x0DBD, 0x0DBD}, {0x0DC0, 0x0DC6}, {0x0DCA, 0x0DCA},
{0x0DCF, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF},
{0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF4}, {0x0E01, 0x0E3A},
{0x0E3F, 0x0E5B}, {0x0E81, 0x0E82}, {0x0E84, 0x0E84},
{0x0E86, 0x0E8A}, {0x0E8C, 0x0EA3}, {0x0EA5, 0x0EA5},
{0x0EA7, 0x0EBD}, {0x0EC0, 0x0EC4}, {0x0EC6, 0x0EC6},
{0x0EC8, 0x0ECE}, {0x0ED0, 0x0ED9}, {0x0EDC, 0x0EDF},
{0x0F00, 0x0F47}, {0x0F49, 0x0F6C}, {0x0F71, 0x0F97},
{0x0F99, 0x0FBC}, {0x0FBE, 0x0FCC}, {0x0FCE, 0x0FDA},
{0x1000, 0x10C5}, {0x10C7, 0x10C7}, {0x10CD, 0x10CD},
{0x10D0, 0x10FF}, {0x1160, 0x1248}, {0x124A, 0x124D},
{0x1250, 0x1256}, {0x1258, 0x1258}, {0x125A, 0x125D},
{0x1260, 0x1288}, {0x128A, 0x128D}, {0x1290, 0x12B0},
{0x12B2, 0x12B5}, {0x12B8, 0x12BE}, {0x12C0, 0x12C0},
{0x12C2, 0x12C5}, {0x12C8, 0x12D6}, {0x12D8, 0x1310},
{0x1312, 0x1315}, {0x1318, 0x135A}, {0x135D, 0x137C},
{0x1380, 0x1399}, {0x13A0, 0x13F5}, {0x13F8, 0x13FD},
{0x1400, 0x169C}, {0x16A0, 0x16F8}, {0x1700, 0x1715},
{0x171F, 0x1736}, {0x1740, 0x1753}, {0x1760, 0x176C},
{0x176E, 0x1770}, {0x1772, 0x1773}, {0x1780, 0x17DD},
{0x17E0, 0x17E9}, {0x17F0, 0x17F9}, {0x1800, 0x1819},
{0x1820, 0x1878}, {0x1880, 0x18AA}, {0x18B0, 0x18F5},
{0x1900, 0x191E}, {0x1920, 0x192B}, {0x1930, 0x193B},
{0x1940, 0x1940}, {0x1944, 0x196D}, {0x1970, 0x1974},
{0x1980, 0x19AB}, {0x19B0, 0x19C9}, {0x19D0, 0x19DA},
{0x19DE, 0x1A1B}, {0x1A1E, 0x1A5E}, {0x1A60, 0x1A7C},
{0x1A7F, 0x1A89}, {0x1A90, 0x1A99}, {0x1AA0, 0x1AAD},
{0x1AB0, 0x1ADD}, {0x1AE0, 0x1AEB}, {0x1B00, 0x1B4C},
{0x1B4E, 0x1BF3}, {0x1BFC, 0x1C37}, {0x1C3B, 0x1C49},
{0x1C4D, 0x1C8A}, {0x1C90, 0x1CBA}, {0x1CBD, 0x1CC7},
{0x1CD0, 0x1CFA}, {0x1D00, 0x1F15}, {0x1F18, 0x1F1D},
{0x1F20, 0x1F45}, {0x1F48, 0x1F4D}, {0x1F50, 0x1F57},
{0x1F59, 0x1F59}, {0x1F5B, 0x1F5B}, {0x1F5D, 0x1F5D},
{0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, {0x1FB6, 0x1FC4},
{0x1FC6, 0x1FD3}, {0x1FD6, 0x1FDB}, {0x1FDD, 0x1FEF},
{0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFE}, {0x2000, 0x200F},
{0x2011, 0x2012}, {0x2017, 0x2017}, {0x201A, 0x201B},
{0x201E, 0x201F}, {0x2023, 0x2023}, {0x2028, 0x202F},
{0x2031, 0x2031}, {0x2034, 0x2034}, {0x2036, 0x203A},
{0x203C, 0x203D}, {0x203F, 0x2064}, {0x2066, 0x2071},
{0x2075, 0x207E}, {0x2080, 0x2080}, {0x2085, 0x208E},
{0x2090, 0x209C}, {0x20A0, 0x20A8}, {0x20AA, 0x20AB},
{0x20AD, 0x20C1}, {0x20D0, 0x20F0}, {0x2100, 0x2102},
{0x2104, 0x2104}, {0x2106, 0x2108}, {0x210A, 0x2112},
{0x2114, 0x2115}, {0x2117, 0x2120}, {0x2123, 0x2125},
{0x2127, 0x212A}, {0x212C, 0x2152}, {0x2155, 0x215A},
{0x215F, 0x215F}, {0x216C, 0x216F}, {0x217A, 0x2188},
{0x218A, 0x218B}, {0x219A, 0x21B7}, {0x21BA, 0x21D1},
{0x21D3, 0x21D3}, {0x21D5, 0x21E6}, {0x21E8, 0x21FF},
{0x2201, 0x2201}, {0x2204, 0x2206}, {0x2209, 0x220A},
{0x220C, 0x220E}, {0x2210, 0x2210}, {0x2212, 0x2214},
{0x2216, 0x2219}, {0x221B, 0x221C}, {0x2221, 0x2222},
{0x2224, 0x2224}, {0x2226, 0x2226}, {0x222D, 0x222D},
{0x222F, 0x2233}, {0x2238, 0x223B}, {0x223E, 0x2247},
{0x2249, 0x224B}, {0x224D, 0x2251}, {0x2253, 0x225F},
{0x2262, 0x2263}, {0x2268, 0x2269}, {0x226C, 0x226D},
{0x2270, 0x2281}, {0x2284, 0x2285}, {0x2288, 0x2294},
{0x2296, 0x2298}, {0x229A, 0x22A4}, {0x22A6, 0x22BE},
{0x22C0, 0x2311}, {0x2313, 0x2319}, {0x231C, 0x2328},
{0x232B, 0x23E8}, {0x23ED, 0x23EF}, {0x23F1, 0x23F2},
{0x23F4, 0x2429}, {0x2440, 0x244A}, {0x24EA, 0x24EA},
{0x254C, 0x254F}, {0x2574, 0x257F}, {0x2590, 0x2591},
{0x2596, 0x259F}, {0x25A2, 0x25A2}, {0x25AA, 0x25B1},
{0x25B4, 0x25B5}, {0x25B8, 0x25BB}, {0x25BE, 0x25BF},
{0x25C2, 0x25C5}, {0x25C9, 0x25CA}, {0x25CC, 0x25CD},
{0x25D2, 0x25E1}, {0x25E6, 0x25EE}, {0x25F0, 0x25FC},
{0x25FF, 0x2604}, {0x2607, 0x2608}, {0x260A, 0x260D},
{0x2610, 0x2613}, {0x2616, 0x261B}, {0x261D, 0x261D},
{0x261F, 0x262F}, {0x2638, 0x263F}, {0x2641, 0x2641},
{0x2643, 0x2647}, {0x2654, 0x265F}, {0x2662, 0x2662},
{0x2666, 0x2666}, {0x266B, 0x266B}, {0x266E, 0x266E},
{0x2670, 0x267E}, {0x2680, 0x2692}, {0x2694, 0x269D},
{0x26A0, 0x26A0}, {0x26A2, 0x26A9}, {0x26AC, 0x26BC},
{0x26C0, 0x26C3}, {0x26E2, 0x26E2}, {0x26E4, 0x26E7},
{0x2700, 0x2704}, {0x2706, 0x2709}, {0x270C, 0x2727},
{0x2729, 0x273C}, {0x273E, 0x274B}, {0x274D, 0x274D},
{0x274F, 0x2752}, {0x2756, 0x2756}, {0x2758, 0x2775},
{0x2780, 0x2794}, {0x2798, 0x27AF}, {0x27B1, 0x27BE},
{0x27C0, 0x27E5}, {0x27EE, 0x2984}, {0x2987, 0x2B1A},
{0x2B1D, 0x2B4F}, {0x2B51, 0x2B54}, {0x2B5A, 0x2B73},
{0x2B76, 0x2B95}, {0x2B97, 0x2CF3}, {0x2CF9, 0x2D25},
{0x2670, 0x267E}, {0x2680, 0x2689}, {0x2690, 0x2692},
{0x2694, 0x269D}, {0x26A0, 0x26A0}, {0x26A2, 0x26A9},
{0x26AC, 0x26BC}, {0x26C0, 0x26C3}, {0x26E2, 0x26E2},
{0x26E4, 0x26E7}, {0x2700, 0x2704}, {0x2706, 0x2709},
{0x270C, 0x2727}, {0x2729, 0x273C}, {0x273E, 0x274B},
{0x274D, 0x274D}, {0x274F, 0x2752}, {0x2756, 0x2756},
{0x2758, 0x2775}, {0x2780, 0x2794}, {0x2798, 0x27AF},
{0x27B1, 0x27BE}, {0x27C0, 0x27E5}, {0x27EE, 0x2984},
{0x2987, 0x2B1A}, {0x2B1D, 0x2B4F}, {0x2B51, 0x2B54},
{0x2B5A, 0x2B73}, {0x2B76, 0x2CF3}, {0x2CF9, 0x2D25},
{0x2D27, 0x2D27}, {0x2D2D, 0x2D2D}, {0x2D30, 0x2D67},
{0x2D6F, 0x2D70}, {0x2D7F, 0x2D96}, {0x2DA0, 0x2DA6},
{0x2DA8, 0x2DAE}, {0x2DB0, 0x2DB6}, {0x2DB8, 0x2DBE},
{0x2DC0, 0x2DC6}, {0x2DC8, 0x2DCE}, {0x2DD0, 0x2DD6},
{0x2DD8, 0x2DDE}, {0x2DE0, 0x2E5D}, {0x303F, 0x303F},
{0x4DC0, 0x4DFF}, {0xA4D0, 0xA62B}, {0xA640, 0xA6F7},
{0xA700, 0xA7CA}, {0xA7D0, 0xA7D1}, {0xA7D3, 0xA7D3},
{0xA7D5, 0xA7D9}, {0xA7F2, 0xA82C}, {0xA830, 0xA839},
{0xA840, 0xA877}, {0xA880, 0xA8C5}, {0xA8CE, 0xA8D9},
{0xA8E0, 0xA953}, {0xA95F, 0xA95F}, {0xA980, 0xA9CD},
{0xA9CF, 0xA9D9}, {0xA9DE, 0xA9FE}, {0xAA00, 0xAA36},
{0xAA40, 0xAA4D}, {0xAA50, 0xAA59}, {0xAA5C, 0xAAC2},
{0xAADB, 0xAAF6}, {0xAB01, 0xAB06}, {0xAB09, 0xAB0E},
{0xAB11, 0xAB16}, {0xAB20, 0xAB26}, {0xAB28, 0xAB2E},
{0xAB30, 0xAB6B}, {0xAB70, 0xABED}, {0xABF0, 0xABF9},
{0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, {0xD800, 0xDFFF},
{0xFB00, 0xFB06}, {0xFB13, 0xFB17}, {0xFB1D, 0xFB36},
{0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, {0xFB40, 0xFB41},
{0xFB43, 0xFB44}, {0xFB46, 0xFBC2}, {0xFBD3, 0xFD8F},
{0xFD92, 0xFDC7}, {0xFDCF, 0xFDCF}, {0xFDF0, 0xFDFF},
{0xFE20, 0xFE2F}, {0xFE70, 0xFE74}, {0xFE76, 0xFEFC},
{0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFC}, {0x10000, 0x1000B},
{0x1000D, 0x10026}, {0x10028, 0x1003A}, {0x1003C, 0x1003D},
{0x1003F, 0x1004D}, {0x10050, 0x1005D}, {0x10080, 0x100FA},
{0x10100, 0x10102}, {0x10107, 0x10133}, {0x10137, 0x1018E},
{0x10190, 0x1019C}, {0x101A0, 0x101A0}, {0x101D0, 0x101FD},
{0x10280, 0x1029C}, {0x102A0, 0x102D0}, {0x102E0, 0x102FB},
{0x10300, 0x10323}, {0x1032D, 0x1034A}, {0x10350, 0x1037A},
{0x10380, 0x1039D}, {0x1039F, 0x103C3}, {0x103C8, 0x103D5},
{0x10400, 0x1049D}, {0x104A0, 0x104A9}, {0x104B0, 0x104D3},
{0x104D8, 0x104FB}, {0x10500, 0x10527}, {0x10530, 0x10563},
{0x1056F, 0x1057A}, {0x1057C, 0x1058A}, {0x1058C, 0x10592},
{0x10594, 0x10595}, {0x10597, 0x105A1}, {0x105A3, 0x105B1},
{0x105B3, 0x105B9}, {0x105BB, 0x105BC}, {0x10600, 0x10736},
{0xA4D0, 0xA62B}, {0xA640, 0xA6F7}, {0xA700, 0xA7DC},
{0xA7F1, 0xA82C}, {0xA830, 0xA839}, {0xA840, 0xA877},
{0xA880, 0xA8C5}, {0xA8CE, 0xA8D9}, {0xA8E0, 0xA953},
{0xA95F, 0xA95F}, {0xA980, 0xA9CD}, {0xA9CF, 0xA9D9},
{0xA9DE, 0xA9FE}, {0xAA00, 0xAA36}, {0xAA40, 0xAA4D},
{0xAA50, 0xAA59}, {0xAA5C, 0xAAC2}, {0xAADB, 0xAAF6},
{0xAB01, 0xAB06}, {0xAB09, 0xAB0E}, {0xAB11, 0xAB16},
{0xAB20, 0xAB26}, {0xAB28, 0xAB2E}, {0xAB30, 0xAB6B},
{0xAB70, 0xABED}, {0xABF0, 0xABF9}, {0xD7B0, 0xD7C6},
{0xD7CB, 0xD7FB}, {0xD800, 0xDFFF}, {0xFB00, 0xFB06},
{0xFB13, 0xFB17}, {0xFB1D, 0xFB36}, {0xFB38, 0xFB3C},
{0xFB3E, 0xFB3E}, {0xFB40, 0xFB41}, {0xFB43, 0xFB44},
{0xFB46, 0xFDCF}, {0xFDF0, 0xFDFF}, {0xFE20, 0xFE2F},
{0xFE70, 0xFE74}, {0xFE76, 0xFEFC}, {0xFEFF, 0xFEFF},
{0xFFF9, 0xFFFC}, {0x10000, 0x1000B}, {0x1000D, 0x10026},
{0x10028, 0x1003A}, {0x1003C, 0x1003D}, {0x1003F, 0x1004D},
{0x10050, 0x1005D}, {0x10080, 0x100FA}, {0x10100, 0x10102},
{0x10107, 0x10133}, {0x10137, 0x1018E}, {0x10190, 0x1019C},
{0x101A0, 0x101A0}, {0x101D0, 0x101FD}, {0x10280, 0x1029C},
{0x102A0, 0x102D0}, {0x102E0, 0x102FB}, {0x10300, 0x10323},
{0x1032D, 0x1034A}, {0x10350, 0x1037A}, {0x10380, 0x1039D},
{0x1039F, 0x103C3}, {0x103C8, 0x103D5}, {0x10400, 0x1049D},
{0x104A0, 0x104A9}, {0x104B0, 0x104D3}, {0x104D8, 0x104FB},
{0x10500, 0x10527}, {0x10530, 0x10563}, {0x1056F, 0x1057A},
{0x1057C, 0x1058A}, {0x1058C, 0x10592}, {0x10594, 0x10595},
{0x10597, 0x105A1}, {0x105A3, 0x105B1}, {0x105B3, 0x105B9},
{0x105BB, 0x105BC}, {0x105C0, 0x105F3}, {0x10600, 0x10736},
{0x10740, 0x10755}, {0x10760, 0x10767}, {0x10780, 0x10785},
{0x10787, 0x107B0}, {0x107B2, 0x107BA}, {0x10800, 0x10805},
{0x10808, 0x10808}, {0x1080A, 0x10835}, {0x10837, 0x10838},
{0x1083C, 0x1083C}, {0x1083F, 0x10855}, {0x10857, 0x1089E},
{0x108A7, 0x108AF}, {0x108E0, 0x108F2}, {0x108F4, 0x108F5},
{0x108FB, 0x1091B}, {0x1091F, 0x10939}, {0x1093F, 0x1093F},
{0x108FB, 0x1091B}, {0x1091F, 0x10939}, {0x1093F, 0x10959},
{0x10980, 0x109B7}, {0x109BC, 0x109CF}, {0x109D2, 0x10A03},
{0x10A05, 0x10A06}, {0x10A0C, 0x10A13}, {0x10A15, 0x10A17},
{0x10A19, 0x10A35}, {0x10A38, 0x10A3A}, {0x10A3F, 0x10A48},
@@ -325,53 +324,63 @@ var neutral = table{
{0x10B58, 0x10B72}, {0x10B78, 0x10B91}, {0x10B99, 0x10B9C},
{0x10BA9, 0x10BAF}, {0x10C00, 0x10C48}, {0x10C80, 0x10CB2},
{0x10CC0, 0x10CF2}, {0x10CFA, 0x10D27}, {0x10D30, 0x10D39},
{0x10D40, 0x10D65}, {0x10D69, 0x10D85}, {0x10D8E, 0x10D8F},
{0x10E60, 0x10E7E}, {0x10E80, 0x10EA9}, {0x10EAB, 0x10EAD},
{0x10EB0, 0x10EB1}, {0x10EFD, 0x10F27}, {0x10F30, 0x10F59},
{0x10F70, 0x10F89}, {0x10FB0, 0x10FCB}, {0x10FE0, 0x10FF6},
{0x11000, 0x1104D}, {0x11052, 0x11075}, {0x1107F, 0x110C2},
{0x110CD, 0x110CD}, {0x110D0, 0x110E8}, {0x110F0, 0x110F9},
{0x11100, 0x11134}, {0x11136, 0x11147}, {0x11150, 0x11176},
{0x11180, 0x111DF}, {0x111E1, 0x111F4}, {0x11200, 0x11211},
{0x11213, 0x11241}, {0x11280, 0x11286}, {0x11288, 0x11288},
{0x1128A, 0x1128D}, {0x1128F, 0x1129D}, {0x1129F, 0x112A9},
{0x112B0, 0x112EA}, {0x112F0, 0x112F9}, {0x11300, 0x11303},
{0x11305, 0x1130C}, {0x1130F, 0x11310}, {0x11313, 0x11328},
{0x1132A, 0x11330}, {0x11332, 0x11333}, {0x11335, 0x11339},
{0x1133B, 0x11344}, {0x11347, 0x11348}, {0x1134B, 0x1134D},
{0x11350, 0x11350}, {0x11357, 0x11357}, {0x1135D, 0x11363},
{0x11366, 0x1136C}, {0x11370, 0x11374}, {0x11400, 0x1145B},
{0x1145D, 0x11461}, {0x11480, 0x114C7}, {0x114D0, 0x114D9},
{0x11580, 0x115B5}, {0x115B8, 0x115DD}, {0x11600, 0x11644},
{0x11650, 0x11659}, {0x11660, 0x1166C}, {0x11680, 0x116B9},
{0x116C0, 0x116C9}, {0x11700, 0x1171A}, {0x1171D, 0x1172B},
{0x11730, 0x11746}, {0x11800, 0x1183B}, {0x118A0, 0x118F2},
{0x118FF, 0x11906}, {0x11909, 0x11909}, {0x1190C, 0x11913},
{0x11915, 0x11916}, {0x11918, 0x11935}, {0x11937, 0x11938},
{0x1193B, 0x11946}, {0x11950, 0x11959}, {0x119A0, 0x119A7},
{0x119AA, 0x119D7}, {0x119DA, 0x119E4}, {0x11A00, 0x11A47},
{0x11A50, 0x11AA2}, {0x11AB0, 0x11AF8}, {0x11B00, 0x11B09},
{0x11C00, 0x11C08}, {0x11C0A, 0x11C36}, {0x11C38, 0x11C45},
{0x11C50, 0x11C6C}, {0x11C70, 0x11C8F}, {0x11C92, 0x11CA7},
{0x11CA9, 0x11CB6}, {0x11D00, 0x11D06}, {0x11D08, 0x11D09},
{0x11D0B, 0x11D36}, {0x11D3A, 0x11D3A}, {0x11D3C, 0x11D3D},
{0x11D3F, 0x11D47}, {0x11D50, 0x11D59}, {0x11D60, 0x11D65},
{0x11D67, 0x11D68}, {0x11D6A, 0x11D8E}, {0x11D90, 0x11D91},
{0x11D93, 0x11D98}, {0x11DA0, 0x11DA9}, {0x11EE0, 0x11EF8},
{0x11F00, 0x11F10}, {0x11F12, 0x11F3A}, {0x11F3E, 0x11F59},
{0x11FB0, 0x11FB0}, {0x11FC0, 0x11FF1}, {0x11FFF, 0x12399},
{0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543},
{0x12F90, 0x12FF2}, {0x13000, 0x13455}, {0x14400, 0x14646},
{0x10EB0, 0x10EB1}, {0x10EC2, 0x10EC7}, {0x10ED0, 0x10ED8},
{0x10EFA, 0x10F27}, {0x10F30, 0x10F59}, {0x10F70, 0x10F89},
{0x10FB0, 0x10FCB}, {0x10FE0, 0x10FF6}, {0x11000, 0x1104D},
{0x11052, 0x11075}, {0x1107F, 0x110C2}, {0x110CD, 0x110CD},
{0x110D0, 0x110E8}, {0x110F0, 0x110F9}, {0x11100, 0x11134},
{0x11136, 0x11147}, {0x11150, 0x11176}, {0x11180, 0x111DF},
{0x111E1, 0x111F4}, {0x11200, 0x11211}, {0x11213, 0x11241},
{0x11280, 0x11286}, {0x11288, 0x11288}, {0x1128A, 0x1128D},
{0x1128F, 0x1129D}, {0x1129F, 0x112A9}, {0x112B0, 0x112EA},
{0x112F0, 0x112F9}, {0x11300, 0x11303}, {0x11305, 0x1130C},
{0x1130F, 0x11310}, {0x11313, 0x11328}, {0x1132A, 0x11330},
{0x11332, 0x11333}, {0x11335, 0x11339}, {0x1133B, 0x11344},
{0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11350, 0x11350},
{0x11357, 0x11357}, {0x1135D, 0x11363}, {0x11366, 0x1136C},
{0x11370, 0x11374}, {0x11380, 0x11389}, {0x1138B, 0x1138B},
{0x1138E, 0x1138E}, {0x11390, 0x113B5}, {0x113B7, 0x113C0},
{0x113C2, 0x113C2}, {0x113C5, 0x113C5}, {0x113C7, 0x113CA},
{0x113CC, 0x113D5}, {0x113D7, 0x113D8}, {0x113E1, 0x113E2},
{0x11400, 0x1145B}, {0x1145D, 0x11461}, {0x11480, 0x114C7},
{0x114D0, 0x114D9}, {0x11580, 0x115B5}, {0x115B8, 0x115DD},
{0x11600, 0x11644}, {0x11650, 0x11659}, {0x11660, 0x1166C},
{0x11680, 0x116B9}, {0x116C0, 0x116C9}, {0x116D0, 0x116E3},
{0x11700, 0x1171A}, {0x1171D, 0x1172B}, {0x11730, 0x11746},
{0x11800, 0x1183B}, {0x118A0, 0x118F2}, {0x118FF, 0x11906},
{0x11909, 0x11909}, {0x1190C, 0x11913}, {0x11915, 0x11916},
{0x11918, 0x11935}, {0x11937, 0x11938}, {0x1193B, 0x11946},
{0x11950, 0x11959}, {0x119A0, 0x119A7}, {0x119AA, 0x119D7},
{0x119DA, 0x119E4}, {0x11A00, 0x11A47}, {0x11A50, 0x11AA2},
{0x11AB0, 0x11AF8}, {0x11B00, 0x11B09}, {0x11B60, 0x11B67},
{0x11BC0, 0x11BE1}, {0x11BF0, 0x11BF9}, {0x11C00, 0x11C08},
{0x11C0A, 0x11C36}, {0x11C38, 0x11C45}, {0x11C50, 0x11C6C},
{0x11C70, 0x11C8F}, {0x11C92, 0x11CA7}, {0x11CA9, 0x11CB6},
{0x11D00, 0x11D06}, {0x11D08, 0x11D09}, {0x11D0B, 0x11D36},
{0x11D3A, 0x11D3A}, {0x11D3C, 0x11D3D}, {0x11D3F, 0x11D47},
{0x11D50, 0x11D59}, {0x11D60, 0x11D65}, {0x11D67, 0x11D68},
{0x11D6A, 0x11D8E}, {0x11D90, 0x11D91}, {0x11D93, 0x11D98},
{0x11DA0, 0x11DA9}, {0x11DB0, 0x11DDB}, {0x11DE0, 0x11DE9},
{0x11EE0, 0x11EF8}, {0x11F00, 0x11F10}, {0x11F12, 0x11F3A},
{0x11F3E, 0x11F5A}, {0x11FB0, 0x11FB0}, {0x11FC0, 0x11FF1},
{0x11FFF, 0x12399}, {0x12400, 0x1246E}, {0x12470, 0x12474},
{0x12480, 0x12543}, {0x12F90, 0x12FF2}, {0x13000, 0x13455},
{0x13460, 0x143FA}, {0x14400, 0x14646}, {0x16100, 0x16139},
{0x16800, 0x16A38}, {0x16A40, 0x16A5E}, {0x16A60, 0x16A69},
{0x16A6E, 0x16ABE}, {0x16AC0, 0x16AC9}, {0x16AD0, 0x16AED},
{0x16AF0, 0x16AF5}, {0x16B00, 0x16B45}, {0x16B50, 0x16B59},
{0x16B5B, 0x16B61}, {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F},
{0x16E40, 0x16E9A}, {0x16F00, 0x16F4A}, {0x16F4F, 0x16F87},
{0x16D40, 0x16D79}, {0x16E40, 0x16E9A}, {0x16EA0, 0x16EB8},
{0x16EBB, 0x16ED3}, {0x16F00, 0x16F4A}, {0x16F4F, 0x16F87},
{0x16F8F, 0x16F9F}, {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C},
{0x1BC80, 0x1BC88}, {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BCA3},
{0x1CF00, 0x1CF2D}, {0x1CF30, 0x1CF46}, {0x1CF50, 0x1CFC3},
{0x1D000, 0x1D0F5}, {0x1D100, 0x1D126}, {0x1D129, 0x1D1EA},
{0x1D200, 0x1D245}, {0x1D2C0, 0x1D2D3}, {0x1D2E0, 0x1D2F3},
{0x1D300, 0x1D356}, {0x1D360, 0x1D378}, {0x1D400, 0x1D454},
{0x1CC00, 0x1CCFC}, {0x1CD00, 0x1CEB3}, {0x1CEBA, 0x1CED0},
{0x1CEE0, 0x1CEF0}, {0x1CF00, 0x1CF2D}, {0x1CF30, 0x1CF46},
{0x1CF50, 0x1CFC3}, {0x1D000, 0x1D0F5}, {0x1D100, 0x1D126},
{0x1D129, 0x1D1EA}, {0x1D200, 0x1D245}, {0x1D2C0, 0x1D2D3},
{0x1D2E0, 0x1D2F3}, {0x1D377, 0x1D378}, {0x1D400, 0x1D454},
{0x1D456, 0x1D49C}, {0x1D49E, 0x1D49F}, {0x1D4A2, 0x1D4A2},
{0x1D4A5, 0x1D4A6}, {0x1D4A9, 0x1D4AC}, {0x1D4AE, 0x1D4B9},
{0x1D4BB, 0x1D4BB}, {0x1D4BD, 0x1D4C3}, {0x1D4C5, 0x1D505},
@@ -385,66 +394,94 @@ var neutral = table{
{0x1E08F, 0x1E08F}, {0x1E100, 0x1E12C}, {0x1E130, 0x1E13D},
{0x1E140, 0x1E149}, {0x1E14E, 0x1E14F}, {0x1E290, 0x1E2AE},
{0x1E2C0, 0x1E2F9}, {0x1E2FF, 0x1E2FF}, {0x1E4D0, 0x1E4F9},
{0x1E7E0, 0x1E7E6}, {0x1E7E8, 0x1E7EB}, {0x1E7ED, 0x1E7EE},
{0x1E7F0, 0x1E7FE}, {0x1E800, 0x1E8C4}, {0x1E8C7, 0x1E8D6},
{0x1E900, 0x1E94B}, {0x1E950, 0x1E959}, {0x1E95E, 0x1E95F},
{0x1EC71, 0x1ECB4}, {0x1ED01, 0x1ED3D}, {0x1EE00, 0x1EE03},
{0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22}, {0x1EE24, 0x1EE24},
{0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32}, {0x1EE34, 0x1EE37},
{0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B}, {0x1EE42, 0x1EE42},
{0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49}, {0x1EE4B, 0x1EE4B},
{0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52}, {0x1EE54, 0x1EE54},
{0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59}, {0x1EE5B, 0x1EE5B},
{0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F}, {0x1EE61, 0x1EE62},
{0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72},
{0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, {0x1EE7E, 0x1EE7E},
{0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, {0x1EEA1, 0x1EEA3},
{0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, {0x1EEF0, 0x1EEF1},
{0x1F000, 0x1F003}, {0x1F005, 0x1F02B}, {0x1F030, 0x1F093},
{0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, {0x1F0C1, 0x1F0CE},
{0x1F0D1, 0x1F0F5}, {0x1F10B, 0x1F10F}, {0x1F12E, 0x1F12F},
{0x1F16A, 0x1F16F}, {0x1F1AD, 0x1F1AD}, {0x1F1E6, 0x1F1FF},
{0x1F321, 0x1F32C}, {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D},
{0x1F394, 0x1F39F}, {0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF},
{0x1F3F1, 0x1F3F3}, {0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F},
{0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A},
{0x1F54F, 0x1F54F}, {0x1F568, 0x1F579}, {0x1F57B, 0x1F594},
{0x1F597, 0x1F5A3}, {0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F},
{0x1F6C6, 0x1F6CB}, {0x1F6CD, 0x1F6CF}, {0x1F6D3, 0x1F6D4},
{0x1F6E0, 0x1F6EA}, {0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F776},
{0x1F77B, 0x1F7D9}, {0x1F800, 0x1F80B}, {0x1F810, 0x1F847},
{0x1F850, 0x1F859}, {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD},
{0x1F8B0, 0x1F8B1}, {0x1F900, 0x1F90B}, {0x1F93B, 0x1F93B},
{0x1F946, 0x1F946}, {0x1FA00, 0x1FA53}, {0x1FA60, 0x1FA6D},
{0x1FB00, 0x1FB92}, {0x1FB94, 0x1FBCA}, {0x1FBF0, 0x1FBF9},
{0xE0001, 0xE0001}, {0xE0020, 0xE007F},
{0x1E5D0, 0x1E5FA}, {0x1E5FF, 0x1E5FF}, {0x1E6C0, 0x1E6DE},
{0x1E6E0, 0x1E6F5}, {0x1E6FE, 0x1E6FF}, {0x1E7E0, 0x1E7E6},
{0x1E7E8, 0x1E7EB}, {0x1E7ED, 0x1E7EE}, {0x1E7F0, 0x1E7FE},
{0x1E800, 0x1E8C4}, {0x1E8C7, 0x1E8D6}, {0x1E900, 0x1E94B},
{0x1E950, 0x1E959}, {0x1E95E, 0x1E95F}, {0x1EC71, 0x1ECB4},
{0x1ED01, 0x1ED3D}, {0x1EE00, 0x1EE03}, {0x1EE05, 0x1EE1F},
{0x1EE21, 0x1EE22}, {0x1EE24, 0x1EE24}, {0x1EE27, 0x1EE27},
{0x1EE29, 0x1EE32}, {0x1EE34, 0x1EE37}, {0x1EE39, 0x1EE39},
{0x1EE3B, 0x1EE3B}, {0x1EE42, 0x1EE42}, {0x1EE47, 0x1EE47},
{0x1EE49, 0x1EE49}, {0x1EE4B, 0x1EE4B}, {0x1EE4D, 0x1EE4F},
{0x1EE51, 0x1EE52}, {0x1EE54, 0x1EE54}, {0x1EE57, 0x1EE57},
{0x1EE59, 0x1EE59}, {0x1EE5B, 0x1EE5B}, {0x1EE5D, 0x1EE5D},
{0x1EE5F, 0x1EE5F}, {0x1EE61, 0x1EE62}, {0x1EE64, 0x1EE64},
{0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72}, {0x1EE74, 0x1EE77},
{0x1EE79, 0x1EE7C}, {0x1EE7E, 0x1EE7E}, {0x1EE80, 0x1EE89},
{0x1EE8B, 0x1EE9B}, {0x1EEA1, 0x1EEA3}, {0x1EEA5, 0x1EEA9},
{0x1EEAB, 0x1EEBB}, {0x1EEF0, 0x1EEF1}, {0x1F000, 0x1F003},
{0x1F005, 0x1F02B}, {0x1F030, 0x1F093}, {0x1F0A0, 0x1F0AE},
{0x1F0B1, 0x1F0BF}, {0x1F0C1, 0x1F0CE}, {0x1F0D1, 0x1F0F5},
{0x1F10B, 0x1F10F}, {0x1F12E, 0x1F12F}, {0x1F16A, 0x1F16F},
{0x1F1AD, 0x1F1AD}, {0x1F1E6, 0x1F1FF}, {0x1F321, 0x1F32C},
{0x1F336, 0x1F336}, {0x1F37D, 0x1F37D}, {0x1F394, 0x1F39F},
{0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF}, {0x1F3F1, 0x1F3F3},
{0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F}, {0x1F441, 0x1F441},
{0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A}, {0x1F54F, 0x1F54F},
{0x1F568, 0x1F579}, {0x1F57B, 0x1F594}, {0x1F597, 0x1F5A3},
{0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F}, {0x1F6C6, 0x1F6CB},
{0x1F6CD, 0x1F6CF}, {0x1F6D3, 0x1F6D4}, {0x1F6E0, 0x1F6EA},
{0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F7D9}, {0x1F800, 0x1F80B},
{0x1F810, 0x1F847}, {0x1F850, 0x1F859}, {0x1F860, 0x1F887},
{0x1F890, 0x1F8AD}, {0x1F8B0, 0x1F8BB}, {0x1F8C0, 0x1F8C1},
{0x1F8D0, 0x1F8D8}, {0x1F900, 0x1F90B}, {0x1F93B, 0x1F93B},
{0x1F946, 0x1F946}, {0x1FA00, 0x1FA57}, {0x1FA60, 0x1FA6D},
{0x1FB00, 0x1FB92}, {0x1FB94, 0x1FBFA}, {0xE0001, 0xE0001},
{0xE0020, 0xE007F},
}
var emoji = table{
{0x203C, 0x203C}, {0x2049, 0x2049}, {0x2122, 0x2122},
{0x2139, 0x2139}, {0x2194, 0x2199}, {0x21A9, 0x21AA},
{0x231A, 0x231B}, {0x2328, 0x2328}, {0x2388, 0x2388},
{0x23CF, 0x23CF}, {0x23E9, 0x23F3}, {0x23F8, 0x23FA},
{0x24C2, 0x24C2}, {0x25AA, 0x25AB}, {0x25B6, 0x25B6},
{0x25C0, 0x25C0}, {0x25FB, 0x25FE}, {0x2600, 0x2605},
{0x2607, 0x2612}, {0x2614, 0x2685}, {0x2690, 0x2705},
{0x2708, 0x2712}, {0x2714, 0x2714}, {0x2716, 0x2716},
{0x271D, 0x271D}, {0x2721, 0x2721}, {0x2728, 0x2728},
{0x2733, 0x2734}, {0x2744, 0x2744}, {0x2747, 0x2747},
{0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755},
{0x2757, 0x2757}, {0x2763, 0x2767}, {0x2795, 0x2797},
{0x27A1, 0x27A1}, {0x27B0, 0x27B0}, {0x27BF, 0x27BF},
{0x2934, 0x2935}, {0x2B05, 0x2B07}, {0x2B1B, 0x2B1C},
{0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x3030, 0x3030},
{0x303D, 0x303D}, {0x3297, 0x3297}, {0x3299, 0x3299},
{0x1F000, 0x1F0FF}, {0x1F10D, 0x1F10F}, {0x1F12F, 0x1F12F},
{0x1F16C, 0x1F171}, {0x1F17E, 0x1F17F}, {0x1F18E, 0x1F18E},
{0x1F191, 0x1F19A}, {0x1F1AD, 0x1F1E5}, {0x1F201, 0x1F20F},
{0x1F21A, 0x1F21A}, {0x1F22F, 0x1F22F}, {0x1F232, 0x1F23A},
{0x1F23C, 0x1F23F}, {0x1F249, 0x1F3FA}, {0x1F400, 0x1F53D},
{0x1F546, 0x1F64F}, {0x1F680, 0x1F6FF}, {0x1F774, 0x1F77F},
{0x1F7D5, 0x1F7FF}, {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F},
{0x1F85A, 0x1F85F}, {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F8FF},
{0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1FAFF},
{0x231A, 0x231B}, {0x2328, 0x2328}, {0x23CF, 0x23CF},
{0x23E9, 0x23F3}, {0x23F8, 0x23FA}, {0x24C2, 0x24C2},
{0x25AA, 0x25AB}, {0x25B6, 0x25B6}, {0x25C0, 0x25C0},
{0x25FB, 0x25FE}, {0x2600, 0x2604}, {0x260E, 0x260E},
{0x2611, 0x2611}, {0x2614, 0x2615}, {0x2618, 0x2618},
{0x261D, 0x261D}, {0x2620, 0x2620}, {0x2622, 0x2623},
{0x2626, 0x2626}, {0x262A, 0x262A}, {0x262E, 0x262F},
{0x2638, 0x263A}, {0x2640, 0x2640}, {0x2642, 0x2642},
{0x2648, 0x2653}, {0x265F, 0x2660}, {0x2663, 0x2663},
{0x2665, 0x2666}, {0x2668, 0x2668}, {0x267B, 0x267B},
{0x267E, 0x267F}, {0x2692, 0x2697}, {0x2699, 0x2699},
{0x269B, 0x269C}, {0x26A0, 0x26A1}, {0x26A7, 0x26A7},
{0x26AA, 0x26AB}, {0x26B0, 0x26B1}, {0x26BD, 0x26BE},
{0x26C4, 0x26C5}, {0x26C8, 0x26C8}, {0x26CE, 0x26CF},
{0x26D1, 0x26D1}, {0x26D3, 0x26D4}, {0x26E9, 0x26EA},
{0x26F0, 0x26F5}, {0x26F7, 0x26FA}, {0x26FD, 0x26FD},
{0x2702, 0x2702}, {0x2705, 0x2705}, {0x2708, 0x270D},
{0x270F, 0x270F}, {0x2712, 0x2712}, {0x2714, 0x2714},
{0x2716, 0x2716}, {0x271D, 0x271D}, {0x2721, 0x2721},
{0x2728, 0x2728}, {0x2733, 0x2734}, {0x2744, 0x2744},
{0x2747, 0x2747}, {0x274C, 0x274C}, {0x274E, 0x274E},
{0x2753, 0x2755}, {0x2757, 0x2757}, {0x2763, 0x2764},
{0x2795, 0x2797}, {0x27A1, 0x27A1}, {0x27B0, 0x27B0},
{0x27BF, 0x27BF}, {0x2934, 0x2935}, {0x2B05, 0x2B07},
{0x2B1B, 0x2B1C}, {0x2B50, 0x2B50}, {0x2B55, 0x2B55},
{0x3030, 0x3030}, {0x303D, 0x303D}, {0x3297, 0x3297},
{0x3299, 0x3299}, {0x1F004, 0x1F004}, {0x1F02C, 0x1F02F},
{0x1F094, 0x1F09F}, {0x1F0AF, 0x1F0B0}, {0x1F0C0, 0x1F0C0},
{0x1F0CF, 0x1F0D0}, {0x1F0F6, 0x1F0FF}, {0x1F170, 0x1F171},
{0x1F17E, 0x1F17F}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A},
{0x1F1AE, 0x1F1E5}, {0x1F201, 0x1F20F}, {0x1F21A, 0x1F21A},
{0x1F22F, 0x1F22F}, {0x1F232, 0x1F23A}, {0x1F23C, 0x1F23F},
{0x1F249, 0x1F25F}, {0x1F266, 0x1F321}, {0x1F324, 0x1F393},
{0x1F396, 0x1F397}, {0x1F399, 0x1F39B}, {0x1F39E, 0x1F3F0},
{0x1F3F3, 0x1F3F5}, {0x1F3F7, 0x1F3FA}, {0x1F400, 0x1F4FD},
{0x1F4FF, 0x1F53D}, {0x1F549, 0x1F54E}, {0x1F550, 0x1F567},
{0x1F56F, 0x1F570}, {0x1F573, 0x1F57A}, {0x1F587, 0x1F587},
{0x1F58A, 0x1F58D}, {0x1F590, 0x1F590}, {0x1F595, 0x1F596},
{0x1F5A4, 0x1F5A5}, {0x1F5A8, 0x1F5A8}, {0x1F5B1, 0x1F5B2},
{0x1F5BC, 0x1F5BC}, {0x1F5C2, 0x1F5C4}, {0x1F5D1, 0x1F5D3},
{0x1F5DC, 0x1F5DE}, {0x1F5E1, 0x1F5E1}, {0x1F5E3, 0x1F5E3},
{0x1F5E8, 0x1F5E8}, {0x1F5EF, 0x1F5EF}, {0x1F5F3, 0x1F5F3},
{0x1F5FA, 0x1F64F}, {0x1F680, 0x1F6C5}, {0x1F6CB, 0x1F6D2},
{0x1F6D5, 0x1F6E5}, {0x1F6E9, 0x1F6E9}, {0x1F6EB, 0x1F6F0},
{0x1F6F3, 0x1F6FF}, {0x1F7DA, 0x1F7FF}, {0x1F80C, 0x1F80F},
{0x1F848, 0x1F84F}, {0x1F85A, 0x1F85F}, {0x1F888, 0x1F88F},
{0x1F8AE, 0x1F8AF}, {0x1F8BC, 0x1F8BF}, {0x1F8C2, 0x1F8CF},
{0x1F8D9, 0x1F8FF}, {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945},
{0x1F947, 0x1F9FF}, {0x1FA58, 0x1FA5F}, {0x1FA6E, 0x1FAFF},
{0x1FC00, 0x1FFFD},
}
+5
View File
@@ -73,6 +73,11 @@ type ImageProperties struct {
// Required: true
Platform ocispec.Platform `json:"Platform"`
// Identity holds information about the identity and origin of the image.
// For image list responses, this can duplicate Build/Pull fields across
// image manifests, because those parts of identity are image-level metadata.
Identity *Identity `json:"Identity,omitempty"`
Size struct {
// Unpacked is the size (in bytes) of the locally unpacked
// (uncompressed) image content that's directly usable by the containers
+3
View File
@@ -8,5 +8,8 @@ type Error struct {
}
func (e *Error) Error() string {
if e == nil {
return "<nil>"
}
return e.Message
}
+22
View File
@@ -1,6 +1,7 @@
package swarm
import (
"cmp"
"net/netip"
"github.com/moby/moby/api/types/network"
@@ -41,6 +42,27 @@ type PortConfig struct {
PublishMode PortConfigPublishMode `json:",omitempty"`
}
// Compare returns the lexical ordering of p and other, and can be used
// with [slices.SortFunc].
//
// The comparison is performed in the following priority order:
// 1. PublishedPort (host port)
// 2. TargetPort (container port)
// 3. Protocol
// 4. PublishMode
func (p PortConfig) Compare(other PortConfig) int {
if n := cmp.Compare(p.PublishedPort, other.PublishedPort); n != 0 {
return n
}
if n := cmp.Compare(p.TargetPort, other.TargetPort); n != 0 {
return n
}
if n := cmp.Compare(p.Protocol, other.Protocol); n != 0 {
return n
}
return cmp.Compare(p.PublishMode, other.PublishMode)
}
// PortConfigPublishMode represents the mode in which the port is to
// be published.
type PortConfigPublishMode string

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