Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/__shared-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,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
uses: ./.github/workflows/__test-action-docker-prune-pull-requests-image-tags.yml
Expand Down
219 changes: 219 additions & 0 deletions .github/workflows/__test-action-docker-setup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
---
name: Test for "docker/setup" action
run-name: Test for "docker/setup" action

on: # yamllint disable-line rule:truthy
workflow_call:

permissions: {}

jobs:
resolve-action-defaults:
name: Resolve action defaults
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
docker-version: ${{ steps.resolve-docker-version.outputs.docker-version }}
steps:
- name: Arrange - Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Arrange - Resolve Docker setup defaults
id: resolve-docker-version
shell: bash
run: |
docker_version="$(ruby -e 'require "yaml"; puts YAML.safe_load(File.read("actions/docker/setup/action.yml")).dig("inputs", "docker-version", "default")')"

if [ -z "$docker_version" ]; then
echo "Failed to resolve docker-version default from actions/docker/setup/action.yml"
exit 1
fi

echo "docker-version=$docker_version" >> "$GITHUB_OUTPUT"

tests:
name: Test for "docker/setup" action
runs-on: ubuntu-latest
needs: resolve-action-defaults
permissions:
contents: read
env:
EXPECTED_DOCKER_VERSION: ${{ needs.resolve-action-defaults.outputs.docker-version }}
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:
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');

const dockerContextResult = await exec.getExecOutput(
"docker",
["context", "inspect", "--format", "{{ (index .Endpoints \"docker\").Host }}"],
{ silent: true },
);
const expectedDockerEndpoint = dockerContextResult.stdout.trim();
assert(expectedDockerEndpoint.length, 'Failed to resolve current Docker context endpoint');

const buildxNodesRaw = await exec.getExecOutput(
"docker",
["buildx", "inspect", buildxName, "--format", "{{json .Nodes}}"],
{ ignoreReturnCode: true, silent: true },
);
assert.equal(
buildxNodesRaw.exitCode,
0,
`Failed to inspect Buildx nodes for builder "${buildxName}": ${buildxNodesRaw.stderr || 'unknown error'}`,
);
let buildxNodes = null;
try {
buildxNodes = JSON.parse(buildxNodesRaw.stdout.trim());
} catch (error) {
assert.fail(`Failed to parse buildx nodes JSON: ${error}`);
}

assert(Array.isArray(buildxNodes) && buildxNodes.length > 0, 'Buildx nodes output is empty');
assert.equal(
buildxNodes[0].Endpoint,
expectedDockerEndpoint,
'Configured Buildx builder is not attached to the current Docker context endpoint',
);

tests-with-multiple-registries-and-no-buildx:
name: Test for "docker/setup" action with multiple registries and no Buildx
runs-on: ubuntu-latest
needs: resolve-action-defaults
permissions:
contents: read
env:
EXPECTED_DOCKER_VERSION: ${{ needs.resolve-action-defaults.outputs.docker-version }}
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:
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',
);
33 changes: 31 additions & 2 deletions actions/docker/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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.7.2"
required: false
# FIXME: upgrade version when available (https://github.com/docker/buildx/releases)
buildx-version:
description: |
Expand Down Expand Up @@ -397,13 +403,35 @@ 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
id: setup-docker
uses: docker/setup-docker-action@77e84dbf09b47d1e29270283c22f16145aa85ca1 # v5.4.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
Expand Down Expand Up @@ -446,6 +474,7 @@ runs:
id: setup-buildx
with:
version: ${{ inputs.buildx-version }}
endpoint: ${{ steps.setup-docker.outputs.sock }}
driver-opts: |
image=${{ inputs.buildkit-image }}
buildkitd-config-inline: ${{ inputs.buildkitd-config-inline }}
Expand Down
Loading