Compare commits

...
61 Commits
Author SHA1 Message Date
Sebastiaan van Stijn a82e528901 Merge pull request #7418 from vvoland/7417-29.x
[29.x] Update to go1.26.10
2026-10-20 13:24:07 +02:00
Paweł Gronowski e35bc1b6ae Update to go1.26.10
These releases include 1 security fix following the security policy:

- crypto/x509: mitigation for security concerns with a specific Certificate Authority

  We're blocking trust in a small set of intermediate certificates due to specific security concerns around their operation.

  Verify will now filter out chains which contain these intermediates, preventing them from being returned to the caller.

  We highly recommend immediately upgrading to this release.

  This is CVE-2026-111943 and Go issue https://go.dev/issue/82345.

release notes: https://go.dev/doc/devel/release#go1.26.10

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-10-20 12:42:59 +02:00
Paweł Gronowski baf6c13889 Bump version
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-10-20 12:40:09 +02:00
Paweł Gronowski 4051f75f69 Merge tag 'v29.9.1' into 7417-29.x
v29.9.1

# -----BEGIN PGP SIGNATURE-----
#
# iK0EABYKAFUWIQQX90wwlHYgZQhz61ZuYvmhLGnMZgUCas/KIxsUgAAAAAAEAA5t
# YW51MiwyLjUrMS4xMiwwLDMbHHBhd2VsLmdyb25vd3NraUBkb2NrZXIuY29tAAoJ
# EG5i+aEsacxmKWgBAKZGzyE7LzPW1Xy2ETngj+i8n96ZxkRPuzU7b2Rd+XLlAQCc
# YddGBSzdhrlh6EQtYdYMx7ArXfx7g8T10tGORAKqBw==
# =7Syv
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed Oct 14 20:29:55 2026 CEST
# gpg:                using EDDSA key 17F74C30947620650873EB566E62F9A12C69CC66
# gpg:                issuer "pawel.gronowski@docker.com"
# gpg: Good signature from "Paweł Gronowski <pawel.gronowski@docker.com>" [ultimate]
2026-10-20 12:39:56 +02:00
Paweł Gronowski 76321a699d Merge tag 'v29.9.0' into 7417-29.x
v29.9.0

# -----BEGIN PGP SIGNATURE-----
#
# iK0EABYKAFUWIQQX90wwlHYgZQhz61ZuYvmhLGnMZgUCasf+dhsUgAAAAAAEAA5t
# YW51MiwyLjUrMS4xMiwwLDMbHHBhd2VsLmdyb25vd3NraUBkb2NrZXIuY29tAAoJ
# EG5i+aEsacxm3EYBAKRhamCCxqMBniTJ9GtLRVn7G3gbweSztHdAzhCo70C6AP41
# K+a4eqECp+bjqTK1ldegnmdyAPZ8g8AoPMpJgSQ2CA==
# =N5Qp
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu Oct  8 22:35:02 2026 CEST
# gpg:                using EDDSA key 17F74C30947620650873EB566E62F9A12C69CC66
# gpg:                issuer "pawel.gronowski@docker.com"
# gpg: Good signature from "Paweł Gronowski <pawel.gronowski@docker.com>" [ultimate]
2026-10-20 12:39:56 +02:00
Paweł Gronowski b82456c62f Merge tag 'v29.8.2' into 7417-29.x
v29.8.2

