From 84d7e3d494baa77548d2b0b5692a68fefbf04552 Mon Sep 17 00:00:00 2001 From: Emilien Escalle Date: Wed, 3 Jun 2026 12:54:22 +0200 Subject: [PATCH] feat(docker): pin installed docker version Signed-off-by: Emilien Escalle --- .github/workflows/__shared-ci.yml | 6 + .../workflows/__test-action-docker-setup.yml | 157 ++++++++++++++++++ actions/docker/setup/action.yml | 31 +++- 3 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/__test-action-docker-setup.yml diff --git a/.github/workflows/__shared-ci.yml b/.github/workflows/__shared-ci.yml index b491dddd..3fc5f040 100644 --- a/.github/workflows/__shared-ci.yml +++ b/.github/workflows/__shared-ci.yml @@ -38,6 +38,12 @@ jobs: packages: write pull-requests: read + test-action-docker-setup: + needs: linter + uses: ./.github/workflows/__test-action-docker-setup.yml + permissions: + contents: read + test-action-docker-prune-pull-requests-image-tags: needs: linter # yamllint disable-line rule:line-length diff --git a/.github/workflows/__test-action-docker-setup.yml b/.github/workflows/__test-action-docker-setup.yml new file mode 100644 index 00000000..c52b4d8a --- /dev/null +++ b/.github/workflows/__test-action-docker-setup.yml @@ -0,0 +1,157 @@ +--- +name: Test for "docker/setup" action +run-name: Test for "docker/setup" action + +on: # yamllint disable-line rule:truthy + workflow_call: + +permissions: {} + +jobs: + tests: + name: Test for "docker/setup" action + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Arrange - Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Arrange - Ensure token is set + run: | + if [ -z "${{ github.token }}" ]; then + echo "GitHub token is not set" + exit 1 + fi + + - name: Act - Setup Docker + id: docker-setup + uses: ./actions/docker/setup + with: + oci-registry: ghcr.io + oci-registry-username: ${{ github.repository_owner }} + oci-registry-password: ${{ github.token }} + + - name: Assert - Check setup outputs and installed tooling + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + EXPECTED_DOCKER_VERSION: 29.5.2 + PUSH_REGISTRY_OUTPUT: ${{ steps.docker-setup.outputs.push-registry }} + CACHE_REGISTRY_OUTPUT: ${{ steps.docker-setup.outputs.cache-registry }} + PULL_REGISTRIES_OUTPUT: ${{ steps.docker-setup.outputs.pull-registries }} + BUILDX_NAME_OUTPUT: ${{ steps.docker-setup.outputs.buildx-name }} + with: + script: | + const assert = require("assert"); + + assert.equal(process.env.PUSH_REGISTRY_OUTPUT, "ghcr.io", '"push-registry" output is not valid'); + assert.equal(process.env.CACHE_REGISTRY_OUTPUT, "ghcr.io", '"cache-registry" output is not valid'); + + let pullRegistries = null; + try { + pullRegistries = JSON.parse(process.env.PULL_REGISTRIES_OUTPUT); + } catch (error) { + assert.fail(`Failed to parse "pull-registries" output: ${error}`); + } + + assert.deepEqual(pullRegistries, ["ghcr.io"], '"pull-registries" output is not valid'); + + const buildxName = `${process.env.BUILDX_NAME_OUTPUT || ''}`.trim(); + assert(buildxName.length, '"buildx-name" output is empty'); + + const dockerVersionResult = await exec.getExecOutput( + "docker", + ["version", "--format", "{{.Server.Version}}"], + { silent: true }, + ); + assert.equal( + dockerVersionResult.stdout.trim(), + process.env.EXPECTED_DOCKER_VERSION, + 'Installed Docker version is not the expected pinned version', + ); + + const buildxVersionResult = await exec.getExecOutput( + "docker", + ["buildx", "version"], + { silent: true }, + ); + assert(buildxVersionResult.stdout.trim().length, '"docker buildx version" returned an empty result'); + + const buildxInspectResult = await exec.getExecOutput( + "docker", + ["buildx", "inspect", buildxName], + { ignoreReturnCode: true, silent: true }, + ); + assert.equal(buildxInspectResult.exitCode, 0, 'Configured Buildx builder is not inspectable'); + + tests-with-multiple-registries-and-no-buildx: + name: Test for "docker/setup" action with multiple registries and no Buildx + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Arrange - Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Arrange - Ensure token is set + run: | + if [ -z "${{ github.token }}" ]; then + echo "GitHub token is not set" + exit 1 + fi + + - name: Act - Setup Docker + id: docker-setup + uses: ./actions/docker/setup + with: + oci-registry: | + {"pull":"docker.io","pull:private":"ghcr.io","push":"ghcr.io"} + oci-registry-username: | + {"push":"${{ github.repository_owner }}"} + oci-registry-password: | + {"push":"${{ github.token }}"} + setup-buildx: false + + - name: Assert - Check registry outputs without Buildx + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + EXPECTED_DOCKER_VERSION: 29.5.2 + PUSH_REGISTRY_OUTPUT: ${{ steps.docker-setup.outputs.push-registry }} + CACHE_REGISTRY_OUTPUT: ${{ steps.docker-setup.outputs.cache-registry }} + PULL_REGISTRIES_OUTPUT: ${{ steps.docker-setup.outputs.pull-registries }} + BUILDX_NAME_OUTPUT: ${{ steps.docker-setup.outputs.buildx-name }} + with: + script: | + const assert = require("assert"); + + assert.equal(process.env.PUSH_REGISTRY_OUTPUT, "ghcr.io", '"push-registry" output is not valid'); + assert.equal(process.env.CACHE_REGISTRY_OUTPUT, "ghcr.io", '"cache-registry" output is not valid'); + + let pullRegistries = null; + try { + pullRegistries = JSON.parse(process.env.PULL_REGISTRIES_OUTPUT); + } catch (error) { + assert.fail(`Failed to parse "pull-registries" output: ${error}`); + } + + assert.deepEqual( + pullRegistries, + ["docker.io", "ghcr.io"], + '"pull-registries" output is not valid for multiple registries', + ); + assert.equal(`${process.env.BUILDX_NAME_OUTPUT || ''}`.trim(), '', '"buildx-name" output must be empty when Buildx is disabled'); + + const dockerVersionResult = await exec.getExecOutput( + "docker", + ["version", "--format", "{{.Server.Version}}"], + { silent: true }, + ); + assert.equal( + dockerVersionResult.stdout.trim(), + process.env.EXPECTED_DOCKER_VERSION, + 'Installed Docker version is not the expected pinned version', + ); diff --git a/actions/docker/setup/action.yml b/actions/docker/setup/action.yml index 9e53d339..86e017d3 100644 --- a/actions/docker/setup/action.yml +++ b/actions/docker/setup/action.yml @@ -50,6 +50,12 @@ inputs: Whether the Buildx builder should be removed during post-job cleanup. default: true required: false + # FIXME: upgrade version when available (https://github.com/docker/actions-toolkit/blob/main/.github/docker-releases.json) + docker-version: + description: | + Docker version used when Docker must be installed by the action. + default: "29.5.2" + required: false # FIXME: upgrade version when available (https://github.com/docker/buildx/releases) buildx-version: description: | @@ -397,13 +403,34 @@ runs: - id: detect-docker uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + EXPECTED_DOCKER_VERSION: ${{ inputs.docker-version }} with: script: | const dockerPath = await io.which('docker', false); - core.setOutput('exists', dockerPath ? 'true' : 'false'); - - if: steps.detect-docker.outputs.exists != 'true' + if (!dockerPath) { + core.setOutput('docker-install-version', process.env.EXPECTED_DOCKER_VERSION); + return; + } + + try { + const { stdout } = await exec.getExecOutput('docker', ['version', '--format', '{{.Server.Version}}']); + const dockerVersion = stdout.trim(); + + // Check if the detected Docker version is the same as the expected version. + if (dockerVersion !== process.env.EXPECTED_DOCKER_VERSION) { + core.setOutput('docker-install-version', process.env.EXPECTED_DOCKER_VERSION); + } + } catch (error) { + core.warning(`Failed to detect Docker version, defaulting to expected version: ${error}`); + core.setOutput('docker-install-version', process.env.EXPECTED_DOCKER_VERSION); + }; + + - if: steps.detect-docker.outputs.docker-install-version uses: docker/setup-docker-action@0234bb73ccb40f0c430b795634f9247e2b5c2d23 # v5.2.0 + with: + version: type=archive,channel=stable,version=${{ steps.detect-docker.outputs.docker-install-version }} - if: inputs.setup-buildx != 'false' uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0