diff --git a/.github/workflows/__shared-ci.yml b/.github/workflows/__shared-ci.yml index 737f4f9a..b7a98e43 100644 --- a/.github/workflows/__shared-ci.yml +++ b/.github/workflows/__shared-ci.yml @@ -22,6 +22,12 @@ jobs: contents: read packages: write + 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/README.md b/actions/docker/setup/README.md index 98767387..0ae2b4d1 100644 --- a/actions/docker/setup/README.md +++ b/actions/docker/setup/README.md @@ -16,7 +16,6 @@ [![License](https://img.shields.io/github/license/hoverkraft-tech/ci-github-container)](http://choosealicense.com/licenses/mit/) [![Stars](https://img.shields.io/github/stars/hoverkraft-tech/ci-github-container?style=social)](https://img.shields.io/github/stars/hoverkraft-tech/ci-github-container?style=social) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/hoverkraft-tech/ci-github-container/blob/main/CONTRIBUTING.md) -[![codecov](https://codecov.io/gh/hoverkraft-tech/compose-action/graph/badge.svg?token=90JXB7EIMA)](https://codecov.io/gh/hoverkraft-tech/compose-action) @@ -31,7 +30,7 @@ Shared action to configure Docker tooling and OCI registry authentication. ## Usage ````yaml -- uses: hoverkraft-tech/ci-github-container/actions/docker/setup@77f98ab8773b824eca7ed3f94e3e9c8b8af5875c # 0.36.1 +- uses: hoverkraft-tech/ci-github-container/actions/docker/setup@8a1b3f38f22d36c8cff996dc37caf0b0e698b983 # feat/docker-pin-installed-docker-version with: # OCI registry configuration used to pull, push and cache images. # Accepts either a registry hostname string (default format) or a JSON object. diff --git a/actions/docker/setup/action.yml b/actions/docker/setup/action.yml index b762a7a4..c0e0aaf6 100644 --- a/actions/docker/setup/action.yml +++ b/actions/docker/setup/action.yml @@ -385,13 +385,35 @@ runs: - id: detect-docker uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + # FIXME: upgrade version when available (https://hub.docker.com/r/dockereng/cli-bin/tags) + EXPECTED_DOCKER_VERSION: 29.5.3 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: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0