# -----BEGIN PGP SIGNATURE-----
#
# iK0EABYKAFUWIQQX90wwlHYgZQhz61ZuYvmhLGnMZgUCar1jPxsUgAAAAAAEAA5t
# YW51MiwyLjUrMS4xMiwwLDMbHHBhd2VsLmdyb25vd3NraUBkb2NrZXIuY29tAAoJ
# EG5i+aEsacxmaQABAKG/rnQXNds4jV6qfRvaOS+tGa3Gy2hTY6o2aOgX3g3LAP4n
# OF3/gfVEcaQAK2dgxYTQnQvZi+/p69M1Bjsmy8ytBg==
# =x2Qn
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed Sep 30 21:30:07 2026 CEST
# gpg:                using EDDSA key 17F74C30947620650873EB566E62F9A12C69CC66
# gpg:                issuer "pawel.gronowski@docker.com"
# gpg: Good signature from "Paweł Gronowski <pawel.gronowski@docker.com>" [ultimate]
2026-10-20 12:39:55 +02:00
Paweł Gronowski a98becc3e3 Bump version
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-10-20 12:36:49 +02:00
Paweł Gronowski dfc520dcc2 Merge pull request #7390 from renovate-bot/renovate/go-1.x
chore(deps): update dependency go to v1.27.1
2026-10-14 20:09:52 +02:00
Mend Renovate a3a2bb6118 chore(deps): update dependency go to v1.27.1
Signed-off-by: Mend Renovate <bot@renovateapp.com>
2026-10-14 18:03:30 +00:00
Sebastiaan van Stijn c11295afc4 Merge pull request #7388 from renovate-bot/renovate/tonistiigi-xx-1.x
chore(deps): update tonistiigi/xx docker tag to v1.10.0
2026-10-14 18:11:18 +02:00
Sebastiaan van Stijn 010e125d8b Merge pull request #7386 from renovate-bot/renovate/docker-buildx-bin-0.x
chore(deps): update docker/buildx-bin docker tag to v0.38.0
2026-10-14 18:10:45 +02:00
Sebastiaan van Stijn 5cdf229230 Merge pull request #7387 from renovate-bot/renovate/docker-compose-bin-5.x
chore(deps): update docker/compose-bin docker tag to v5.6.0
2026-10-14 18:10:28 +02:00
Mend Renovate 66f6797582 chore(deps): update tonistiigi/xx docker tag to v1.10.0
Signed-off-by: Mend Renovate <bot@renovateapp.com>
2026-10-14 16:02:38 +00:00
Mend Renovate cb8870a537 chore(deps): update docker/compose-bin docker tag to v5.6.0
Signed-off-by: Mend Renovate <bot@renovateapp.com>
2026-10-14 16:02:33 +00:00
Mend Renovate 529f66d2f9 chore(deps): update docker/buildx-bin docker tag to v0.38.0
Signed-off-by: Mend Renovate <bot@renovateapp.com>
2026-10-14 16:02:28 +00:00
Paweł Gronowski 4d6ff7d646 Merge pull request #7380 from renovate-bot/renovate/actions-upload-artifact-7.x
chore(deps): update actions/upload-artifact action to v7.0.2
2026-10-14 16:53:33 +02:00
Paweł Gronowski b997c5b7aa Merge pull request #7381 from renovate-bot/renovate/docker-actions
chore(deps): update docker/setup-buildx-action action to v4.4.1
2026-10-14 16:49:47 +02:00
Mend Renovate 1822903c1d chore(deps): update docker/setup-buildx-action action to v4.4.1
Signed-off-by: Mend Renovate <bot@renovateapp.com>
2026-10-14 14:34:17 +00:00
Mend Renovate 37013cb262 chore(deps): update actions/upload-artifact action to v7.0.2
Signed-off-by: Mend Renovate <bot@renovateapp.com>
2026-10-14 14:34:11 +00:00
Paweł Gronowski 61ee187552 Merge pull request #7251 from vvoland/add-renovate
Add Renovate config
2026-10-14 16:32:20 +02:00
Paweł Gronowski 433b13a249 Add Renovate config
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-10-14 15:25:17 +02:00
Sebastiaan van Stijn 5c8bfb2844 Merge pull request #7378 from thaJeztah/pr_7371_alt
cli-plugins: Add generic hook messages (alternative)
2026-10-13 17:27:57 +02:00
Paweł Gronowski 528a2ada2e Merge pull request #7375 from thaJeztah/bump_go_winio
vendor: github.com/Microsoft/go-winio v0.6.3
2026-10-13 17:25:25 +02:00
Nicolas Beck de8f4032ad cli-plugins: Add generic hook messages
Allow plugins to return informational messages without the next-steps heading
or indentation. Invoke each hook once and render generic messages before the
shared next-steps block.

Centralize grouping and formatting in PrintMessages, with a callback for
template evaluation and error handling. This avoids the need for an
intermediate EvaluatedMessage type and simplifies hook collection to return
the original responses.

Skip messages containing only whitespace while preserving empty lines
within non-empty messages. Keep PrintNextSteps as a deprecated wrapper
to preserve existing callers and output.

Update the printer tests and remove the obsolete appendMessages helper
and its tests.

Co-authored-by: Nicolas Beck <nicolas.beck@docker.com>
Signed-off-by: Nicolas Beck <nicolas.beck@docker.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-10-12 16:31:43 +02:00
Sebastiaan van Stijn e4078058de Merge pull request #7373 from TanvirTian/7372-fix-remote-context-download-error
fix: return remote context download errors
2026-10-10 16:03:08 +02:00
Sebastiaan van Stijn 8da8b00555 vendor: github.com/Microsoft/go-winio v0.6.3
- improve interoperability with stdlib sentinel errors and deprecate aliases

full diff: https://github.com/Microsoft/go-winio/compare/f19d9717deb0...v0.6.3

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-10-10 15:28:26 +02:00
Tanvir Tian 067d069f0c fix: return remote context download errors
Signed-off-by: Tanvir Tian <beachpilis@gmail.com>
2026-10-10 18:16:02 +06:00
Sebastiaan van Stijn 02fbbf3eb0 Merge pull request #7369 from nico1510/offload-plugin-guidance
cmd/docker: Add guidance for unavailable offload commands
2026-10-09 18:08:27 +02:00
Sebastiaan van Stijn 890976a13b Merge pull request #7370 from thaJeztah/bump_version
Bump version to 29.9.1
2026-10-09 15:12:08 +02:00
Sebastiaan van Stijn 23c66ccd61 Bump version to 29.9.1
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-10-09 13:45:39 +02:00
Nicolas Beck 3e686a8b59 cmd/docker: Add guidance for unavailable offload commands
Show installation guidance and a documentation link when the offload
plugin is missing or invalid. Handle this before command execution so
plugin-specific flags and --help also receive the guidance.

