From 9b51892b8061ff03e91e8aad41e86cad21057272 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 6 Mar 2026 13:52:18 +0100 Subject: [PATCH 1/8] update to go1.25.8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 (cherry picked from commit f7d83cbae878257edce743b95355eac78d846ae8) Signed-off-by: Sebastiaan van Stijn --- .github/workflows/codeql.yml | 2 +- .github/workflows/test.yml | 2 +- .golangci.yml | 2 +- Dockerfile | 2 +- dockerfiles/Dockerfile.dev | 2 +- dockerfiles/Dockerfile.lint | 2 +- dockerfiles/Dockerfile.vendor | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index cf7dcf1742df..b1b430a3680d 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -63,7 +63,7 @@ jobs: name: Update Go uses: actions/setup-go@v6 with: - go-version: "1.25.7" + go-version: "1.25.8" - name: Initialize CodeQL uses: github/codeql-action/init@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 899bc0143de1..1be65deccad0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -67,7 +67,7 @@ jobs: name: Set up Go uses: actions/setup-go@v6 with: - go-version: "1.25.7" + go-version: "1.25.8" - name: Test run: | diff --git a/.golangci.yml b/.golangci.yml index 764185372160..8024a7864f17 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -5,7 +5,7 @@ run: # which causes it to fallback to go1.17 semantics. # # TODO(thaJeztah): update "usetesting" settings to enable go1.24 features once our minimum version is go1.24 - go: "1.25.7" + go: "1.25.8" timeout: 5m diff --git a/Dockerfile b/Dockerfile index 49f3d203af35..45a582c23c68 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ ARG BASE_VARIANT=alpine ARG ALPINE_VERSION=3.23 ARG BASE_DEBIAN_DISTRO=bookworm -ARG GO_VERSION=1.25.7 +ARG GO_VERSION=1.25.8 # XX_VERSION specifies the version of the xx utility to use. # It must be a valid tag in the docker.io/tonistiigi/xx image repository. diff --git a/dockerfiles/Dockerfile.dev b/dockerfiles/Dockerfile.dev index 8e172845cb58..4493c22138e7 100644 --- a/dockerfiles/Dockerfile.dev +++ b/dockerfiles/Dockerfile.dev @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 -ARG GO_VERSION=1.25.7 +ARG GO_VERSION=1.25.8 # ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image. # It must be a supported tag in the docker.io/library/alpine image repository diff --git a/dockerfiles/Dockerfile.lint b/dockerfiles/Dockerfile.lint index 1e996bdc5bca..24da2e13ba7f 100644 --- a/dockerfiles/Dockerfile.lint +++ b/dockerfiles/Dockerfile.lint @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 -ARG GO_VERSION=1.25.7 +ARG GO_VERSION=1.25.8 # ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image. # It must be a supported tag in the docker.io/library/alpine image repository diff --git a/dockerfiles/Dockerfile.vendor b/dockerfiles/Dockerfile.vendor index 566548f3e3c9..ed3ab0de93c8 100644 --- a/dockerfiles/Dockerfile.vendor +++ b/dockerfiles/Dockerfile.vendor @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 -ARG GO_VERSION=1.25.7 +ARG GO_VERSION=1.25.8 # ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image. # It must be a supported tag in the docker.io/library/alpine image repository From 6d71967120b4cdbb556211c7a2514c42dcb2e582 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 10 Mar 2026 11:18:28 +0100 Subject: [PATCH 2/8] cli/command: add missing "go:build" comments - commit e8dc2fce32f2b0cfc6e5e04599bcdab363dbc022 modernized loops to range over int, which requires go1.22 or later. - commit 85ebca52fd8d711dc0303e6872392bb2b03e0bea modernized code to use stdlib min/max, which requires go1.21 or later. Signed-off-by: Sebastiaan van Stijn (cherry picked from commit 9a471180cb7d39c236d090399a9d362c3f5a8ebd) Signed-off-by: Sebastiaan van Stijn --- cli/command/container/opts.go | 2 +- cli/command/container/tty.go | 3 +++ cli/command/service/progress/progress.go | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/cli/command/container/opts.go b/cli/command/container/opts.go index 9caa830a0d7b..c4dcea38e9e4 100644 --- a/cli/command/container/opts.go +++ b/cli/command/container/opts.go @@ -1,4 +1,4 @@ -// FIXME(vvoland): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: //go:build go1.24 package container diff --git a/cli/command/container/tty.go b/cli/command/container/tty.go index 89352f789851..a9a0b70ec364 100644 --- a/cli/command/container/tty.go +++ b/cli/command/container/tty.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.24 + package container import ( diff --git a/cli/command/service/progress/progress.go b/cli/command/service/progress/progress.go index 94b59d950eea..fc6ff7eb52f5 100644 --- a/cli/command/service/progress/progress.go +++ b/cli/command/service/progress/progress.go @@ -1,3 +1,6 @@ +// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: +//go:build go1.24 + package progress import ( From af45a23fa9a06cb282294054d213b9b0fbae1ac7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2026 08:42:41 +0000 Subject: [PATCH 3/8] 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] (cherry picked from commit 6453c4c3a7e55d2fbfc98e66116ae416d5b75452) Signed-off-by: Sebastiaan van Stijn --- .github/workflows/build.yml | 6 +++--- .github/workflows/test.yml | 2 +- .github/workflows/validate.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f12472c678f5..da3ab4487d3b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -66,7 +66,7 @@ jobs: uses: docker/setup-buildx-action@v4 - name: Build - uses: docker/bake-action@v6 + uses: docker/bake-action@v7 with: targets: ${{ matrix.target }} set: | @@ -125,7 +125,7 @@ jobs: type=semver,pattern={{major}}.{{minor}} - name: Build and push image - uses: docker/bake-action@v6 + uses: docker/bake-action@v7 with: files: | ./docker-bake.hcl @@ -168,7 +168,7 @@ jobs: uses: docker/setup-buildx-action@v4 - name: Build - uses: docker/bake-action@v6 + uses: docker/bake-action@v7 with: targets: plugins-cross set: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 899bc0143de1..2a8eed8a24ed 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,7 +33,7 @@ jobs: uses: docker/setup-buildx-action@v4 - name: Test - uses: docker/bake-action@v6 + uses: docker/bake-action@v7 with: targets: test-coverage - diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index e6508d8fc783..3e1e64150313 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -38,7 +38,7 @@ jobs: steps: - name: Run - uses: docker/bake-action@v6 + uses: docker/bake-action@v7 with: targets: ${{ matrix.target }} From 9709c8fe08f902f0aa41ffffaa00085ab39aecd8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2026 08:42:45 +0000 Subject: [PATCH 4/8] 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] (cherry picked from commit 39d676c72d113929fe9e49a902810135545d1dd0) Signed-off-by: Sebastiaan van Stijn --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index da3ab4487d3b..68202ef30730 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -114,7 +114,7 @@ jobs: - name: Docker meta id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: dockereng/cli-bin tags: | From 0da6a5121f81387dc7eeba18f928c7e6f3d5cd9d Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 25 Mar 2026 14:29:11 +0100 Subject: [PATCH 5/8] ci: pin actions to digests As a follow-up, we should use the full version (major.minor.patch). Signed-off-by: Sebastiaan van Stijn (cherry picked from commit 97b9e04a940c3e83fcd588f8442204f2e7e0c5c4) Signed-off-by: Sebastiaan van Stijn --- .github/dependabot.yml | 2 ++ .github/workflows/build.yml | 24 ++++++++++++------------ .github/workflows/codeql.yml | 11 ++++++----- .github/workflows/e2e.yml | 6 +++--- .github/workflows/test.yml | 13 +++++++------ .github/workflows/validate.yml | 6 +++--- 6 files changed, 33 insertions(+), 29 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 53a17bae892a..dd1d00a4ba2d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,3 +7,5 @@ updates: labels: - "area/testing" - "status/2-code-review" + cooldown: + default-days: 7 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 68202ef30730..5628a03788a7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,7 +35,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Create matrix id: platforms @@ -63,10 +63,10 @@ jobs: steps: - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Build - uses: docker/bake-action@v7 + uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7 with: targets: ${{ matrix.target }} set: | @@ -88,7 +88,7 @@ jobs: fi - name: Upload artifacts - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 with: name: ${{ env.ARTIFACT_NAME }} path: /tmp/out/* @@ -101,20 +101,20 @@ jobs: - name: Login to DockerHub if: github.event_name != 'pull_request' - uses: docker/login-action@v4 + uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4 with: username: ${{ secrets.DOCKERHUB_CLIBIN_USERNAME }} password: ${{ secrets.DOCKERHUB_CLIBIN_TOKEN }} - name: Set up QEMU - uses: docker/setup-qemu-action@v4 + uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Docker meta id: meta - uses: docker/metadata-action@v6 + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6 with: images: dockereng/cli-bin tags: | @@ -125,7 +125,7 @@ jobs: type=semver,pattern={{major}}.{{minor}} - name: Build and push image - uses: docker/bake-action@v7 + uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7 with: files: | ./docker-bake.hcl @@ -143,7 +143,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Create matrix id: platforms @@ -165,10 +165,10 @@ jobs: steps: - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Build - uses: docker/bake-action@v7 + uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7 with: targets: plugins-cross set: | diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index b1b430a3680d..e9155fe2062b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -46,7 +46,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 2 # CodeQL 2.16.4's auto-build added support for multi-module repositories, @@ -61,19 +61,20 @@ jobs: ln -s vendor.sum go.sum - name: Update Go - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version: "1.25.8" + cache: false - name: Initialize CodeQL - uses: github/codeql-action/init@v4 + uses: github/codeql-action/init@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1 with: languages: go - name: Autobuild - uses: github/codeql-action/autobuild@v4 + uses: github/codeql-action/autobuild@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4 + uses: github/codeql-action/analyze@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1 with: category: "/language:go" diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index a2e2076fbeb8..e895fc9877a6 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -44,7 +44,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Update daemon.json run: | @@ -63,7 +63,7 @@ jobs: docker info - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Run ${{ matrix.target }} run: | @@ -74,7 +74,7 @@ jobs: TESTFLAGS: -coverprofile=/tmp/coverage/coverage.txt - name: Send to Codecov - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5 with: files: ./build/coverage/coverage.txt token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f17f36363b2c..b3f8de89e669 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,15 +30,15 @@ jobs: steps: - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Test - uses: docker/bake-action@v7 + uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7 with: targets: test-coverage - name: Send to Codecov - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5 with: files: ./build/coverage/coverage.txt token: ${{ secrets.CODECOV_TOKEN }} @@ -60,14 +60,15 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: path: ${{ env.GOPATH }}/src/github.com/docker/cli - name: Set up Go - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version: "1.25.8" + cache: false - name: Test run: | @@ -80,7 +81,7 @@ jobs: shell: bash - name: Send to Codecov - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5 with: files: /tmp/coverage.txt working-directory: ${{ env.GOPATH }}/src/github.com/docker/cli diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 3e1e64150313..afa834b9c829 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -38,7 +38,7 @@ jobs: steps: - name: Run - uses: docker/bake-action@v7 + uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7 with: targets: ${{ matrix.target }} @@ -48,7 +48,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Generate shell: 'script --return --quiet --command "bash {0}"' @@ -74,7 +74,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Run shell: 'script --return --quiet --command "bash {0}"' From 49a1af6aaa6fac88e6da6a46951a4d52bb81d8b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Wed, 8 Jul 2026 15:18:58 +0200 Subject: [PATCH 6/8] gha: Add release branch sync workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 (cherry picked from commit 182f56fe8c587efab21a0e79952672d42ea28aa5) Signed-off-by: Paweł Gronowski --- .github/workflows/sync-release-branch.yml | 137 ++++++++++++++++++++++ scripts/sync-branch | 34 ++++++ scripts/unmerged-tags | 48 ++++++++ 3 files changed, 219 insertions(+) create mode 100644 .github/workflows/sync-release-branch.yml create mode 100755 scripts/sync-branch create mode 100755 scripts/unmerged-tags diff --git a/.github/workflows/sync-release-branch.yml b/.github/workflows/sync-release-branch.yml new file mode 100644 index 000000000000..2bbdab4b96f9 --- /dev/null +++ b/.github/workflows/sync-release-branch.yml @@ -0,0 +1,137 @@ +name: Sync Docker release branch + +concurrency: + group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: false + +permissions: + contents: read + +on: + workflow_dispatch: + inputs: + tag: + description: Tag to sync from, for example v29.6.0 + required: true + type: string + dry_run: + description: Merge but don't push + required: true + default: false + type: boolean + +jobs: + sync-release-branch: + runs-on: ubuntu-24.04 + permissions: + contents: write + outputs: + base_sha: ${{ steps.sync.outputs.base_sha }} + has_changes: ${{ steps.sync.outputs.has_changes }} + temporary_branch: ${{ steps.sync.outputs.temporary_branch }} + temporary_sha: ${{ steps.sync.outputs.temporary_sha }} + timeout-minutes: 10 + steps: + - name: Checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + fetch-depth: 0 + + - name: Validate + env: + BRANCH: ${{ github.ref_name }} + run: | + if [ "$BRANCH" = "master" ]; then + echo "::error::This workflow is expected to be run on a release branch, not master" + exit 1 + fi + + - name: Configure git author + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Sync release branch to tag range + id: sync + env: + DRY_RUN: ${{ inputs.dry_run }} + RELEASE_BRANCH: ${{ github.ref_name }} + RUN_ATTEMPT: ${{ github.run_attempt }} + RUN_ID: ${{ github.run_id }} + TAG: ${{ inputs.tag }} + run: | + set -o pipefail + base_sha=$(git rev-parse "origin/$RELEASE_BRANCH") + temporary_branch="process/sync-release-branch/$RUN_ID-$RUN_ATTEMPT" + echo "base_sha=$base_sha" >> "$GITHUB_OUTPUT" + echo "temporary_branch=$temporary_branch" >> "$GITHUB_OUTPUT" + + tags_file=$(mktemp) + scripts/unmerged-tags \ + "$RELEASE_BRANCH" \ + "$TAG" \ + > "$tags_file" + + echo >> "$GITHUB_STEP_SUMMARY" + echo "## Tags to sync" >> "$GITHUB_STEP_SUMMARY" + echo >> "$GITHUB_STEP_SUMMARY" + sed 's/^/- /' "$tags_file" >> "$GITHUB_STEP_SUMMARY" + + xargs -r scripts/sync-branch < "$tags_file" | tee -a "$GITHUB_STEP_SUMMARY" + + if [[ "$DRY_RUN" == "true" ]]; then + echo "has_changes=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + if [[ $(git rev-parse HEAD) == $(git rev-parse "origin/$RELEASE_BRANCH") ]]; then + echo "has_changes=false" >> "$GITHUB_OUTPUT" + echo "No changes to push" + exit 0 + fi + + echo "has_changes=true" >> "$GITHUB_OUTPUT" + git push origin "HEAD:refs/heads/$temporary_branch" + echo "temporary_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" + + push-release-branch: + needs: sync-release-branch + if: ${{ !inputs.dry_run && needs.sync-release-branch.outputs.has_changes == 'true' }} + runs-on: ubuntu-24.04 + environment: docker-releases + permissions: + contents: write + timeout-minutes: 10 + steps: + - 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: ${{ github.ref_name }} + TEMPORARY_BRANCH: ${{ needs.sync-release-branch.outputs.temporary_branch }} + TEMPORARY_SHA: ${{ needs.sync-release-branch.outputs.temporary_sha }} + run: | + git fetch origin "$RELEASE_BRANCH" + current_sha=$(git rev-parse "origin/$RELEASE_BRANCH") + if [[ "$current_sha" != "$BASE_SHA" ]]; then + echo "$RELEASE_BRANCH changed from $BASE_SHA to $current_sha" + exit 1 + fi + + git fetch origin "$TEMPORARY_BRANCH" + current_temporary_sha=$(git rev-parse FETCH_HEAD) + if [[ "$current_temporary_sha" != "$TEMPORARY_SHA" ]]; then + echo "$TEMPORARY_BRANCH changed from $TEMPORARY_SHA to $current_temporary_sha" + exit 1 + fi + + git push origin "FETCH_HEAD:$RELEASE_BRANCH" + + - name: Delete temporary branch + env: + TEMPORARY_BRANCH: ${{ needs.sync-release-branch.outputs.temporary_branch }} + run: git push origin --delete "$TEMPORARY_BRANCH" diff --git a/scripts/sync-branch b/scripts/sync-branch new file mode 100755 index 000000000000..4da920c10b3c --- /dev/null +++ b/scripts/sync-branch @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Merge the given release tags. +set -Eeuo pipefail + +if [[ $# -lt 1 ]]; then + echo "usage: $0 TAG..." >&2 + exit 1 +fi + +tags_to_sync=("$@") + +for tag_to_sync in "${tags_to_sync[@]}"; do + if git merge --no-ff "$tag_to_sync"; then + continue + fi + + if ! git rev-parse --verify --quiet MERGE_HEAD > /dev/null || git diff --quiet --diff-filter=U; then + exit 1 + fi + + git checkout "$tag_to_sync" -- '.' + git add --all + git merge --continue +done + +# Check that every tag is in the branch. +# This catches cases where a merge did not actually incorporate one of the +# requested release tags. +for tag_to_sync in "${tags_to_sync[@]}"; do + if ! git merge-base --is-ancestor "$tag_to_sync" HEAD; then + echo "tag $tag_to_sync is not contained in $(git rev-parse --abbrev-ref HEAD)" >&2 + exit 1 + fi +done diff --git a/scripts/unmerged-tags b/scripts/unmerged-tags new file mode 100755 index 000000000000..57b90666af3c --- /dev/null +++ b/scripts/unmerged-tags @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +if [[ $# -ne 2 ]]; then + echo "usage: $0 " >&2 + exit 1 +fi + +release_branch="$1" +tag="$2" + +if [[ "$tag" == *"-rc."* ]]; then + echo "error: RC tags cannot be used as sync targets" >&2 + exit 1 +fi + +if [[ "$tag" != v* ]]; then + echo "error: Tag must start with 'v' (e.g. v29.6.0)" >&2 + exit 1 +fi + +if ! git rev-parse --verify --quiet "refs/tags/$tag" > /dev/null; then + echo "error: Tag $tag does not exist" >&2 + exit 1 +fi + +# Return early the requested tag is already merged into release branch. +if git merge-base --is-ancestor "$tag" "$release_branch"; then + exit 0 +fi + +if git rev-parse --verify --quiet upstream/master > /dev/null 2>&1; then + master="upstream/master" +else + master="origin/master" +fi + +if ! git merge-base --is-ancestor "$tag" "$master"; then + echo "error: Tag $tag is not in $master" >&2 + exit 1 +fi + +# Get all docker release tags merged into master but not into release branch +git tag --merged "$master" --no-merged "$release_branch" \ + | grep '^v' \ + | grep -v -- "-rc." \ + | sort -V \ + | awk -v tag="$tag" '{print} $0==tag{exit}' From f72fb0caf0e539e490de93e4182a29f68b8127c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Thu, 9 Jul 2026 16:47:11 +0200 Subject: [PATCH 7/8] scripts/sync-branch: Fix non-interactive merge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Paweł Gronowski (cherry picked from commit f57e5284570c6b734442c18101756d672486a970) Signed-off-by: Paweł Gronowski --- scripts/sync-branch | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/sync-branch b/scripts/sync-branch index 4da920c10b3c..ed24e507ffcc 100755 --- a/scripts/sync-branch +++ b/scripts/sync-branch @@ -10,7 +10,7 @@ fi tags_to_sync=("$@") for tag_to_sync in "${tags_to_sync[@]}"; do - if git merge --no-ff "$tag_to_sync"; then + if git merge --no-edit --no-ff "$tag_to_sync"; then continue fi @@ -20,7 +20,9 @@ for tag_to_sync in "${tags_to_sync[@]}"; do git checkout "$tag_to_sync" -- '.' git add --all - git merge --continue + + # Can't use --no-edit with --continue + EDITOR=true git merge --continue done # Check that every tag is in the branch. From dba867594c282175d6b6dca05e5df6da2719e78f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Thu, 16 Jul 2026 12:51:30 +0200 Subject: [PATCH 8/8] Bump to 29.6.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Paweł Gronowski --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index d73ed40a839d..2c11dc9aa1a2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -29.6.1 +29.6.2