Compare commits

..
23 Commits
Author SHA1 Message Date
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
891 changed files with 15936 additions and 58446 deletions
-7
View File
@@ -9,10 +9,3 @@ updates:
- "status/2-code-review"
cooldown:
default-days: 7
groups:
codeql-actions:
patterns:
- "github/codeql-action/*"
docker-actions:
patterns:
- "docker/*"
+45 -31
View File
@@ -35,7 +35,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
-
@@ -65,10 +65,10 @@ jobs:
steps:
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
-
name: Build
uses: docker/bake-action@d3418bd7d0e9324001bca92fa8ba175ea7e6dc9b # v7.3.0
uses: docker/bake-action@6614cfa25eff9a0b2b2697efb0b6159e7680d584 # v7.2.0
with:
targets: ${{ matrix.target }}
set: |
@@ -98,32 +98,46 @@ jobs:
if-no-files-found: error
bin-image:
runs-on: ubuntu-24.04
if: ${{ github.event_name != 'pull_request' && github.repository == 'docker/cli' }}
uses: docker/github-builder/.github/workflows/bake.yml@58cb9f5b71b1836d6f690c1e95effdeb9b98cb8a # v1.17.0
permissions:
contents: read # same as global permission
id-token: write # for signing attestation(s) and authenticating to Docker Hub with GitHub OIDC Token
with:
setup-qemu: true
target: bin-image-cross
cache: true
cache-scope: bin-image
output: image
push: true
vars: |
VERSION=${{ github.ref }}
meta-images: |
dockereng/cli-bin
meta-tags: |
type=semver,pattern={{version}}
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
registry-identities: |
- type: dockerhub
username: dockereng
connection_id: ${{ vars.DOCKERHUB_OIDC_CONNECTIONID }}
steps:
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
username: ${{ secrets.DOCKERHUB_CLIBIN_USERNAME }}
password: ${{ secrets.DOCKERHUB_CLIBIN_TOKEN }}
-
name: Set up QEMU
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
-
name: Docker meta
id: meta
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
with:
images: dockereng/cli-bin
tags: |
type=semver,pattern={{version}}
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
-
name: Build and push image
uses: docker/bake-action@6614cfa25eff9a0b2b2697efb0b6159e7680d584 # v7.2.0
with:
files: |
./docker-bake.hcl
cwd://${{ steps.meta.outputs.bake-file }}
targets: bin-image-cross
push: ${{ github.event_name != 'pull_request' }}
set: |
*.cache-from=type=gha,scope=bin-image
*.cache-to=type=gha,scope=bin-image,mode=max
prepare-plugins:
runs-on: ubuntu-24.04
@@ -132,7 +146,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
-
@@ -156,10 +170,10 @@ jobs:
steps:
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
-
name: Build
uses: docker/bake-action@d3418bd7d0e9324001bca92fa8ba175ea7e6dc9b # v7.3.0
uses: docker/bake-action@6614cfa25eff9a0b2b2697efb0b6159e7680d584 # v7.2.0
with:
targets: plugins-cross
set: |
+6 -6
View File
@@ -46,7 +46,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 2
persist-credentials: false
@@ -62,20 +62,20 @@ jobs:
ln -s vendor.sum go.sum
-
name: Update Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: "1.26.8"
go-version: "1.26.4"
cache: false
-
name: Initialize CodeQL
uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
with:
languages: go
-
name: Autobuild
uses: github/codeql-action/autobuild@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
uses: github/codeql-action/autobuild@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
-
name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
with:
category: "/language:go"
+3 -3
View File
@@ -44,7 +44,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
-
@@ -65,7 +65,7 @@ jobs:
docker info
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
-
name: Run ${{ matrix.target }}
run: |
@@ -76,7 +76,7 @@ jobs:
TESTFLAGS: -coverprofile=/tmp/coverage/coverage.txt
-
name: Send to Codecov
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
with:
files: ./build/coverage/coverage.txt
token: ${{ secrets.CODECOV_TOKEN }}
-9
View File
@@ -7,17 +7,8 @@ on:
permissions: {}
# Deduplicate simultaneous pull_request events for the same fork PR.
# When reviewers are requested at the same time, GitHub fires multiple
# review_requested events. Without this group each event triggers a
# separate review via workflow_run, producing duplicate reviews.
concurrency:
group: pr-review-trigger-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
save-context:
if: github.event.pull_request.head.repo.fork
runs-on: ubuntu-latest
steps:
- name: Save event context
+2 -2
View File
@@ -11,13 +11,13 @@ permissions:
jobs:
review:
uses: docker/docker-agent-action/.github/workflows/review-pr.yml@06e1767af06263c93d712449cbf859778d9392ee # v2.0.5
uses: docker/docker-agent-action/.github/workflows/review-pr.yml@e96a4bb40cac114f64358621e1d08346c8eadc8c # v2.0.1
permissions:
contents: read # Read repository files and PR diffs
pull-requests: write # Post review comments
issues: write # Create security incident issues if secrets detected
checks: write # (Optional) Show review progress as a check run
id-token: write # Required for OIDC authentication to AWS Secrets Manager
actions: write # Required since v2.0.3 — review-lock cache cleanup and feedback artifacts
actions: read # Download artifacts from trigger workflow
with:
trigger-run-id: ${{ github.event_name == 'workflow_run' && format('{0}', github.event.workflow_run.id) || '' }}
+11 -31
View File
@@ -1,7 +1,7 @@
name: Sync Docker release branch
concurrency:
group: ${{ github.workflow }}-${{ inputs.release_branch }}
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: false
permissions:
@@ -10,10 +10,6 @@ permissions:
on:
workflow_dispatch:
inputs:
release_branch:
description: Release branch to sync, for example 29.x
required: true
type: string
tag:
description: Tag to sync from, for example v29.6.0
required: true
@@ -29,7 +25,6 @@ jobs:
runs-on: ubuntu-24.04
permissions:
contents: write
actions: write
outputs:
base_sha: ${{ steps.sync.outputs.base_sha }}
has_changes: ${{ steps.sync.outputs.has_changes }}
@@ -38,25 +33,16 @@ jobs:
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
- name: Validate
env:
BRANCH: ${{ github.ref_name }}
RELEASE_BRANCH: ${{ inputs.release_branch }}
run: |
if [ "$BRANCH" != "master" ]; then
echo "::error::This workflow is expected to be run on master, not $BRANCH"
exit 1
fi
if [ "$RELEASE_BRANCH" = "master" ]; then
echo "::error::The release branch must not be master"
exit 1
fi
if ! [[ "$RELEASE_BRANCH" =~ ^[0-9]+\.[x0-9]+$ ]]; then
echo "::error::Invalid release branch name: '$RELEASE_BRANCH'. Expected format: 29.x"
if [ "$BRANCH" = "master" ]; then
echo "::error::This workflow is expected to be run on a release branch, not master"
exit 1
fi
@@ -69,7 +55,7 @@ jobs:
id: sync
env:
DRY_RUN: ${{ inputs.dry_run }}
RELEASE_BRANCH: ${{ inputs.release_branch }}
RELEASE_BRANCH: ${{ github.ref_name }}
RUN_ATTEMPT: ${{ github.run_attempt }}
RUN_ID: ${{ github.run_id }}
TAG: ${{ inputs.tag }}
@@ -80,14 +66,9 @@ jobs:
echo "base_sha=$base_sha" >> "$GITHUB_OUTPUT"
echo "temporary_branch=$temporary_branch" >> "$GITHUB_OUTPUT"
# Keep the master checkout unchanged so scripts run from the dispatched revision.
release_worktree="$RUNNER_TEMP/release-branch"
git worktree add --detach "$release_worktree" "$base_sha"
cd "$release_worktree"
tags_file=$(mktemp)
"$GITHUB_WORKSPACE/scripts/unmerged-tags" \
"origin/$RELEASE_BRANCH" \
scripts/unmerged-tags \
"$RELEASE_BRANCH" \
"$TAG" \
> "$tags_file"
@@ -96,7 +77,7 @@ jobs:
echo >> "$GITHUB_STEP_SUMMARY"
sed 's/^/- /' "$tags_file" >> "$GITHUB_STEP_SUMMARY"
xargs -r "$GITHUB_WORKSPACE/scripts/sync-branch" < "$tags_file" | tee -a "$GITHUB_STEP_SUMMARY"
xargs -r scripts/sync-branch < "$tags_file" | tee -a "$GITHUB_STEP_SUMMARY"
if [[ "$DRY_RUN" == "true" ]]; then
echo "has_changes=false" >> "$GITHUB_OUTPUT"
@@ -120,18 +101,17 @@ jobs:
environment: docker-releases
permissions:
contents: write
actions: write
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Checkout release
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
- name: Push release branch
env:
BASE_SHA: ${{ needs.sync-release-branch.outputs.base_sha }}
RELEASE_BRANCH: ${{ inputs.release_branch }}
RELEASE_BRANCH: ${{ github.ref_name }}
TEMPORARY_BRANCH: ${{ needs.sync-release-branch.outputs.temporary_branch }}
TEMPORARY_SHA: ${{ needs.sync-release-branch.outputs.temporary_sha }}
run: |
+7 -7
View File
@@ -30,15 +30,15 @@ jobs:
steps:
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
-
name: Test
uses: docker/bake-action@d3418bd7d0e9324001bca92fa8ba175ea7e6dc9b # v7.3.0
uses: docker/bake-action@6614cfa25eff9a0b2b2697efb0b6159e7680d584 # v7.2.0
with:
targets: test-coverage
-
name: Send to Codecov
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
with:
files: ./build/coverage/coverage.txt
token: ${{ secrets.CODECOV_TOKEN }}
@@ -60,15 +60,15 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
path: ${{ env.GOPATH }}/src/github.com/docker/cli
persist-credentials: false
-
name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: "1.26.8"
go-version: "1.26.4"
cache: false
-
name: Test
@@ -82,7 +82,7 @@ jobs:
shell: bash
-
name: Send to Codecov
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
with:
files: /tmp/coverage.txt
working-directory: ${{ env.GOPATH }}/src/github.com/docker/cli
+6 -6
View File
@@ -38,7 +38,7 @@ jobs:
steps:
-
name: Run
uses: docker/bake-action@d3418bd7d0e9324001bca92fa8ba175ea7e6dc9b # v7.3.0
uses: docker/bake-action@6614cfa25eff9a0b2b2697efb0b6159e7680d584 # v7.2.0
with:
targets: ${{ matrix.target }}
@@ -48,7 +48,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
-
@@ -76,7 +76,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
-
@@ -93,15 +93,15 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
path: src/github.com/docker/cli
persist-credentials: false
-
name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: "1.26.8"
go-version: "1.26.4"
cache: false
-
name: Run gocompat check
+1 -1
View File
@@ -21,7 +21,7 @@ on:
jobs:
run:
uses: crazy-max/.github/.github/workflows/zizmor.yml@f91220cba053bd54299ba645a599ee8ed436f222 # v1.11.0
uses: crazy-max/.github/.github/workflows/zizmor.yml@46267a6e61cd56aac2fc79943df180152f4c89d6 # v1.10.1
permissions:
contents: read
security-events: write
+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.8"
go: "1.26.4"
timeout: 5m
+1 -1
View File
@@ -267,7 +267,7 @@ Then you just add a line to every git commit message:
Signed-off-by: Joe Smith <joe.smith@email.com>
Use a known identity (sorry, no anonymous contributions.)
Use your real name (sorry, no pseudonyms or anonymous contributions.)
If you set your `user.name` and `user.email` git configs, you can sign your
commit automatically with `git commit -s`.
+1 -1
View File
@@ -8,7 +8,7 @@ ARG BASE_VARIANT=alpine
ARG ALPINE_VERSION=3.23
ARG BASE_DEBIAN_DISTRO=bookworm
ARG GO_VERSION=1.26.8
ARG GO_VERSION=1.26.4
# XX_VERSION specifies the version of the xx utility to use.
# It must be a valid tag in the docker.io/tonistiigi/xx image repository.
+1
View File
@@ -3,6 +3,7 @@
[![PkgGoDev](https://pkg.go.dev/badge/github.com/docker/cli)](https://pkg.go.dev/github.com/docker/cli)
[![Build Status](https://img.shields.io/github/actions/workflow/status/docker/cli/build.yml?branch=master&label=build&logo=github)](https://github.com/docker/cli/actions?query=workflow%3Abuild)
[![Test Status](https://img.shields.io/github/actions/workflow/status/docker/cli/test.yml?branch=master&label=test&logo=github)](https://github.com/docker/cli/actions?query=workflow%3Atest)
[![Go Report Card](https://goreportcard.com/badge/github.com/docker/cli)](https://goreportcard.com/report/github.com/docker/cli)
[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/docker/cli/badge)](https://scorecard.dev/viewer/?uri=github.com/docker/cli)
[![Codecov](https://img.shields.io/codecov/c/github/docker/cli?logo=codecov)](https://codecov.io/gh/docker/cli)
+1 -1
View File
@@ -1 +1 @@
29.8.0
29.6.2
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
// Package hooks defines the contract between the Docker CLI and CLI plugin hook
// implementations.
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package hooks
+1 -2
View File
@@ -3,7 +3,6 @@ package manager
import (
"os"
"path/filepath"
"slices"
"sync"
"testing"
@@ -46,7 +45,7 @@ func TestPluginStubCompletionRestoresOSArgs(t *testing.T) {
t.Cleanup(func() { os.Args = savedArgs })
originalArgs := []string{"docker", "image", "ls"}
os.Args = slices.Clone(originalArgs)
os.Args = append([]string(nil), originalArgs...)
_, directive := cmd.ValidArgsFunction(cmd, []string{"--all"}, "alp")
assert.Equal(t, directive, cobra.ShellCompDirectiveError)
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package manager
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package manager
+3 -6
View File
@@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
package manager
import (
@@ -9,7 +6,7 @@ import (
"os"
"os/exec"
"path/filepath"
slices "slices"
"sort"
"strings"
"sync"
@@ -167,8 +164,8 @@ func ListPlugins(dockerCli config.Provider, rootcmd *cobra.Command) ([]Plugin, e
return nil, err
}
slices.SortFunc(plugins, func(a, b Plugin) int {
return sortorder.NaturalCompare(a.Name, b.Name)
sort.Slice(plugins, func(i, j int) bool {
return sortorder.NaturalLess(plugins[i].Name, plugins[j].Name)
})
return plugins, nil
+2 -4
View File
@@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
package manager
import (
@@ -170,7 +167,8 @@ func (p *Plugin) RunHook(ctx context.Context, hookData hooks.Request) ([]byte, e
out, err := pCmd.Output()
if err != nil {
if _, ok := errors.AsType[*exec.ExitError](err); ok {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
return nil, wrapAsPluginError(err, "plugin hook subcommand exited unsuccessfully")
}
return nil, wrapAsPluginError(err, "failed to execute plugin hook subcommand: "+pCmd.String())
+2 -4
View File
@@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
package plugin
import (
@@ -100,7 +97,8 @@ func Run(makeCmd func(command.Cli) *cobra.Command, meta metadata.Metadata, ops .
plugin := makeCmd(dockerCLI)
if err := RunPlugin(dockerCLI, plugin, meta); err != nil {
if stErr, ok := errors.AsType[cli.StatusError](err); ok {
var stErr cli.StatusError
if errors.As(err, &stErr) {
// StatusError should only be used for errors, and all errors should
// have a non-zero exit status, so never exit with 0
if stErr.StatusCode == 0 { // FIXME(thaJeztah): this should never be used with a zero status-code. Check if we do this anywhere.
+3 -6
View File
@@ -1,12 +1,9 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
package cli
import (
"fmt"
"os"
"slices"
"sort"
"strings"
"github.com/docker/cli/cli-plugins/metadata"
@@ -276,8 +273,8 @@ func topCommands(cmd *cobra.Command) []*cobra.Command {
cmds = append(cmds, sub)
}
}
slices.SortStableFunc(cmds, func(a, b *cobra.Command) int {
return sortorder.NaturalCompare(a.Annotations["category-top"], b.Annotations["category-top"])
sort.SliceStable(cmds, func(i, j int) bool {
return sortorder.NaturalLess(cmds[i].Annotations["category-top"], cmds[j].Annotations["category-top"])
})
return cmds
}
+2 -6
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package command
@@ -316,11 +316,7 @@ func newAPIClientFromEndpoint(ep docker.Endpoint, configFile *configfile.ConfigF
opts = append(opts, withCustomHeaders)
}
opts = append(opts, extraOpts...)
apiClient, err := client.New(opts...)
if err != nil {
return nil, err
}
return apiClient, nil
return client.New(opts...)
}
func resolveDockerEndpoint(s store.Reader, contextName string) (docker.Endpoint, error) {
+15 -42
View File
@@ -132,15 +132,6 @@ func TestNewAPIClientFromFlagsWithAPIVersionFromEnv(t *testing.T) {
assert.Equal(t, apiclient.ClientVersion(), expectedVersion)
}
func TestNewAPIClientFromFlagsWithInvalidAPIVersionFromEnv(t *testing.T) {
t.Setenv("DOCKER_API_VERSION", "1")
t.Setenv("DOCKER_HOST", ":2375")
apiClient, err := NewAPIClientFromFlags(&flags.ClientOptions{}, &configfile.ConfigFile{})
assert.ErrorContains(t, err, "invalid API version")
assert.Check(t, apiClient == nil)
}
type fakeClient struct {
client.Client
pingFunc func() (client.PingResult, error)
@@ -217,70 +208,52 @@ func TestInitializeFromClient(t *testing.T) {
// Makes sure we don't hang forever on the initial connection.
// https://github.com/docker/cli/issues/3652
func TestInitializeFromClientHangs(t *testing.T) {
const (
// Sized against measured scheduler stalls:
// under CPU pressure this test sees 40-90ms stalls;
// this should give about 5x headroom.
// See https://github.com/docker/cli/issues/6003.
clientInitTimeout = 500 * time.Millisecond
// This is only a backstop against a genuine hang.
// It should never be reached on a healthy run.
// So, it should be fine to have a lenient timeout here.
waitTimeout = 10 * time.Second
)
tmpDir := t.TempDir()
socket := filepath.Join(tmpDir, "my.sock")
l, err := net.Listen("unix", socket)
assert.NilError(t, err)
// Buffered, so the handler can record
// that it was reached without a reader
// having to be ready at that instant.
receivedReqCh := make(chan struct{}, 1)
releaseHandlerCh := make(chan struct{})
receiveReqCh := make(chan bool)
timeoutCtx, cancel := context.WithTimeout(context.TODO(), time.Second)
defer cancel()
// Simulate a server that hangs on connections.
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
select {
case receivedReqCh <- struct{}{}:
default:
case <-timeoutCtx.Done():
case receiveReqCh <- true: // Blocks until someone receives on the channel.
}
<-releaseHandlerCh
_, _ = w.Write([]byte("OK"))
}))
ts.Listener = l
ts.Start()
t.Cleanup(func() {
close(releaseHandlerCh)
ts.Close()
})
defer ts.Close()
opts := &flags.ClientOptions{Hosts: []string{"unix://" + socket}}
configFile := &configfile.ConfigFile{}
apiClient, err := NewAPIClientFromFlags(opts, configFile)
assert.NilError(t, err)
initErrCh := make(chan error, 1)
initializedCh := make(chan bool)
go func() {
cli := &DockerCli{client: apiClient, initTimeout: clientInitTimeout}
cli := &DockerCli{client: apiClient, initTimeout: time.Millisecond}
err := cli.Initialize(flags.NewClientOptions())
assert.Check(t, err)
cli.CurrentVersion()
initErrCh <- err
close(initializedCh)
}()
select {
case err := <-initErrCh:
assert.Check(t, err)
case <-time.After(waitTimeout):
case <-timeoutCtx.Done():
t.Fatal("timeout waiting for initialization to complete")
case <-initializedCh:
}
select {
case <-receivedReqCh:
case <-time.After(waitTimeout):
case <-timeoutCtx.Done():
t.Fatal("server never received an init request")
case <-receiveReqCh:
}
}
-22
View File
@@ -172,28 +172,6 @@ func FromList(options ...string) cobra.CompletionFunc {
return Unique(cobra.FixedCompletions(options, cobra.ShellCompDirectiveNoFileComp))
}
// WithPrefix prefixes every element in the slice with the given prefix.
// It is a helper for building "--filter" completions, where each candidate
// value is offered as "key=value".
func WithPrefix(prefix string, values []string) []string {
result := make([]string, len(values))
for i, v := range values {
result[i] = prefix + v
}
return result
}
// WithSuffix appends the given suffix to every element in the slice. It is a
// helper for building "--filter" completions, where filter keys are offered
// with a trailing "=" (combined with [cobra.ShellCompDirectiveNoSpace]).
func WithSuffix(suffix string, values []string) []string {
result := make([]string, len(values))
for i, v := range values {
result[i] = v + suffix
}
return result
}
// FileNames is a convenience function to use [cobra.ShellCompDirectiveDefault],
// which indicates to let the shell perform its default behavior after
// completions have been provided.
+3 -16
View File
@@ -3,9 +3,9 @@ package completion
import (
"context"
"errors"
"sort"
"testing"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/image"
"github.com/moby/moby/api/types/network"
@@ -177,10 +177,9 @@ func TestCompleteEnvVarNames(t *testing.T) {
values, directives := EnvVarNames()(nil, nil, "")
assert.Check(t, is.Equal(directives&cobra.ShellCompDirectiveNoFileComp, cobra.ShellCompDirectiveNoFileComp), "Should not perform file completion")
sort.Strings(values)
expected := []string{"ENV_A", "ENV_B"}
assert.Check(t, is.DeepEqual(values, expected, cmpopts.SortSlices(func(a, b string) bool {
return a < b
})))
assert.Check(t, is.DeepEqual(values, expected))
}
func TestCompleteFileNames(t *testing.T) {
@@ -197,18 +196,6 @@ func TestCompleteFromList(t *testing.T) {
assert.Check(t, is.DeepEqual(values, expected))
}
func TestWithPrefix(t *testing.T) {
assert.Check(t, is.DeepEqual(WithPrefix("node=", []string{"n1", "n2"}), []string{"node=n1", "node=n2"}))
assert.Check(t, is.DeepEqual(WithPrefix("node=", []string{}), []string{}))
assert.Check(t, is.DeepEqual(WithPrefix("", []string{"n1"}), []string{"n1"}))
}
func TestWithSuffix(t *testing.T) {
assert.Check(t, is.DeepEqual(WithSuffix("=", []string{"id", "name"}), []string{"id=", "name="}))
assert.Check(t, is.DeepEqual(WithSuffix("=", []string{}), []string{}))
assert.Check(t, is.DeepEqual(WithSuffix("", []string{"id"}), []string{"id"}))
}
func TestCompleteImageNames(t *testing.T) {
tests := []struct {
doc string
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package config
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package config
+3 -7
View File
@@ -1,11 +1,8 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
package config
import (
"context"
"slices"
"sort"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
@@ -13,7 +10,6 @@ import (
flagsHelper "github.com/docker/cli/cli/flags"
"github.com/docker/cli/opts"
"github.com/fvbommel/sortorder"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -66,8 +62,8 @@ func runList(ctx context.Context, dockerCLI command.Cli, options listOptions) er
}
}
slices.SortFunc(res.Items, func(a, b swarm.Config) int {
return sortorder.NaturalCompare(a.Spec.Name, b.Spec.Name)
sort.Slice(res.Items, func(i, j int) bool {
return sortorder.NaturalLess(res.Items[i].Spec.Name, res.Items[j].Spec.Name)
})
configCtx := formatter.Context{
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package container
+1 -1
View File
@@ -71,7 +71,7 @@ func newCreateCommand(dockerCLI command.Cli) *cobra.Command {
flags.SetInterspersed(false)
flags.StringVar(&options.name, "name", "", "Assign a name to the container")
flags.StringVar(&options.pull, "pull", PullImageMissing, `Pull image before creating ("`+PullImageAlways+`", "`+PullImageMissing+`", "`+PullImageNever+`")`)
flags.StringVar(&options.pull, "pull", PullImageMissing, `Pull image before creating ("`+PullImageAlways+`", "|`+PullImageMissing+`", "`+PullImageNever+`")`)
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Suppress the pull output")
flags.BoolVarP(&options.useAPISocket, "use-api-socket", "", false, "Bind mount Docker API socket and required auth")
_ = flags.SetAnnotation("use-api-socket", "experimentalCLI", nil) // Mark flag as experimental for now.
+4 -4
View File
@@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"runtime"
"sort"
"strings"
"testing"
@@ -14,7 +15,6 @@ import (
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/internal/test"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/system"
"github.com/moby/moby/client"
@@ -284,12 +284,12 @@ func TestCreateContainerWithProxyConfig(t *testing.T) {
"ALL_PROXY=allProxy",
"all_proxy=allProxy",
}
sort.Strings(expected)
fakeCLI := test.NewFakeCli(&fakeClient{
createContainerFunc: func(options client.ContainerCreateOptions) (client.ContainerCreateResult, error) {
assert.DeepEqual(t, options.Config.Env, expected, cmpopts.SortSlices(func(a, b string) bool {
return a < b
}))
sort.Strings(options.Config.Env)
assert.DeepEqual(t, options.Config.Env, expected)
return client.ContainerCreateResult{}, nil
},
})
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package container
+1 -6
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package container
@@ -139,7 +139,6 @@ type containerOptions struct {
runtime string
autoRemove bool
init bool
umask opts.UmaskOpt
annotations *opts.MapOpts
Image string
@@ -314,9 +313,6 @@ func addFlags(flags *pflag.FlagSet) *containerOptions {
flags.Var(&copts.shmSize, "shm-size", "Size of /dev/shm")
flags.StringVar(&copts.utsMode, "uts", "", "UTS namespace to use")
flags.StringVar(&copts.runtime, "runtime", "", "Runtime to use for this container")
flags.Var(&copts.umask, "umask", "Set umask for the container")
flags.SetAnnotation("umask", "version", []string{"1.56"})
flags.SetAnnotation("umask", "ostype", []string{"linux"})
flags.BoolVar(&copts.init, "init", false, "Run an init inside the container that forwards signals and reaps processes")
flags.SetAnnotation("init", "version", []string{"1.25"})
@@ -714,7 +710,6 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
MaskedPaths: maskedPaths,
ReadonlyPaths: readonlyPaths,
Annotations: copts.annotations.GetAll(),
Umask: copts.umask.Value(),
}
if copts.autoRemove && !hostConfig.RestartPolicy.IsNone() {
-13
View File
@@ -154,19 +154,6 @@ func TestParseRunLinks(t *testing.T) {
}
}
func TestParseRunWithoutUmask(t *testing.T) {
_, hostConfig, _, err := parseRun([]string{"ubuntu", "bash"})
assert.NilError(t, err)
assert.Assert(t, hostConfig.Umask == nil)
}
func TestParseRunUmask(t *testing.T) {
_, hostConfig, _, err := parseRun([]string{"--umask", "0022", "ubuntu", "bash"})
assert.NilError(t, err)
assert.Assert(t, hostConfig.Umask != nil)
assert.Equal(t, uint32(0o22), *hostConfig.Umask)
}
func TestParseRunAttach(t *testing.T) {
tests := []struct {
input string
+4 -5
View File
@@ -1,13 +1,10 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
package container
import (
"context"
"fmt"
"net"
"slices"
"sort"
"strings"
"github.com/docker/cli/cli"
@@ -83,7 +80,9 @@ func runPort(ctx context.Context, dockerCli command.Cli, opts *portOptions) erro
}
if len(out) > 0 {
slices.SortFunc(out, sortorder.NaturalCompare)
sort.Slice(out, func(i, j int) bool {
return sortorder.NaturalLess(out[i], out[j])
})
_, _ = fmt.Fprintln(dockerCli.Out(), strings.Join(out, "\n"))
}
+3 -4
View File
@@ -4,11 +4,11 @@ import (
"context"
"errors"
"io"
"sort"
"sync"
"testing"
"github.com/docker/cli/internal/test"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
@@ -87,9 +87,8 @@ func TestRestart(t *testing.T) {
} else {
assert.Check(t, is.Nil(err))
}
assert.Check(t, is.DeepEqual(restarted, tc.restarted, cmpopts.SortSlices(func(a, b string) bool {
return a < b
})))
sort.Strings(restarted)
assert.Check(t, is.DeepEqual(restarted, tc.restarted))
})
}
}
+1
View File
@@ -88,6 +88,7 @@ func runRm(ctx context.Context, dockerCLI command.Cli, opts *rmOptions) error {
for _, name := range opts.containers {
if err := <-errChan; err != nil {
if opts.force && errdefs.IsNotFound(err) {
_, _ = fmt.Fprintln(dockerCLI.Err(), err)
continue
}
errs = append(errs, err)
+3 -6
View File
@@ -4,11 +4,11 @@ import (
"context"
"errors"
"io"
"sort"
"sync"
"testing"
"github.com/docker/cli/internal/test"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
)
@@ -52,11 +52,8 @@ func TestRemoveForce(t *testing.T) {
} else {
assert.NilError(t, err)
}
assert.Equal(t, cli.ErrBuffer().String(), "")
expected := []string{"mycontainer", "nosuchcontainer"}
assert.DeepEqual(t, removed, expected, cmpopts.SortSlices(func(a, b string) bool {
return a < b
}))
sort.Strings(removed)
assert.DeepEqual(t, removed, []string{"mycontainer", "nosuchcontainer"})
})
}
}
+2 -4
View File
@@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
package container
import (
@@ -172,7 +169,8 @@ func RunStart(ctx context.Context, dockerCli command.Cli, opts *StartOptions) er
}
}
if attachErr := <-cErr; attachErr != nil {
if _, ok := errors.AsType[term.EscapeError](attachErr); ok {
var escapeError term.EscapeError
if errors.As(attachErr, &escapeError) {
// The user entered the detach escape sequence.
return nil
}
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package container
+3 -4
View File
@@ -4,11 +4,11 @@ import (
"context"
"errors"
"io"
"sort"
"sync"
"testing"
"github.com/docker/cli/internal/test"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
@@ -88,9 +88,8 @@ func TestStop(t *testing.T) {
} else {
assert.Check(t, is.Nil(err))
}
assert.Check(t, is.DeepEqual(stopped, tc.stopped, cmpopts.SortSlices(func(a, b string) bool {
return a < b
})))
sort.Strings(stopped)
assert.Check(t, is.DeepEqual(stopped, tc.stopped))
})
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package container
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package command
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package context
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package context
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package context
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package context
+4 -4
View File
@@ -1,12 +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.26
//go:build go1.25
package context
import (
"fmt"
"os"
"slices"
"sort"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
@@ -101,8 +101,8 @@ func runList(dockerCli command.Cli, opts *listOptions) error {
Error: errMsg,
})
}
slices.SortFunc(contexts, func(a, b *formatter.ClientContext) int {
return sortorder.NaturalCompare(a.Name, b.Name)
sort.Slice(contexts, func(i, j int) bool {
return sortorder.NaturalLess(contexts[i].Name, contexts[j].Name)
})
if err := format(dockerCli, opts, contexts); err != nil {
return err
+2 -4
View File
@@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
package context
import (
@@ -72,7 +69,8 @@ func parseBool(config map[string]string, name string) (bool, error) {
}
res, err := strconv.ParseBool(strVal)
if err != nil {
if nErr, ok := errors.AsType[*strconv.NumError](err); ok {
var nErr *strconv.NumError
if errors.As(err, &nErr) {
return res, fmt.Errorf("%s: parsing %q: %w", name, nErr.Num, nErr.Err)
}
return res, fmt.Errorf("%s: %w", name, err)
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package command
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package command
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package command
+13 -16
View File
@@ -1,10 +1,7 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
package formatter
import (
"slices"
"sort"
"strconv"
"strings"
"time"
@@ -55,20 +52,20 @@ shared: {{.Shared}}
}
func buildCacheSort(buildCache []build.CacheRecord) {
slices.SortFunc(buildCache, func(a, b build.CacheRecord) int {
sort.Slice(buildCache, func(i, j int) bool {
lui, luj := buildCache[i].LastUsedAt, buildCache[j].LastUsedAt
switch {
case a.LastUsedAt == nil && b.LastUsedAt == nil:
return strings.Compare(a.ID, b.ID)
case a.LastUsedAt == nil:
return -1
case b.LastUsedAt == nil:
return 1
case lui == nil && luj == nil:
return strings.Compare(buildCache[i].ID, buildCache[j].ID) < 0
case lui == nil:
return true
case luj == nil:
return false
case lui.Equal(*luj):
return strings.Compare(buildCache[i].ID, buildCache[j].ID) < 0
default:
return lui.Before(*luj)
}
if c := a.LastUsedAt.Compare(*b.LastUsedAt); c != 0 {
return c
}
return strings.Compare(a.ID, b.ID)
})
}
+20 -14
View File
@@ -1,13 +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.26
//go:build go1.25
package formatter
import (
"cmp"
"fmt"
"net"
"slices"
"sort"
"strconv"
"strings"
"time"
@@ -296,7 +295,7 @@ func (c *ContainerContext) Labels() string {
for k, v := range c.c.Labels {
joinLabels = append(joinLabels, k+"="+v)
}
slices.Sort(joinLabels)
sort.Strings(joinLabels)
return strings.Join(joinLabels, ",")
}
@@ -396,7 +395,9 @@ func DisplayablePorts(ports []container.PortSummary) string {
var result []string
var hostMappings []string
var groupMapKeys []string
slices.SortFunc(ports, comparePorts)
sort.Slice(ports, func(i, j int) bool {
return comparePorts(ports[i], ports[j])
})
for _, port := range ports {
current := port.PrivatePort
@@ -451,13 +452,18 @@ func formGroup(key string, start, last uint16) string {
return group + "/" + groupType
}
// comparePorts compares ports by private port, IP address, public port,
// and protocol, in that order.
func comparePorts(a, b container.PortSummary) int {
return cmp.Or(
cmp.Compare(a.PrivatePort, b.PrivatePort),
a.IP.Compare(b.IP),
cmp.Compare(a.PublicPort, b.PublicPort),
cmp.Compare(a.Type, b.Type),
)
func comparePorts(i, j container.PortSummary) bool {
if i.PrivatePort != j.PrivatePort {
return i.PrivatePort < j.PrivatePort
}
if i.IP != j.IP {
return i.IP.String() < j.IP.String()
}
if i.PublicPort != j.PublicPort {
return i.PublicPort < j.PublicPort
}
return i.Type < j.Type
}
+1 -19
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package formatter
@@ -946,24 +946,6 @@ func TestDisplayablePorts(t *testing.T) {
},
expected: "80/tcp, 80/udp, 1024/tcp, 1024/udp, 12345/sctp, 1.1.1.1:1024->80/tcp, 1.1.1.1:1024->80/udp, 2.1.1.1:1024->80/tcp, 2.1.1.1:1024->80/udp, 1.1.1.1:80->1024/tcp, 1.1.1.1:80->1024/udp, 2.1.1.1:80->1024/tcp, 2.1.1.1:80->1024/udp", //nolint:revive // ignore line-length-limit (revive)
},
{
// host IPs are ordered numerically, not lexicographically:
// "10.0.0.2" sorts as a string before "9.0.0.1".
ports: []container.PortSummary{
{
IP: netip.MustParseAddr("10.0.0.2"),
PublicPort: 8080,
PrivatePort: 80,
Type: "tcp",
}, {
IP: netip.MustParseAddr("9.0.0.1"),
PublicPort: 8081,
PrivatePort: 80,
Type: "tcp",
},
},
expected: "9.0.0.1:8081->80/tcp, 10.0.0.2:8080->80/tcp",
},
}
for _, port := range cases {
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package formatter
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package formatter
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package formatter
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package formatter
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package formatter
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package formatter
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package formatter
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package formatter
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package idresolver
+2 -4
View File
@@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
package image
import (
@@ -367,7 +364,8 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
err = jsonstream.Display(ctx, response.Body, streams.NewOut(buildBuff), jsonstream.WithAuxCallback(aux))
if err != nil {
if jerr, ok := errors.AsType[*jsonstream.JSONError](err); ok {
var jerr *jsonstream.JSONError
if errors.As(err, &jerr) {
// If no error code is set, default to 1
if jerr.Code == 0 {
jerr.Code = 1
@@ -1,6 +1,3 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
package git
import (
@@ -10,7 +7,6 @@ import (
"os"
"os/exec"
"path/filepath"
"slices"
"strings"
"github.com/moby/sys/symlink"
@@ -206,7 +202,7 @@ func (repo gitRepo) checkout(root string) (string, error) {
}
func (repo gitRepo) gitWithinDir(dir string, args ...string) ([]byte, error) {
args = slices.Concat([]string{"-c", "protocol.file.allow=never"}, args) // Block sneaky repositories from using repos from the filesystem as submodules.
args = append([]string{"-c", "protocol.file.allow=never"}, args...) // Block sneaky repositories from using repos from the filesystem as submodules.
cmd := exec.Command("git", args...)
cmd.Dir = dir
// Disable unsafe remote protocols.
+2 -2
View File
@@ -8,7 +8,7 @@ import (
"io"
"os"
"path/filepath"
"slices"
"sort"
"testing"
"github.com/docker/cli/cli/streams"
@@ -211,6 +211,6 @@ func (f *fakeBuild) filenames(t *testing.T) []string {
for _, header := range h {
names = append(names, header.Name)
}
slices.Sort(names)
sort.Strings(names)
return names
}
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package image
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package image
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package image
+3 -5
View File
@@ -102,11 +102,9 @@ func TestRunPushRespectsNoColorForAuxNotes(t *testing.T) {
SelectedManifest: ocispec.Descriptor{Digest: "sha256:2222222222222222222222222222222222222222222222222222222222222222"},
})
assert.NilError(t, err)
var buf bytes.Buffer
buf.WriteString(`{"aux":`)
buf.Write(aux)
buf.WriteString("}\n")
return fakeStreamResult{ReadCloser: io.NopCloser(&buf)}, nil
line := append([]byte(`{"aux":`), aux...)
line = append(line, '}', '\n')
return fakeStreamResult{ReadCloser: io.NopCloser(bytes.NewReader(line))}, nil
},
})
cli.Out().SetIsTerminal(true)
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package image
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package inspect
+24 -2
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package manifest
@@ -48,6 +48,27 @@ func newManifestStore(dockerCLI command.Cli) store.Store {
return store.NewStore(filepath.Join(config.Dir(), "manifests"))
}
// authConfigKey is the key used to store credentials for Docker Hub. It is
// a copy of [registry.IndexServer].
//
// [registry.IndexServer]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/registry#IndexServer
const authConfigKey = "https://index.docker.io/v1/"
// getAuthConfigKey special-cases using the full index address of the official
// index as the AuthConfig key, and uses the (host)name[:port] for private indexes.
//
// It is similar to [registry.GetAuthConfigKey], but does not require on
// [registrytypes.IndexInfo] as intermediate.
//
// [registry.GetAuthConfigKey]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/registry#GetAuthConfigKey
// [registrytypes.IndexInfo]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/api/types/registry#IndexInfo
func getAuthConfigKey(domainName string) string {
if domainName == "docker.io" || domainName == "index.docker.io" {
return authConfigKey
}
return domainName
}
// newRegistryClient returns a client for communicating with a Docker distribution
// registry
func newRegistryClient(dockerCLI command.Cli, allowInsecure bool) registryclient.RegistryClient {
@@ -57,7 +78,8 @@ func newRegistryClient(dockerCLI command.Cli, allowInsecure bool) registryclient
}
cfg := dockerCLI.ConfigFile()
resolver := func(ctx context.Context, domainName string) registry.AuthConfig {
a, _ := cfg.GetAuthConfig(domainName)
configKey := getAuthConfigKey(domainName)
a, _ := cfg.GetAuthConfig(configKey)
return registry.AuthConfig{
Username: a.Username,
Password: a.Password,
+10 -6
View File
@@ -189,6 +189,8 @@ func TestNetworkCreateWithFlags(t *testing.T) {
// TestNetworkCreateIPv4 verifies behavior of the "--ipv4" option. This option
// is an optional bool, and must default to "nil", not "true" or "false".
func TestNetworkCreateIPv4(t *testing.T) {
boolPtr := func(val bool) *bool { return &val }
tests := []struct {
doc, name string
flags []string
@@ -203,19 +205,19 @@ func TestNetworkCreateIPv4(t *testing.T) {
doc: "IPv4 enabled",
name: "ipv4-enabled",
flags: []string{"--ipv4=true"},
expected: new(true),
expected: boolPtr(true),
},
{
doc: "IPv4 enabled (shorthand)",
name: "ipv4-enabled-shorthand",
flags: []string{"--ipv4"},
expected: new(true),
expected: boolPtr(true),
},
{
doc: "IPv4 disabled",
name: "ipv4-disabled",
flags: []string{"--ipv4=false"},
expected: new(false),
expected: boolPtr(false),
},
}
@@ -241,6 +243,8 @@ func TestNetworkCreateIPv4(t *testing.T) {
// TestNetworkCreateIPv6 verifies behavior of the "--ipv6" option. This option
// is an optional bool, and must default to "nil", not "true" or "false".
func TestNetworkCreateIPv6(t *testing.T) {
strPtr := func(val bool) *bool { return &val }
tests := []struct {
doc, name string
flags []string
@@ -255,19 +259,19 @@ func TestNetworkCreateIPv6(t *testing.T) {
doc: "IPV6 enabled",
name: "ipv6-enabled",
flags: []string{"--ipv6=true"},
expected: new(true),
expected: strPtr(true),
},
{
doc: "IPV6 enabled (shorthand)",
name: "ipv6-enabled-shorthand",
flags: []string{"--ipv6"},
expected: new(true),
expected: strPtr(true),
},
{
doc: "IPV6 disabled",
name: "ipv6-disabled",
flags: []string{"--ipv6=false"},
expected: new(false),
expected: strPtr(false),
},
}
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package network
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package network
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package network
+3 -7
View File
@@ -1,11 +1,8 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
package network
import (
"context"
"slices"
"sort"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
@@ -13,7 +10,6 @@ import (
flagsHelper "github.com/docker/cli/cli/flags"
"github.com/docker/cli/opts"
"github.com/fvbommel/sortorder"
"github.com/moby/moby/api/types/network"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -65,8 +61,8 @@ func runList(ctx context.Context, dockerCLI command.Cli, options listOptions) er
}
}
slices.SortFunc(res.Items, func(a, b network.Summary) int {
return sortorder.NaturalCompare(a.Name, b.Name)
sort.Slice(res.Items, func(i, j int) bool {
return sortorder.NaturalLess(res.Items[i].Name, res.Items[j].Name)
})
networksCtx := formatter.Context{
-34
View File
@@ -2,26 +2,12 @@ package node
import (
"os"
"strings"
"github.com/docker/cli/cli/command/completion"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
var (
// nodePsFilters are the filters that can be used with "docker node ps --filter".
nodePsFilters = []string{"desired-state", "id", "label", "name"}
// taskDesiredStates are the valid values for the "desired-state" task filter.
taskDesiredStates = []string{
string(swarm.TaskStateRunning),
string(swarm.TaskStateShutdown),
string(swarm.TaskStateAccepted),
}
)
// completeNodeNames offers completion for swarm node (host)names and optional IDs.
// By default, only names are returned.
// Set DOCKER_COMPLETION_SHOW_NODE_IDS=yes to also complete IDs.
@@ -49,23 +35,3 @@ func completeNodeNames(dockerCLI completion.APIClientProvider) cobra.CompletionF
return names, cobra.ShellCompDirectiveNoFileComp
}
}
// completeNodePsFilters provides completion for the filters that can be used
// with "docker node ps --filter".
func completeNodePsFilters(_ completion.APIClientProvider) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
key, _, ok := strings.Cut(toComplete, "=")
if !ok {
return completion.WithSuffix("=", nodePsFilters), cobra.ShellCompDirectiveNoSpace
}
switch key {
case "desired-state":
return completion.WithPrefix("desired-state=", taskDesiredStates), cobra.ShellCompDirectiveNoFileComp
case "id", "name", "label":
// Task IDs, names, and labels are not easily discoverable; only offer the key.
return nil, cobra.ShellCompDirectiveNoFileComp
default:
return completion.WithSuffix("=", nodePsFilters), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
}
}
}
-52
View File
@@ -1,52 +0,0 @@
package node
import (
"testing"
"github.com/docker/cli/internal/test"
"github.com/spf13/cobra"
"gotest.tools/v3/assert"
)
func TestCompleteNodePsFilters(t *testing.T) {
tests := []struct {
doc string
toComplete string
expected []string
directive cobra.ShellCompDirective
}{
{
doc: "no input offers the filter keys",
toComplete: "",
expected: []string{"desired-state=", "id=", "label=", "name="},
directive: cobra.ShellCompDirectiveNoSpace,
},
{
doc: "desired-state values",
toComplete: "desired-state=",
expected: []string{"desired-state=running", "desired-state=shutdown", "desired-state=accepted"},
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "label offers no values",
toComplete: "label=",
expected: nil,
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "unknown key falls back to the filter keys",
toComplete: "bogus=",
expected: []string{"desired-state=", "id=", "label=", "name="},
directive: cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp,
},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{})
completions, directive := completeNodePsFilters(cli)(newPsCommand(cli), nil, tc.toComplete)
assert.DeepEqual(t, completions, tc.expected)
assert.Equal(t, directive, tc.directive)
})
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package node
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package node
+3 -7
View File
@@ -1,11 +1,8 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
package node
import (
"context"
"slices"
"sort"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
@@ -13,7 +10,6 @@ import (
flagsHelper "github.com/docker/cli/cli/flags"
"github.com/docker/cli/opts"
"github.com/fvbommel/sortorder"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -77,8 +73,8 @@ func runList(ctx context.Context, dockerCLI command.Cli, options listOptions) er
Output: dockerCLI.Out(),
Format: newFormat(format, options.quiet),
}
slices.SortFunc(res.Items, func(a, b swarm.Node) int {
return sortorder.NaturalCompare(a.Description.Hostname, b.Description.Hostname)
sort.Slice(res.Items, func(i, j int) bool {
return sortorder.NaturalLess(res.Items[i].Description.Hostname, res.Items[j].Description.Hostname)
})
return formatWrite(nodesCtx, res, info)
}
-2
View File
@@ -48,8 +48,6 @@ func newPsCommand(dockerCLI command.Cli) *cobra.Command {
flags.StringVar(&options.format, "format", "", "Pretty-print tasks using a Go template")
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Only display task IDs")
_ = cmd.RegisterFlagCompletionFunc("filter", completeNodePsFilters(dockerCLI))
return cmd
}
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package node
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package plugin
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package plugin
+3 -7
View File
@@ -1,11 +1,8 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
package plugin
import (
"context"
"slices"
"sort"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
@@ -13,7 +10,6 @@ import (
flagsHelper "github.com/docker/cli/cli/flags"
"github.com/docker/cli/opts"
"github.com/fvbommel/sortorder"
"github.com/moby/moby/api/types/plugin"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -58,8 +54,8 @@ func runList(ctx context.Context, dockerCli command.Cli, options listOptions) er
return err
}
slices.SortFunc(resp.Items, func(a, b plugin.Plugin) int {
return sortorder.NaturalCompare(a.Name, b.Name)
sort.Slice(resp.Items, func(i, j int) bool {
return sortorder.NaturalLess(resp.Items[i].Name, resp.Items[j].Name)
})
format := options.format
+7 -12
View File
@@ -97,22 +97,17 @@ func GetDefaultAuthConfig(cfg *configfile.ConfigFile, checkCredStore bool, serve
// If defaultUsername is not empty, the username prompt includes that username
// and the user can hit enter without inputting a username to use that default
// username.
func PromptUserForCredentials(ctx context.Context, cli Streams, argUser, argPassword, defaultUsername, serverAddress string) (registrytypes.AuthConfig, error) {
func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword, defaultUsername, serverAddress string) (registrytypes.AuthConfig, error) {
// On Windows, force the use of the regular OS stdin stream.
//
// StdStreams() may wrap stdin with windowsconsole.NewAnsiReader to
// emulate VT input on consoles that do not support it natively, but
// that wrapper has historically caused interactive prompts to hang
// or behave incorrectly.
//
// See:
// - https://github.com/moby/moby/issues/14336
// - https://github.com/moby/moby/issues/14210
// - https://github.com/moby/moby/pull/17738
stdIn := cli.In()
//
// TODO(thaJeztah): we need to confirm if this special handling is still needed, as we may not be doing this in other places.
if runtime.GOOS == "windows" {
// TODO(thaJeztah); change to io.Reader and skip wrapping once prompt.DisableInputEcho no longer requires a streams.In
stdIn = streams.NewIn(os.Stdin)
cli.SetIn(streams.NewIn(os.Stdin))
}
argUser = strings.TrimSpace(argUser)
@@ -137,7 +132,7 @@ func PromptUserForCredentials(ctx context.Context, cli Streams, argUser, argPass
}
var err error
argUser, err = prompt.ReadInput(ctx, stdIn, cli.Out(), msg)
argUser, err = prompt.ReadInput(ctx, cli.In(), cli.Out(), msg)
if err != nil {
return registrytypes.AuthConfig{}, err
}
@@ -151,7 +146,7 @@ func PromptUserForCredentials(ctx context.Context, cli Streams, argUser, argPass
isEmpty := strings.TrimSpace(argPassword) == ""
if isEmpty {
restoreInput, err := prompt.DisableInputEcho(stdIn)
restoreInput, err := prompt.DisableInputEcho(cli.In())
if err != nil {
return registrytypes.AuthConfig{}, err
}
@@ -171,7 +166,7 @@ func PromptUserForCredentials(ctx context.Context, cli Streams, argUser, argPass
"To create a PAT, visit " + aec.Underline.Apply("https://app.docker.com/settings") + "\n\n")
}
argPassword, err = prompt.ReadInput(ctx, stdIn, cli.Out(), "Password: ")
argPassword, err = prompt.ReadInput(ctx, cli.In(), cli.Out(), "Password: ")
if err != nil {
return registrytypes.AuthConfig{}, err
}
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package secret
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package secret
+3 -7
View File
@@ -1,11 +1,8 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
package secret
import (
"context"
"slices"
"sort"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
@@ -13,7 +10,6 @@ import (
flagsHelper "github.com/docker/cli/cli/flags"
"github.com/docker/cli/opts"
"github.com/fvbommel/sortorder"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -63,8 +59,8 @@ func runSecretList(ctx context.Context, dockerCLI command.Cli, options listOptio
}
}
slices.SortFunc(res.Items, func(a, b swarm.Secret) int {
return sortorder.NaturalCompare(a.Spec.Name, b.Spec.Name)
sort.Slice(res.Items, func(i, j int) bool {
return sortorder.NaturalLess(res.Items[i].Spec.Name, res.Items[j].Spec.Name)
})
secretCtx := formatter.Context{
-91
View File
@@ -2,32 +2,12 @@ package service
import (
"os"
"strings"
"github.com/docker/cli/cli/command/completion"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
var (
// serviceListFilters are the filters that can be used with "docker service ls --filter".
serviceListFilters = []string{"id", "label", "mode", "name"}
// serviceModes are the valid values for the "mode" filter of "docker service ls".
serviceModes = []string{"replicated", "global", "replicated-job", "global-job"}
// servicePsFilters are the filters that can be used with "docker service ps --filter".
servicePsFilters = []string{"desired-state", "id", "name", "node"}
// taskDesiredStates are the valid values for the "desired-state" task filter.
taskDesiredStates = []string{
string(swarm.TaskStateRunning),
string(swarm.TaskStateShutdown),
string(swarm.TaskStateAccepted),
}
)
// completeServiceNames offers completion for swarm service names and optional IDs.
// By default, only names are returned.
// Set DOCKER_COMPLETION_SHOW_SERVICE_IDS=yes to also complete IDs.
@@ -51,74 +31,3 @@ func completeServiceNames(dockerCLI completion.APIClientProvider) cobra.Completi
return names, cobra.ShellCompDirectiveNoFileComp
}
}
// completeServiceListFilters provides completion for the filters that can be
// used with "docker service ls --filter".
func completeServiceListFilters(dockerCLI completion.APIClientProvider) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
key, _, ok := strings.Cut(toComplete, "=")
if !ok {
return completion.WithSuffix("=", serviceListFilters), cobra.ShellCompDirectiveNoSpace
}
switch key {
case "id", "name":
return completion.WithPrefix(key+"=", serviceNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp
case "mode":
return completion.WithPrefix("mode=", serviceModes), cobra.ShellCompDirectiveNoFileComp
case "label":
return nil, cobra.ShellCompDirectiveNoFileComp
default:
return completion.WithSuffix("=", serviceListFilters), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
}
}
}
// completeServicePsFilters provides completion for the filters that can be
// used with "docker service ps --filter".
func completeServicePsFilters(dockerCLI completion.APIClientProvider) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
key, _, ok := strings.Cut(toComplete, "=")
if !ok {
return completion.WithSuffix("=", servicePsFilters), cobra.ShellCompDirectiveNoSpace
}
switch key {
case "desired-state":
return completion.WithPrefix("desired-state=", taskDesiredStates), cobra.ShellCompDirectiveNoFileComp
case "node":
return completion.WithPrefix("node=", nodeNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp
case "id", "name":
// Task IDs and names are not easily discoverable; only offer the key.
return nil, cobra.ShellCompDirectiveNoFileComp
default:
return completion.WithSuffix("=", servicePsFilters), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
}
}
}
// serviceNames contacts the API to get a list of service names.
// In case of an error, an empty list is returned.
func serviceNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
res, err := dockerCLI.Client().ServiceList(cmd.Context(), client.ServiceListOptions{})
if err != nil {
return []string{}
}
names := make([]string, 0, len(res.Items))
for _, service := range res.Items {
names = append(names, service.Spec.Name)
}
return names
}
// nodeNames contacts the API to get a list of node (host)names.
// In case of an error, an empty list is returned.
func nodeNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
res, err := dockerCLI.Client().NodeList(cmd.Context(), client.NodeListOptions{})
if err != nil {
return []string{}
}
names := make([]string, 0, len(res.Items))
for _, node := range res.Items {
names = append(names, node.Description.Hostname)
}
return names
}
-156
View File
@@ -1,156 +0,0 @@
package service
import (
"context"
"errors"
"testing"
"github.com/docker/cli/internal/test"
"github.com/docker/cli/internal/test/builders"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
"gotest.tools/v3/assert"
)
func TestCompleteServicePsFilters(t *testing.T) {
tests := []struct {
doc string
client *fakeClient
toComplete string
expected []string
directive cobra.ShellCompDirective
}{
{
doc: "no input offers the filter keys",
toComplete: "",
expected: []string{"desired-state=", "id=", "name=", "node="},
directive: cobra.ShellCompDirectiveNoSpace,
},
{
doc: "desired-state values",
toComplete: "desired-state=",
expected: []string{"desired-state=running", "desired-state=shutdown", "desired-state=accepted"},
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "node values",
client: &fakeClient{
nodeListFunc: func(_ context.Context, _ client.NodeListOptions) (client.NodeListResult, error) {
return client.NodeListResult{
Items: []swarm.Node{
*builders.Node(builders.Hostname("n1")),
*builders.Node(builders.Hostname("n2")),
},
}, nil
},
},
toComplete: "node=",
expected: []string{"node=n1", "node=n2"},
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "node values on API error",
client: &fakeClient{
nodeListFunc: func(_ context.Context, _ client.NodeListOptions) (client.NodeListResult, error) {
return client.NodeListResult{}, errors.New("API error")
},
},
toComplete: "node=",
expected: []string{},
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "id offers no values",
toComplete: "id=",
expected: nil,
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "unknown key falls back to the filter keys",
toComplete: "bogus=",
expected: []string{"desired-state=", "id=", "name=", "node="},
directive: cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp,
},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
cli := test.NewFakeCli(tc.client)
completions, directive := completeServicePsFilters(cli)(newPsCommand(cli), nil, tc.toComplete)
assert.DeepEqual(t, completions, tc.expected)
assert.Equal(t, directive, tc.directive)
})
}
}
func TestCompleteServiceListFilters(t *testing.T) {
tests := []struct {
doc string
client *fakeClient
toComplete string
expected []string
directive cobra.ShellCompDirective
}{
{
doc: "no input offers the filter keys",
toComplete: "",
expected: []string{"id=", "label=", "mode=", "name="},
directive: cobra.ShellCompDirectiveNoSpace,
},
{
doc: "mode values",
toComplete: "mode=",
expected: []string{"mode=replicated", "mode=global", "mode=replicated-job", "mode=global-job"},
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "name values",
client: &fakeClient{
serviceListFunc: func(_ context.Context, _ client.ServiceListOptions) (client.ServiceListResult, error) {
return client.ServiceListResult{
Items: []swarm.Service{
*builders.Service(builders.ServiceName("s1")),
*builders.Service(builders.ServiceName("s2")),
},
}, nil
},
},
toComplete: "name=",
expected: []string{"name=s1", "name=s2"},
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "name values on API error",
client: &fakeClient{
serviceListFunc: func(_ context.Context, _ client.ServiceListOptions) (client.ServiceListResult, error) {
return client.ServiceListResult{}, errors.New("API error")
},
},
toComplete: "name=",
expected: []string{},
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "label offers no values",
toComplete: "label=",
expected: nil,
directive: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "unknown key falls back to the filter keys",
toComplete: "bogus=",
expected: []string{"id=", "label=", "mode=", "name="},
directive: cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp,
},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
cli := test.NewFakeCli(tc.client)
completions, directive := completeServiceListFilters(cli)(newListCommand(cli), nil, tc.toComplete)
assert.DeepEqual(t, completions, tc.expected)
assert.Equal(t, directive, tc.directive)
})
}
}
+12 -15
View File
@@ -1,13 +1,9 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
package service
import (
"cmp"
"errors"
"fmt"
"slices"
"sort"
"strconv"
"strings"
"time"
@@ -619,8 +615,8 @@ func NewListFormat(source string, quiet bool) formatter.Format {
// ListFormatWrite writes the context
func ListFormatWrite(ctx formatter.Context, services client.ServiceListResult) error {
render := func(format func(subContext formatter.SubContext) error) error {
slices.SortFunc(services.Items, func(a, b swarm.Service) int {
return sortorder.NaturalCompare(a.Spec.Name, b.Spec.Name)
sort.Slice(services.Items, func(i, j int) bool {
return sortorder.NaturalLess(services.Items[i].Spec.Name, services.Items[j].Spec.Name)
})
for _, service := range services.Items {
serviceCtx := &serviceContext{service: service}
@@ -779,16 +775,17 @@ func (c *serviceContext) Ports() string {
return ""
}
// Sort by protocol first, then by published port.
slices.SortFunc(c.service.Endpoint.Ports, func(a, b swarm.PortConfig) int {
return cmp.Or(
cmp.Compare(a.Protocol, b.Protocol),
cmp.Compare(a.PublishedPort, b.PublishedPort),
)
pr := portRange{}
ports := []string{}
servicePorts := c.service.Endpoint.Ports
sort.Slice(servicePorts, func(i, j int) bool {
if servicePorts[i].Protocol == servicePorts[j].Protocol {
return servicePorts[i].PublishedPort < servicePorts[j].PublishedPort
}
return servicePorts[i].Protocol < servicePorts[j].Protocol
})
var pr portRange
var ports []string
for _, p := range c.service.Endpoint.Ports {
if p.PublishMode == swarm.PortConfigPublishModeIngress {
prIsRange := pr.tEnd != pr.tStart
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.26
//go:build go1.25
package service

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