Signed-off-by: Nicolas Beck <nicolas.beck@docker.com>
2026-10-09 10:06:48 +00:00
Paweł Gronowski eabc65846b Merge tag 'v29.8.1' into 29.x
v29.8.1
2026-09-29 14:09:44 +02:00
Paweł Gronowski b873f57cd0 Merge tag 'v29.8.0' into 29.x
v29.8.0
2026-09-29 14:09:44 +02:00
Paweł Gronowski c8a849c2f9 Merge tag 'v29.8.0-rc.1' into 29.x
v29.8.0-rc.1
2026-09-29 14:09:44 +02:00
Paweł Gronowski f4efc1b596 Merge tag 'v29.7.2' into 29.x
v29.7.2
2026-08-11 13:21:01 +02:00
Paweł Gronowski 2747e2e3bf Merge tag 'v29.7.1' into 29.x
v29.7.1
2026-08-11 13:21:01 +02:00
Paweł Gronowski 329cdf1a9d Merge tag 'v29.7.0' into 29.x
v29.7.0
2026-08-11 13:21:01 +02:00
Paweł Gronowski 0c7e2e7203 Merge tag 'v29.7.0-rc.1' into 29.x
v29.7.0-rc.1
2026-08-11 13:21:00 +02:00
Paweł Gronowski dfc4efb1e2 Merge pull request #7109 from vvoland/update-docker
[29.x] Bump to 29.6.2
2026-07-16 18:09:24 +02:00
Paweł Gronowski dba867594c Bump to 29.6.2
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-07-16 12:51:30 +02:00
Paweł Gronowski d9aefe565a Merge tag 'v29.6.1' into 29.x
v29.6.1
2026-07-15 17:35:42 +02:00
Paweł Gronowski 9182b351c1 Merge tag 'v29.6.0' into 29.x
v29.6.0
2026-07-15 17:35:42 +02:00
Paweł Gronowski 0ae55a3e6c Merge tag 'v29.5.3' into 29.x
v29.5.3
2026-07-15 17:35:42 +02:00
Paweł Gronowski 029a4b21b3 Merge tag 'v29.5.2' into 29.x
v29.5.2
2026-07-15 17:35:41 +02:00
Paweł Gronowski e596fea454 Merge tag 'v29.5.1' into 29.x
v29.5.1
2026-07-15 17:35:41 +02:00
Paweł Gronowski 0c72f456aa Merge tag 'v29.5.0' into 29.x
v29.5.0
2026-07-15 17:35:41 +02:00
Paweł Gronowski 26f2736a2b Merge tag 'v29.4.1' into 29.x
v29.4.1
2026-07-15 17:35:41 +02:00
Paweł Gronowski ac4c4dfabd Merge tag 'v29.4.0' into 29.x
v29.4.0
2026-07-15 17:35:40 +02:00
Sebastiaan van Stijn 1245054535 Merge pull request #7099 from vvoland/7098-29.x
[29.x backport] scripts/sync-branch: Fix non-interactive merge
2026-07-10 08:34:26 +02:00
Paweł Gronowski f72fb0caf0 scripts/sync-branch: Fix non-interactive merge
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
(cherry picked from commit f57e528457)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-07-09 16:52:02 +02:00
Sebastiaan van Stijn e4af2bf0d3 Merge pull request #7097 from vvoland/7091-29.x
[29.x backport] gha: Add release branch sync workflow
2026-07-09 16:35:45 +02:00
Paweł Gronowski 49a1af6aaa gha: Add release branch sync workflow
Add a manually dispatched workflow for maintainers to sync a Docker
release branch to a selected release tag.

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

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

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
(cherry picked from commit 182f56fe8c)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-07-09 14:45:32 +02:00
Paweł Gronowski 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 Stijn 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ł Gronowski 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] 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] 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ł Gronowski 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
29 changed files with 438 additions and 143 deletions
+59
View File
@@ -0,0 +1,59 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
// https://docs.renovatebot.com/modules/manager/github-actions/
// https://docs.renovatebot.com/presets-helpers/
"helpers:pinGitHubActionDigestsToSemver",
":gitSignOff"
],
"gomod": {
"managerFilePatterns": ["/(^|/)vendor\\.mod$/"]
},
"postUpdateOptions": ["gomodTidy"],
"minimumReleaseAge": "3 days",
"internalChecksFilter": "strict",
"packageRules": [
{
"matchManagers": ["gomod"],
"matchFileNames": ["vendor.mod"],
"semanticCommits": "disabled",
"commitMessagePrefix": "vendor:"
},
{
"matchManagers": ["gomod"],
"matchFileNames": ["cmd/docker-trust/go.mod"],
// The trust module does not vendor dependencies; vendor/ belongs to the root module.
"postUpdateOptions": ["gomodSkipVendor"],
"semanticCommits": "disabled",
"commitMessagePrefix": "cmd/docker-trust:"
},
{
"matchManagers": ["gomod"],
"matchFileNames": ["vendor.mod"],
"groupName": "golang.org/x packages",
"groupSlug": "golang-x",
"matchDepNames": ["/^golang\\.org\\/x\\//"]
},
{
"matchManagers": ["github-actions"],
"minimumReleaseAge": "7 days",
"addLabels": ["area/ci", "status/2-code-review"]
},
{
"matchManagers": ["github-actions"],
"groupName": "docker GitHub Actions",
"groupSlug": "docker-actions",
"matchDepNames": ["/^docker\\//"]
},
{
// Keep all the google.golang.org/genproto modules at the same version.
"matchManagers": ["gomod"],
"matchFileNames": ["vendor.mod"],
"groupName": "google.golang.org/genproto packages",
"groupSlug": "google-genproto",
"matchDepNames": ["/^google\\.golang\\.org\\/genproto(\\/|$)/"],
"schedule": ["every month"]
}
]
}
+3 -3
View File
@@ -65,7 +65,7 @@ jobs:
steps:
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
-
name: Build
uses: docker/bake-action@018cb6412ab401ebaa809aa5f85966b74628600f # v7.4.0
@@ -91,7 +91,7 @@ jobs:
PLATFORM: ${{ matrix.platform }}
-
name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: ${{ env.ARTIFACT_NAME }}
path: /tmp/out/*
@@ -156,7 +156,7 @@ jobs:
steps:
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
-
name: Build
uses: docker/bake-action@018cb6412ab401ebaa809aa5f85966b74628600f # v7.4.0
+1 -1
View File
@@ -64,7 +64,7 @@ jobs:
name: Update Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.26.9"
go-version: "1.26.10"
cache: false
-
name: Initialize CodeQL
+1 -1
View File
@@ -66,7 +66,7 @@ jobs:
docker info
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
-
name: Run ${{ matrix.target }}
run: |
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
fi
- name: Upload context
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: pr-review-context
path: context/
+2 -2
View File
@@ -30,7 +30,7 @@ jobs:
steps:
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
-
name: Test
uses: docker/bake-action@018cb6412ab401ebaa809aa5f85966b74628600f # v7.4.0
@@ -68,7 +68,7 @@ jobs:
name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.26.9"
go-version: "1.26.10"
cache: false
-
name: Test
+1 -1
View File
@@ -101,7 +101,7 @@ jobs:
name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.26.9"
go-version: "1.26.10"
cache: false
-
name: Run gocompat check
+1 -1
View File
@@ -5,7 +5,7 @@ run:
# which causes it to fallback to go1.17 semantics.
#
# TODO(thaJeztah): update "usetesting" settings to enable go1.24 features once our minimum version is go1.24
go: "1.26.9"
go: "1.26.10"
timeout: 5m
+4 -4
View File
@@ -8,11 +8,11 @@ ARG BASE_VARIANT=alpine
ARG ALPINE_VERSION=3.23
ARG BASE_DEBIAN_DISTRO=bookworm
ARG GO_VERSION=1.26.9
ARG GO_VERSION=1.26.10
# 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.9.0
ARG XX_VERSION=1.10.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.34.1
ARG BUILDX_VERSION=0.38.0
# COMPOSE_VERSION is the version of compose to install in the dev container.
# It must be a tag in the docker.io/docker/compose-bin image repository
# on Docker Hub.
ARG COMPOSE_VERSION=v5.1.3
ARG COMPOSE_VERSION=v5.6.0
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
+1 -1
View File
@@ -1 +1 @@
29.9.0
29.9.2
+4
View File
@@ -30,7 +30,11 @@ package hooks
type ResponseType int
const (
// NextSteps renders suggestions under a shared "What's next:" header.
NextSteps ResponseType = 0
// GenericMessage renders before NextSteps, without a header or indentation.
// CLI versions that do not recognize this type ignore the response.
GenericMessage ResponseType = 1
)
// Request is the type representing the information
+35 -10
View File
@@ -7,17 +7,42 @@ const (
indent = " "
)
// PrintNextSteps renders list of [NextSteps] messages and writes them
// to out. It is a no-op if messages is empty.
func PrintNextSteps(out io.Writer, messages []string) {
if len(messages) == 0 {
return
// PrintMessages groups messages by type and writes them to out. Generic messages
// appear first, without a header or indentation, followed by next steps under a
// shared "What's next:" header. Order within each type is preserved.
// Messages with no lines or an unknown type produce no output.
func PrintMessages(out io.Writer, messages []Response, evaluate func(Response) []string) {
grouped := make(map[ResponseType][]string)
for _, message := range messages {
lines := evaluate(message)
grouped[message.Type] = append(grouped[message.Type], lines...)
}
for _, responseType := range []ResponseType{GenericMessage, NextSteps} {
lines := grouped[responseType]
if len(lines) == 0 {
continue
}
_, _ = io.WriteString(out, whatsNext)
for _, msg := range messages {
_, _ = io.WriteString(out, indent)
_, _ = io.WriteString(out, msg)
_, _ = io.WriteString(out, "\n")
header, prefix := "\n", ""
if responseType == NextSteps {
header, prefix = whatsNext, indent
}
_, _ = io.WriteString(out, header)
for _, line := range lines {
_, _ = io.WriteString(out, prefix)
_, _ = io.WriteString(out, line)
_, _ = io.WriteString(out, "\n")
}
}
}
// PrintNextSteps renders list of [NextSteps] messages and writes them
// to out. It is a no-op if messages is empty.
//
// Deprecated: use [PrintMessages] instead.
func PrintNextSteps(out io.Writer, messages []string) {
PrintMessages(out, []Response{{Type: NextSteps}}, func(Response) []string {
return messages
})
}
+84 -1
View File
@@ -8,7 +8,85 @@ import (
"gotest.tools/v3/assert"
)
func TestPrintHookMessages(t *testing.T) {
func TestPrintMessages(t *testing.T) {
const header = "\n\x1b[1mWhat's next:\x1b[0m\n"
for _, tc := range []struct {
doc string
messages []hooks.Response
expected string
}{
{doc: "no messages"},
{
doc: "no lines",
messages: []hooks.Response{
{Type: hooks.GenericMessage},
{Type: hooks.NextSteps},
},
},
{
doc: "generic message",
messages: []hooks.Response{
{Type: hooks.GenericMessage, Template: "Session active"},
},
expected: "\nSession active\n",
},
{
doc: "preserve message formatting",
messages: []hooks.Response{
{Type: hooks.GenericMessage, Template: "Session active\n\n Details"},
},
expected: "\nSession active\n\n Details\n",
},
{
doc: "next steps",
messages: []hooks.Response{
{Type: hooks.NextSteps, Template: "Try another command"},
},
expected: header + " Try another command\n",
},
{
doc: "group interleaved types and preserve order within each type",
messages: []hooks.Response{
{Type: hooks.NextSteps, Template: "First suggestion"},
{Type: hooks.GenericMessage, Template: "Session active"},
{Type: hooks.NextSteps, Template: "Second suggestion\nThird suggestion"},
{Type: hooks.GenericMessage, Template: "Another status"},
},
expected: "\nSession active\nAnother status\n" + header + " First suggestion\n Second suggestion\n Third suggestion\n",
},
{
doc: "unknown type",
messages: []hooks.Response{
{Type: hooks.ResponseType(99), Template: "Ignored"},
},
},
{
doc: "unknown type does not affect known messages",
messages: []hooks.Response{
{Type: hooks.ResponseType(99), Template: "Ignored"},
{Type: hooks.GenericMessage, Template: "Session active"},
{Type: hooks.NextSteps, Template: "Try another command"},
},
expected: "\nSession active\n" + header + " Try another command\n",
},
} {
t.Run(tc.doc, func(t *testing.T) {
var w strings.Builder
hooks.PrintMessages(&w, tc.messages, func(message hooks.Response) []string {
if message.Template == "" {
return nil
}
lines, err := hooks.ParseTemplate(message.Template, nil)
assert.NilError(t, err)
return lines
})
assert.Equal(t, w.String(), tc.expected)
})
}
}
func TestPrintNextSteps(t *testing.T) {
const header = "\n\x1b[1mWhat's next:\x1b[0m\n"
tests := []struct {
@@ -34,6 +112,11 @@ func TestPrintHookMessages(t *testing.T) {
" Foo\n" +
" bar\n",
},
{
doc: "preserve empty and indented lines",
messages: []string{"", " Details", ""},
expectedOutput: header + " \n Details\n \n",
},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
+11 -1
View File
@@ -7,6 +7,7 @@ import (
"bytes"
"errors"
"fmt"
"slices"
"strings"
"text/template"
@@ -43,7 +44,16 @@ func ParseTemplate(hookTemplate string, cmd *cobra.Command) ([]string, error) {
if n := strings.Count(out, "\n"); n > maxMessages {
return nil, fmt.Errorf("hook template contains too many messages (%d): maximum is %d", n, maxMessages)
}
return strings.SplitN(out, "\n", maxMessages), nil
lines := strings.SplitN(out, "\n", maxMessages)
// Skip messages containing only whitespace, but preserve empty lines
// in messages that have content (e.g., for paragraph spacing).
if !slices.ContainsFunc(lines, func(line string) bool {
return strings.TrimSpace(line) != ""
}) {
return nil, nil
}
return lines, nil
}
var ErrHookTemplateParse = errors.New("failed to parse hook template")
+11 -1
View File
@@ -33,7 +33,17 @@ func TestParseTemplate(t *testing.T) {
{
doc: "empty template",
template: "",
expectedOutput: []string{""},
expectedOutput: nil,
},
{
doc: "only empty lines",
template: "\n\n",
expectedOutput: nil,
},
{
doc: "whitespace-only lines",
template: " \n\t\n ",
expectedOutput: nil,
},
{
doc: "plain message",
+23 -47
View File
@@ -49,11 +49,18 @@ func RunPluginHooks(ctx context.Context, dockerCLI config.Provider, rootCmd, sub
}
func runHooks(ctx context.Context, cfg *configfile.ConfigFile, rootCmd, subCommand *cobra.Command, invokedCommand string, flags map[string]string, cmdErrorMessage string) {
nextSteps := invokeAndCollectHooks(ctx, cfg, rootCmd, subCommand, invokedCommand, flags, cmdErrorMessage)
hooks.PrintNextSteps(subCommand.ErrOrStderr(), nextSteps)
messages := invokeAndCollectHooks(ctx, cfg, rootCmd, invokedCommand, flags, cmdErrorMessage)
hooks.PrintMessages(subCommand.ErrOrStderr(), messages, func(message hooks.Response) []string {
lines, err := hooks.ParseTemplate(message.Template, subCommand)
if err != nil {
logrus.WithError(err).Debug("Failed to evaluate hook template")
return nil
}
return lines
})
}
func invokeAndCollectHooks(ctx context.Context, cfg *configfile.ConfigFile, rootCmd, subCmd *cobra.Command, subCmdStr string, flags map[string]string, cmdErrorMessage string) []string {
func invokeAndCollectHooks(ctx context.Context, cfg *configfile.ConfigFile, rootCmd *cobra.Command, subCmdStr string, flags map[string]string, cmdErrorMessage string) []hooks.Response {
if ctx.Err() != nil {
return nil
}
@@ -64,17 +71,17 @@ func invokeAndCollectHooks(ctx context.Context, cfg *configfile.ConfigFile, root
}
pluginDirs := getPluginDirs(cfg)
nextSteps := make([]string, 0, len(pluginsCfg))
var collected []hooks.Response
tryInvokeHook := func(pluginName string, pluginCfg map[string]string) (messages []string, ok bool, err error) {
tryInvokeHook := func(pluginName string, pluginCfg map[string]string) (hooks.Response, error) {
match, matched := pluginMatch(pluginCfg, subCmdStr, cmdErrorMessage)
if !matched {
return nil, false, nil
return hooks.Response{}, nil
}
p, err := getPlugin(pluginName, pluginDirs, rootCmd)
if err != nil {
return nil, false, err
return hooks.Response{}, err
}
resp, err := p.RunHook(ctx, hooks.Request{
@@ -83,29 +90,23 @@ func invokeAndCollectHooks(ctx context.Context, cfg *configfile.ConfigFile, root
CommandError: cmdErrorMessage,
})
if err != nil {
return nil, false, err
return hooks.Response{}, err
}
var message hooks.Response
if err := json.Unmarshal(resp, &message); err != nil {
return nil, false, fmt.Errorf("failed to unmarshal hook response (%q): %w", string(resp), err)
return hooks.Response{}, fmt.Errorf("failed to unmarshal hook response (%q): %w", string(resp), err)
}
// currently the only hook type
if message.Type != hooks.NextSteps {
return nil, false, errors.New("unexpected hook response type: " + strconv.Itoa(int(message.Type)))
if message.Type != hooks.NextSteps && message.Type != hooks.GenericMessage {
return hooks.Response{}, errors.New("unexpected hook response type: " + strconv.Itoa(int(message.Type)))
}
messages, err = hooks.ParseTemplate(message.Template, subCmd)
if err != nil {
return nil, false, err
}
return messages, true, nil
return message, nil
}
for pluginName, pluginCfg := range pluginsCfg {
messages, ok, err := tryInvokeHook(pluginName, pluginCfg)
message, err := tryInvokeHook(pluginName, pluginCfg)
if err != nil {
// skip misbehaving plugins, but don't halt execution
logrus.WithFields(logrus.Fields{
@@ -114,38 +115,13 @@ func invokeAndCollectHooks(ctx context.Context, cfg *configfile.ConfigFile, root
}).Debug("Plugin hook invocation failed")
continue
}
if !ok {
if message.Template == "" {
continue
}
var appended bool
nextSteps, appended = appendNextSteps(nextSteps, messages)
if !appended {
logrus.WithFields(logrus.Fields{
"plugin": pluginName,
}).Debug("Plugin responded with an empty hook message; ignoring")
}
collected = append(collected, message)
}
return nextSteps
}
// appendNextSteps appends the processed hook output to the nextSteps slice.
// If the processed hook output is empty, it is not appended.
// Empty lines are not stripped if there's at least one non-empty line.
func appendNextSteps(nextSteps []string, processed []string) ([]string, bool) {
empty := true
for _, l := range processed {
if strings.TrimSpace(l) != "" {
empty = false
break
}
}
if empty {
return nextSteps, false
}
return append(nextSteps, processed...), true
return collected
}
// pluginMatch takes a plugin configuration and a string representing the
+108 -35
View File
@@ -2,6 +2,11 @@ package manager
import (
"context"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"github.com/docker/cli/cli/config/configfile"
@@ -258,38 +263,6 @@ func TestMatchHookConfig(t *testing.T) {
}
}
func TestAppendNextSteps(t *testing.T) {
testCases := []struct {
processed []string
expectedOut []string
}{
{
processed: []string{},
expectedOut: []string{},
},
{
processed: []string{"", ""},
expectedOut: []string{},
},
{
processed: []string{"Some hint", "", "Some other hint"},
expectedOut: []string{"Some hint", "", "Some other hint"},
},
{
processed: []string{"Hint 1", "Hint 2"},
expectedOut: []string{"Hint 1", "Hint 2"},
},
}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
got, appended := appendNextSteps([]string{}, tc.processed)
assert.Check(t, is.DeepEqual(got, tc.expectedOut))
assert.Check(t, is.Equal(appended, len(got) > 0))
})
}
}
func TestRunPluginHooksPassesErrorMessage(t *testing.T) {
cfg := configfile.New("")
cfg.Plugins = map[string]map[string]string{
@@ -336,7 +309,7 @@ func TestInvokeAndCollectHooksErrorHooksSkippedOnSuccess(t *testing.T) {
// 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,
context.Background(), cfg, root,
"build", map[string]string{}, "",
)
assert.Check(t, is.Len(result, 0))
@@ -349,7 +322,7 @@ func TestInvokeAndCollectHooksNoPlugins(t *testing.T) {
root.AddCommand(sub)
result := invokeAndCollectHooks(
context.Background(), cfg, root, sub,
context.Background(), cfg, root,
"build", map[string]string{}, "some error",
)
assert.Check(t, is.Len(result, 0))
@@ -368,8 +341,108 @@ func TestInvokeAndCollectHooksCancelledContext(t *testing.T) {
cancel() // cancel immediately
result := invokeAndCollectHooks(
ctx, cfg, root, sub,
ctx, cfg, root,
"build", map[string]string{}, "exit status 1",
)
assert.Check(t, is.Nil(result))
}
func TestRunHooksMessages(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("test plugins require a POSIX shell")
}
const header = "\n\x1b[1mWhat's next:\x1b[0m\n"
for _, tc := range []struct {
doc string
responses map[string]string
expected string
}{
{
doc: "generic message preserves formatting and expands templates",
responses: map[string]string{
"status": `{"Type":1,"Template":"Status for {{command}}\n\n Details"}`,
},
expected: "\nStatus for build\n\n Details\n",
},
{
doc: "omitted type defaults to next steps",
responses: map[string]string{
"suggestion": `{"Template":"Try another command"}`,
},
expected: header + " Try another command\n",
},
{
doc: "generic messages precede shared next steps header",
responses: map[string]string{
"statusone": `{"Type":1,"Template":"Session active"}`,
"statustwo": `{"Type":1,"Template":"Session active"}`,
"suggestionone": `{"Type":0,"Template":"Try another command"}`,
"suggestiontwo": `{"Type":0,"Template":"Try another command"}`,
},
expected: "\nSession active\nSession active\n" + header + " Try another command\n Try another command\n",
},
{
doc: "empty responses produce no output",
responses: map[string]string{
"status": `{"Type":1,"Template":" \n\t"}`,
"suggestion": `{"Type":0,"Template":""}`,
},
},
{
doc: "empty generic message does not affect next steps",
responses: map[string]string{
"status": `{"Type":1,"Template":""}`,
"suggestion": `{"Type":0,"Template":"Try another command"}`,
},
expected: header + " Try another command\n",
},
{
doc: "invalid responses do not affect other plugins",
responses: map[string]string{
"unknown": `{"Type":99,"Template":"Ignored"}`,
"invalid": `{"Type":1,"Template":"{{"}`,
"status": `{"Type":1,"Template":"Session active"}`,
"suggestion": `{"Type":0,"Template":"Try another command"}`,
},
expected: "\nSession active\n" + header + " Try another command\n",
},
} {
t.Run(tc.doc, func(t *testing.T) {
pluginDir := t.TempDir()
cfg := configfile.New("")
cfg.CLIPluginsExtraDirs = []string{pluginDir}
cfg.Plugins = make(map[string]map[string]string)
for name, response := range tc.responses {
plugin := fmt.Sprintf(`#!/bin/sh
case "$1" in
docker-cli-plugin-metadata)
printf '%%s\n' '{"SchemaVersion":"0.1.0","Vendor":"test"}'
;;
*)
test "$2" = docker-cli-plugin-hooks || exit 1
printf 'called\n' >> "$0.calls"
printf '%%s\n' '%s'
;;
esac
`, response)
assert.NilError(t, os.WriteFile(filepath.Join(pluginDir, "docker-"+name), []byte(plugin), 0o755))
cfg.Plugins[name] = map[string]string{"hooks": "build"}
}
root := &cobra.Command{Use: "docker"}
sub := &cobra.Command{Use: "build"}
root.AddCommand(sub)
var output strings.Builder
sub.SetErr(&output)
runHooks(context.Background(), cfg, root, sub, "build", nil, "")
assert.Equal(t, output.String(), tc.expected)
for name := range tc.responses {
calls, err := os.ReadFile(filepath.Join(pluginDir, "docker-"+name+".calls"))
assert.NilError(t, err)
assert.Equal(t, string(calls), "called\n", "each plugin hook must run exactly once")
}
})
}
}
+5 -2
View File
@@ -260,8 +260,11 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
contextDir = tempDir
case build.ContextTypeRemote:
buildCtx, relDockerfile, err = build.GetContextFromURL(progBuff, options.context, options.dockerfileName)
if err != nil && options.quiet {
_, _ = fmt.Fprintln(dockerCli.Err(), progBuff)
if err != nil {
if options.quiet {
_, _ = fmt.Fprintln(dockerCli.Err(), progBuff)
}
return err
}
default:
return fmt.Errorf("unable to prepare context: path %q not found", options.context)
+31
View File
@@ -6,6 +6,8 @@ import (
"compress/gzip"
"context"
"io"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"slices"
@@ -172,6 +174,35 @@ RUN echo hello world
assert.DeepEqual(t, fakeBuild.filenames(t), []string{"Dockerfile"})
}
// TestRunBuildRemoteContextDownloadError verifies that remote context
// download errors are propagated.
//
// regression test for https://github.com/docker/cli/issues/7372
func TestRunBuildRemoteContextDownloadError(t *testing.T) {
t.Setenv("DOCKER_BUILDKIT", "0")
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "not found", http.StatusNotFound)
}))
defer server.Close()
imageBuildCalled := false
cli := test.NewFakeCli(&fakeClient{
imageBuildFunc: func(ctx context.Context, buildContext io.Reader, options client.ImageBuildOptions) (client.ImageBuildResult, error) {
imageBuildCalled = true
return client.ImageBuildResult{Body: io.NopCloser(bytes.NewReader(nil))}, nil
},
})
options := newBuildOptions()
options.context = server.URL
err := runBuild(context.Background(), cli, options)
assert.ErrorContains(t, err, "unable to download remote context")
assert.Assert(t, !imageBuildCalled, "ImageBuild should not be called when downloading the remote context fails")
}
type fakeBuild struct {
context *tar.Reader
options client.ImageBuildOptions
+7 -3
View File
@@ -571,11 +571,15 @@ func runDocker(ctx context.Context, dockerCli *command.DockerCli) error {
return nil
}
if !errdefs.IsNotFound(err) {
// For plugin not found we fall through to
// cmd.Execute() which deals with reporting
// "command not found" in a consistent way.
return err
}
if args[0] == "offload" {
// Report this before cmd.ExecuteContext(): plugin-specific flags such as
// --cpus otherwise fail parsing before the root command's RunE.
return errors.New("docker: to use 'docker offload', install the plugin.\nSee https://docs.docker.com/go/cloud-flag/")
}
// Other missing or invalid plugins fall through to cmd.ExecuteContext(),
// which reports the standard unknown-command error.
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.26.9
ARG GO_VERSION=1.26.10
# 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
@@ -10,7 +10,7 @@ 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.34.1
ARG BUILDX_VERSION=0.38.0
FROM docker/buildx-bin:${BUILDX_VERSION} AS buildx
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS golang
+1 -1
View File
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.26.9
ARG GO_VERSION=1.26.10
# ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image.
# It must be a supported tag in the docker.io/library/alpine image repository
+1 -1
View File
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.26.9
ARG GO_VERSION=1.26.10
# ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image.
# It must be a supported tag in the docker.io/library/alpine image repository
+1 -1
View File
@@ -71,7 +71,7 @@ require (
require (
github.com/Azure/go-ansiterm v0.0.0-20261006220739-8c912ac31dd4 // indirect
github.com/Microsoft/go-winio v0.6.3-0.20260930231756-f19d9717deb0 // indirect; see https://github.com/microsoft/hcsshim/pull/2545
github.com/Microsoft/go-winio v0.6.3 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
+2 -2
View File
@@ -4,8 +4,8 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8af
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Azure/go-ansiterm v0.0.0-20261006220739-8c912ac31dd4 h1:m2xcjRABtG288vnlV3U/PKh/HnPURxZp50RKNUwBxM8=
github.com/Azure/go-ansiterm v0.0.0-20261006220739-8c912ac31dd4/go.mod h1:MTUE7ZuZO89FAtAwozGg6AWD83pcdefnEq5gF82MB8s=
github.com/Microsoft/go-winio v0.6.3-0.20260930231756-f19d9717deb0 h1:71VU8kW/LCxJkx41+YYys1EgQO+AUnPa1TrxTSyYUCU=
github.com/Microsoft/go-winio v0.6.3-0.20260930231756-f19d9717deb0/go.mod h1:ma0QpxizD4fyyzJ1C7MIblymTD2Nxz5Mho/3CepwIZg=
github.com/Microsoft/go-winio v0.6.3 h1:M/52tsoyTfwaxajLnXZm7PqkbMGRH8iSWP+8fcg9MQ4=
github.com/Microsoft/go-winio v0.6.3/go.mod h1:ma0QpxizD4fyyzJ1C7MIblymTD2Nxz5Mho/3CepwIZg=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
+25 -7
View File
@@ -3,8 +3,9 @@
package winio
import (
"errors"
"context"
"io"
"os"
"runtime"
"sync"
"sync/atomic"
@@ -20,16 +21,33 @@ import (
//sys setFileCompletionNotificationModes(h windows.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
//sys wsaGetOverlappedResult(h windows.Handle, o *windows.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
var (
ErrFileClosed = errors.New("file has already been closed")
ErrTimeout = &timeoutError{}
)
// ErrFileClosed is returned when an operation is performed on a closed file.
//
// Deprecated: use [os.ErrClosed] instead.
var ErrFileClosed = os.ErrClosed
// ErrTimeout is returned when an I/O operation exceeds its deadline.
//
// It matches both [context.DeadlineExceeded] and [os.ErrDeadlineExceeded]
// with [errors.Is].
var ErrTimeout error = &timeoutError{}
// timeoutError preserves the historical "i/o timeout" error while matching
// both context.DeadlineExceeded and os.ErrDeadlineExceeded through errors.Is.
// This mirrors the behavior of net.errTimeout:
// https://github.com/golang/go/blob/go1.27.1/src/net/net.go
//
// TODO: consider replacing ErrTimeout with os.ErrDeadlineExceeded if os.ErrDeadlineExceeded is changed to match context.DeadlineExceeded.
type timeoutError struct{}
func (*timeoutError) Error() string { return "i/o timeout" }
func (*timeoutError) Timeout() bool { return true }
func (*timeoutError) Temporary() bool { return true }
func (*timeoutError) Unwrap() error { return os.ErrDeadlineExceeded }
func (*timeoutError) Is(err error) bool {
return err == context.DeadlineExceeded
}
type timeoutChan chan struct{}
@@ -143,7 +161,7 @@ func (f *win32File) prepareIO() (*ioOperation, error) {
f.wgLock.RLock()
if f.closing.Load() {
f.wgLock.RUnlock()
return nil, ErrFileClosed
return nil, os.ErrClosed
}
f.wg.Add(1)
f.wgLock.RUnlock()
@@ -192,7 +210,7 @@ func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, er
err = r.err
if err == windows.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
if f.closing.Load() {
err = ErrFileClosed
err = os.ErrClosed
}
} else if err != nil && f.socket {
// err is from Win32. Query the overlapped structure to get the winsock error.
+1 -1
View File
@@ -441,7 +441,7 @@ func canRedial(err error) bool {
func (conn *HvsockConn) opErr(op string, err error) error {
// translate from "file closed" to "socket closed"
if errors.Is(err, ErrFileClosed) {
if errors.Is(err, os.ErrClosed) {
err = socket.ErrSocketClosed
}
return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err}
+11 -12
View File
@@ -102,12 +102,10 @@ func (status ntStatus) Err() error {
return rtlNtStatusToDosError(status)
}
var (
// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
ErrPipeListenerClosed = net.ErrClosed
errPipeWriteClosed = errors.New("pipe has been closed for write")
)
// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
//
// Deprecated: use [net.ErrClosed] instead.
var ErrPipeListenerClosed = net.ErrClosed
type win32Pipe struct {
*win32File
@@ -144,9 +142,10 @@ func (f *win32Pipe) Disconnect() error {
}
// CloseWrite closes the write side of a message pipe in byte mode.
// It returns [io.ErrClosedPipe] if the write side is already closed.
func (f *win32MessageBytePipe) CloseWrite() error {
if f.writeClosed {
return errPipeWriteClosed
return io.ErrClosedPipe
}
err := f.win32File.Flush()
if err != nil {
@@ -161,10 +160,10 @@ func (f *win32MessageBytePipe) CloseWrite() error {
}
// Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since
// they are used to implement CloseWrite().
// they are used to implement CloseWrite. It returns [io.ErrClosedPipe] if the write side is closed.
func (f *win32MessageBytePipe) Write(b []byte) (int, error) {
if f.writeClosed {
return 0, errPipeWriteClosed
return 0, io.ErrClosedPipe
}
if len(b) == 0 {
return 0, nil
@@ -454,7 +453,7 @@ func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) {
_ = p.Close()
p = nil
<-ch
err = ErrPipeListenerClosed
err = net.ErrClosed
}
return p, err
}
@@ -479,7 +478,7 @@ func (l *win32PipeListener) listenerRoutine() {
}
}
responseCh <- acceptResponse{p, err}
closed = err == ErrPipeListenerClosed //nolint:errorlint // err is Errno
closed = err == net.ErrClosed //nolint:errorlint // err is Errno
}
}
windows.Close(l.firstHandle)
@@ -571,7 +570,7 @@ func (l *win32PipeListener) Accept() (net.Conn, error) {
}
return &win32Pipe{win32File: response.f, path: l.path}, nil
case <-l.doneCh:
return nil, ErrPipeListenerClosed
return nil, net.ErrClosed
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ dario.cat/mergo
## explicit; go 1.17
github.com/Azure/go-ansiterm
github.com/Azure/go-ansiterm/winterm
# github.com/Microsoft/go-winio v0.6.3-0.20260930231756-f19d9717deb0
# github.com/Microsoft/go-winio v0.6.3
## explicit; go 1.26.0
github.com/Microsoft/go-winio
github.com/Microsoft/go-winio/internal/fs