@@ -17,85 +17,77 @@ jobs:
1717 arrange :
1818 name : Arrange
1919 runs-on : ubuntu-latest
20+ outputs :
21+ image-name-prefix : ${{ steps.define-image-name-prefix.outputs.prefix }}
2022 steps :
2123 - run : |
2224 if [ -z "${{ secrets.GITHUB_TOKEN }}" ]; then
2325 echo "GitHub token secret is not set"
2426 exit 1
2527 fi
2628
27- - name : Delete existing test packages
28- env :
29- GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
29+ - id : define-image-name-prefix
3030 run : |
31- # Delete ephemeral test packages before testing
32- for IMAGE_NAME in test-mono-arch-signed test-multi-arch-signed test-mono-arch-unsigned test-multi-arch-unsigned; do
33- gh api \
34- --method DELETE \
35- -H "Accept: application/vnd.github+json" \
36- -H "X-GitHub-Api-Version: 2022-11-28" \
37- /orgs/${{ github.repository_owner }}/packages/container/ci-github-container%2F"${IMAGE_NAME}" || echo "No existing ${IMAGE_NAME} package to delete"
38- done
31+ # Create unique image name prefix for parallel runs
32+ if [ "${{ github.event_name }}" = "pull_request" ]; then
33+ PREFIX="test-pr-${{ github.event.pull_request.number }}-${{ github.run_number }}"
34+ else
35+ PREFIX="test-${{ github.ref_name }}-${{ github.run_number }}"
36+ fi
37+ # Replace / with - for branch names
38+ PREFIX=$(echo "$PREFIX" | sed 's/\//-/g')
39+ echo "prefix=$PREFIX" >> "$GITHUB_OUTPUT"
40+ echo "Using image name prefix: $PREFIX"
3941
40- act-build-signed- images :
41- name : Act - Build signed images (${{ matrix.arch-type }})
42+ act-build-images :
43+ name : Act - Build images (${{ matrix.arch-type }}, ${{ matrix.sign && 'signed' || 'unsigned' }})
4244 needs : arrange
4345 strategy :
4446 fail-fast : false
4547 matrix :
4648 include :
4749 - arch-type : mono-arch
48- image-name : test- mono-arch-signed
50+ image-name-suffix : mono-arch-signed
4951 platforms : ' ["linux/amd64"]'
5052 platform-count : 1
53+ sign : true
54+ expected-tagged-versions : 2
55+ expected-untagged-versions : 2
56+ expected-total-versions : 4
5157 - arch-type : multi-arch
52- image-name : test- multi-arch-signed
58+ image-name-suffix : multi-arch-signed
5359 platforms : ' ["linux/amd64","linux/arm64"]'
5460 platform-count : 2
55- uses : ./.github/workflows/docker-build-images.yml
56- secrets :
57- oci-registry-password : ${{ secrets.GITHUB_TOKEN }}
58- build-secret-github-app-key : ${{ secrets.CI_BOT_APP_PRIVATE_KEY }}
59- with :
60- sign : true
61- images : |
62- [
63- {
64- "name": "${{ matrix.image-name }}",
65- "context": ".",
66- "dockerfile": "./tests/application/Dockerfile",
67- "build-args": { "BUILD_RUN_ID": "${{ github.run_id }}" },
68- "target": "base",
69- "platforms": ${{ matrix.platforms }},
70- "tag": "0.1.0"
71- }
72- ]
73-
74- act-build-unsigned-images :
75- name : Act - Build unsigned images (${{ matrix.arch-type }})
76- needs : arrange
77- strategy :
78- fail-fast : false
79- matrix :
80- include :
61+ sign : true
62+ expected-tagged-versions : 2
63+ expected-untagged-versions : 3
64+ expected-total-versions : 5
8165 - arch-type : mono-arch
82- image-name : test- mono-arch-unsigned
66+ image-name-suffix : mono-arch-unsigned
8367 platforms : ' ["linux/amd64"]'
8468 platform-count : 1
69+ sign : false
70+ expected-tagged-versions : 1
71+ expected-untagged-versions : 1
72+ expected-total-versions : 2
8573 - arch-type : multi-arch
86- image-name : test- multi-arch-unsigned
74+ image-name-suffix : multi-arch-unsigned
8775 platforms : ' ["linux/amd64","linux/arm64"]'
8876 platform-count : 2
77+ sign : false
78+ expected-tagged-versions : 1
79+ expected-untagged-versions : 2
80+ expected-total-versions : 3
8981 uses : ./.github/workflows/docker-build-images.yml
9082 secrets :
9183 oci-registry-password : ${{ secrets.GITHUB_TOKEN }}
9284 build-secret-github-app-key : ${{ secrets.CI_BOT_APP_PRIVATE_KEY }}
9385 with :
94- sign : false
86+ sign : ${{ matrix.sign }}
9587 images : |
9688 [
9789 {
98- "name": "${{ matrix.image-name }}",
90+ "name": "${{ needs.arrange.outputs.image-name-prefix }}-${{ matrix.image-name-suffix }}",
9991 "context": ".",
10092 "dockerfile": "./tests/application/Dockerfile",
10193 "build-args": { "BUILD_RUN_ID": "${{ github.run_id }}" },
@@ -105,151 +97,43 @@ jobs:
10597 }
10698 ]
10799
108- assert-signed- images :
109- name : Assert - Signed images (${{ matrix.arch-type }})
110- needs : act-build-signed- images
100+ assert-images :
101+ name : Assert - Images (${{ matrix.arch-type }}, ${{ matrix.sign && 'signed' || 'unsigned' }})
102+ needs : [arrange, act-build-images]
111103 runs-on : ubuntu-latest
112104 strategy :
113105 fail-fast : false
114106 matrix :
115107 include :
116108 - arch-type : mono-arch
117- image-name : test- mono-arch-signed
109+ image-name-suffix : mono-arch-signed
118110 platform-count : 1
119111 platforms : ' ["linux/amd64"]'
112+ sign : true
120113 expected-tagged-versions : 2
121114 expected-untagged-versions : 2
122115 expected-total-versions : 4
123116 - arch-type : multi-arch
124- image-name : test- multi-arch-signed
117+ image-name-suffix : multi-arch-signed
125118 platform-count : 2
126119 platforms : ' ["linux/amd64","linux/arm64"]'
120+ sign : true
127121 expected-tagged-versions : 2
128122 expected-untagged-versions : 3
129123 expected-total-versions : 5
130- steps :
131- - name : Login to GitHub Container Registry
132- uses : docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
133- with :
134- registry : ghcr.io
135- username : ${{ github.repository_owner }}
136- password : ${{ github.token }}
137-
138- - name : Verify image exists
139- env :
140- IMAGE_NAME : ${{ matrix.image-name }}
141- IMAGE_TAG : 0.1.0
142- run : |
143- docker pull ghcr.io/hoverkraft-tech/ci-github-container/"${IMAGE_NAME}":"${IMAGE_TAG}"
144-
145- - name : Verify package has correct number of versions
146- env :
147- GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
148- IMAGE_NAME : ${{ matrix.image-name }}
149- EXPECTED_TOTAL_VERSIONS : ${{ matrix.expected-total-versions }}
150- EXPECTED_TAGGED_VERSIONS : ${{ matrix.expected-tagged-versions }}
151- EXPECTED_UNTAGGED_VERSIONS : ${{ matrix.expected-untagged-versions }}
152- run : |
153- # Get all versions
154- versions=$(gh api \
155- -H "Accept: application/vnd.github+json" \
156- -H "X-GitHub-Api-Version: 2022-11-28" \
157- /orgs/${{ github.repository_owner }}/packages/container/ci-github-container%2F"${IMAGE_NAME}"/versions)
158-
159- total_count=$(echo "$versions" | jq '. | length')
160- tagged_count=$(echo "$versions" | jq '[.[] | select(.metadata.container.tags | length > 0)] | length')
161- untagged_count=$(echo "$versions" | jq '[.[] | select(.metadata.container.tags | length == 0)] | length')
162-
163- echo "Found $total_count total versions ($tagged_count tagged, $untagged_count untagged)"
164- echo "Expected $EXPECTED_TOTAL_VERSIONS total versions ($EXPECTED_TAGGED_VERSIONS tagged, $EXPECTED_UNTAGGED_VERSIONS untagged)"
165-
166- if [ "$total_count" -ne "$EXPECTED_TOTAL_VERSIONS" ]; then
167- echo "ERROR: Expected $EXPECTED_TOTAL_VERSIONS total versions, but found $total_count"
168- exit 1
169- fi
170-
171- if [ "$tagged_count" -ne "$EXPECTED_TAGGED_VERSIONS" ]; then
172- echo "ERROR: Expected $EXPECTED_TAGGED_VERSIONS tagged versions, but found $tagged_count"
173- exit 1
174- fi
175-
176- if [ "$untagged_count" -ne "$EXPECTED_UNTAGGED_VERSIONS" ]; then
177- echo "ERROR: Expected $EXPECTED_UNTAGGED_VERSIONS untagged versions, but found $untagged_count"
178- exit 1
179- fi
180-
181- echo "✓ Package version counts are correct"
182-
183- - name : Verify image manifest and platforms
184- uses : actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
185- env :
186- IMAGE_NAME : ${{ matrix.image-name }}
187- IMAGE_TAG : " 0.1.0"
188- EXPECTED_PLATFORMS : ${{ matrix.platforms }}
189- EXPECTED_PLATFORM_COUNT : ${{ matrix.platform-count }}
190- with :
191- script : |
192- const assert = require("assert");
193- const imageName = process.env.IMAGE_NAME;
194- const imageTag = process.env.IMAGE_TAG;
195- const image = `ghcr.io/hoverkraft-tech/ci-github-container/${imageName}:${imageTag}`;
196-
197- const { exitCode, stdout, stderr } = await exec.getExecOutput('docker', ['manifest', 'inspect', '-v', image]);
198-
199- if (exitCode !== 0 || stderr) {
200- throw new Error(`Failed to inspect manifest for image: ${image}: ${stderr || stdout}`);
201- }
202-
203- const manifest = JSON.parse(stdout);
204- const expectedPlatformCount = parseInt(process.env.EXPECTED_PLATFORM_COUNT);
205-
206- assert.equal(manifest.length, expectedPlatformCount, `Expected ${expectedPlatformCount} platforms, got: ${manifest.length}`);
207-
208- const expectedPlatforms = JSON.parse(process.env.EXPECTED_PLATFORMS);
209- expectedPlatforms.forEach(platformStr => {
210- const [os, arch] = platformStr.split('/');
211- const platformExists = manifest.some(
212- platform => (
213- platform?.Descriptor?.platform?.architecture === arch &&
214- platform?.Descriptor?.platform?.os === os
215- )
216- );
217-
218- assert(platformExists, `Expected platform not found: ${platformStr}`);
219- });
220-
221- - name : Install cosign
222- uses : sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
223-
224- - name : Verify image signature
225- env :
226- IMAGE_NAME : ${{ matrix.image-name }}
227- IMAGE_TAG : 0.1.0
228- run : |
229- cosign verify \
230- --certificate-oidc-issuer https://token.actions.githubusercontent.com \
231- --certificate-identity-regexp https://github.com/hoverkraft-tech/ci-github-container \
232- ghcr.io/hoverkraft-tech/ci-github-container/"${IMAGE_NAME}":"${IMAGE_TAG}"
233-
234- assert-unsigned-images :
235- name : Assert - Unsigned images (${{ matrix.arch-type }})
236- needs : act-build-unsigned-images
237- runs-on : ubuntu-latest
238- strategy :
239- fail-fast : false
240- matrix :
241- include :
242124 - arch-type : mono-arch
243- image-name : test- mono-arch-unsigned
125+ image-name-suffix : mono-arch-unsigned
244126 platform-count : 1
245127 platforms : ' ["linux/amd64"]'
128+ sign : false
246129 expected-tagged-versions : 1
247130 expected-untagged-versions : 1
248131 expected-total-versions : 2
249132 - arch-type : multi-arch
250- image-name : test- multi-arch-unsigned
133+ image-name-suffix : multi-arch-unsigned
251134 platform-count : 2
252135 platforms : ' ["linux/amd64","linux/arm64"]'
136+ sign : false
253137 expected-tagged-versions : 1
254138 expected-untagged-versions : 2
255139 expected-total-versions : 3
@@ -263,15 +147,15 @@ jobs:
263147
264148 - name : Verify image exists
265149 env :
266- IMAGE_NAME : ${{ matrix.image-name }}
150+ IMAGE_NAME : ${{ needs.arrange.outputs.image-name-prefix }}-${{ matrix.image-name-suffix }}
267151 IMAGE_TAG : 0.1.0
268152 run : |
269153 docker pull ghcr.io/hoverkraft-tech/ci-github-container/"${IMAGE_NAME}":"${IMAGE_TAG}"
270154
271155 - name : Verify package has correct number of versions
272156 env :
273157 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
274- IMAGE_NAME : ${{ matrix.image-name }}
158+ IMAGE_NAME : ${{ needs.arrange.outputs.image-name-prefix }}-${{ matrix.image-name-suffix }}
275159 EXPECTED_TOTAL_VERSIONS : ${{ matrix.expected-total-versions }}
276160 EXPECTED_TAGGED_VERSIONS : ${{ matrix.expected-tagged-versions }}
277161 EXPECTED_UNTAGGED_VERSIONS : ${{ matrix.expected-untagged-versions }}
@@ -309,7 +193,7 @@ jobs:
309193 - name : Verify image manifest and platforms
310194 uses : actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
311195 env :
312- IMAGE_NAME : ${{ matrix.image-name }}
196+ IMAGE_NAME : ${{ needs.arrange.outputs.image-name-prefix }}-${{ matrix.image-name-suffix }}
313197 IMAGE_TAG : " 0.1.0"
314198 EXPECTED_PLATFORMS : ${{ matrix.platforms }}
315199 EXPECTED_PLATFORM_COUNT : ${{ matrix.platform-count }}
@@ -345,11 +229,28 @@ jobs:
345229 });
346230
347231 - name : Install cosign
232+ if : matrix.sign
233+ uses : sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
234+
235+ - name : Verify image signature
236+ if : matrix.sign
237+ env :
238+ IMAGE_NAME : ${{ needs.arrange.outputs.image-name-prefix }}-${{ matrix.image-name-suffix }}
239+ IMAGE_TAG : 0.1.0
240+ run : |
241+ cosign verify \
242+ --certificate-oidc-issuer https://token.actions.githubusercontent.com \
243+ --certificate-identity-regexp https://github.com/hoverkraft-tech/ci-github-container \
244+ ghcr.io/hoverkraft-tech/ci-github-container/"${IMAGE_NAME}":"${IMAGE_TAG}"
245+
246+ - name : Install cosign for unsigned verification
247+ if : ${{ !matrix.sign }}
348248 uses : sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
349249
350250 - name : Verify image is NOT signed (should fail)
251+ if : ${{ !matrix.sign }}
351252 env :
352- IMAGE_NAME : ${{ matrix.image-name }}
253+ IMAGE_NAME : ${{ needs.arrange.outputs.image-name-prefix }}-${{ matrix.image-name-suffix }}
353254 IMAGE_TAG : 0.1.0
354255 continue-on-error : true
355256 id : verify-unsigned
@@ -360,7 +261,7 @@ jobs:
360261 ghcr.io/hoverkraft-tech/ci-github-container/"${IMAGE_NAME}":"${IMAGE_TAG}"
361262
362263 - name : Assert verification failed (image should not be signed)
363- if : steps.verify-unsigned.outcome == 'success'
264+ if : ${{ !matrix.sign && steps.verify-unsigned.outcome == 'success' }}
364265 run : |
365266 echo "ERROR: Image should not be signed but signature verification succeeded"
366267 exit 1
@@ -491,17 +392,19 @@ jobs:
491392 name : Cleanup ephemeral test packages
492393 if : always()
493394 needs :
494- - assert-signed-images
495- - assert-unsigned- images
395+ - arrange
396+ - assert-images
496397 - assert-build-args-secrets-and-registry-caching
497398 runs-on : ubuntu-latest
498399 steps :
499400 - name : Delete ephemeral test packages
500401 env :
501402 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
403+ IMAGE_PREFIX : ${{ needs.arrange.outputs.image-name-prefix }}
502404 run : |
503405 # Delete ephemeral test packages created during testing
504- for IMAGE_NAME in test-mono-arch-signed test-multi-arch-signed test-mono-arch-unsigned test-multi-arch-unsigned; do
406+ for SUFFIX in mono-arch-signed multi-arch-signed mono-arch-unsigned multi-arch-unsigned; do
407+ IMAGE_NAME="${IMAGE_PREFIX}-${SUFFIX}"
505408 gh api \
506409 --method DELETE \
507410 -H "Accept: application/vnd.github+json" \
0 commit comments