Skip to content

Commit ac8c1ef

Browse files
hoverkraft-bot[bot]neilime
authored andcommitted
feat(docker-build-images): optimize caching and signing
Signed-off-by: Emilien Escalle <[email protected]>
1 parent 7384bd5 commit ac8c1ef

File tree

6 files changed

+195
-106
lines changed

6 files changed

+195
-106
lines changed

.github/workflows/__test-workflow-docker-build-images.yml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ jobs:
105105
)
106106
);
107107
108+
assert.equal(applicationMultiArchImage.platforms.length, 3);
109+
assert(applicationMultiArchImage.platforms.includes("linux/amd64"));
110+
assert(applicationMultiArchImage.platforms.includes("linux/arm64"));
111+
assert(applicationMultiArchImage.platforms.includes("linux/arm/v7"));
112+
108113
const applicationMonoArchImage = builtImages["test-mono-arch"];
109114
110115
assert.equal(applicationMonoArchImage.name, "test-mono-arch");
@@ -121,6 +126,8 @@ jobs:
121126
applicationMonoArchImage.images[0],
122127
`ghcr.io/hoverkraft-tech/ci-github-container/test-mono-arch:0.1.0@${applicationMonoArchImage.digest}`
123128
);
129+
assert.equal(applicationMonoArchImage.platforms.length, 1);
130+
assert(applicationMonoArchImage.platforms.includes("linux/amd64"));
124131
125132
- uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
126133
with:
@@ -278,8 +285,8 @@ jobs:
278285
secrets:
279286
oci-registry-password: ${{ secrets.GITHUB_TOKEN }}
280287
build-secrets: |
281-
SECRET_REPOSITORY_OWNER=${{ github.repository_owner }}
282-
SECRET_REPOSITORY=${{ github.repository }}
288+
SECRET_TEST=test-secret
289+
SECRET_ANOTHER_TEST=another-test-secret
283290
build-secret-github-app-key: ${{ secrets.CI_BOT_APP_PRIVATE_KEY }}
284291
with:
285292
cache-type: registry
@@ -293,12 +300,12 @@ jobs:
293300
"platforms": ["linux/amd64","linux/arm64"],
294301
"build-args": {
295302
"BUILD_RUN_ID": "${{ github.run_id }}",
296-
"BUILD_REPOSITORY_OWNER": "${{ github.repository_owner }}",
297-
"BUILD_REPOSITORY": "${{ github.repository }}"
303+
"BUILD_ARG_TEST": "test-arg",
304+
"BUILD_ARG_ANOTHER_TEST": "another-test-arg"
298305
},
299306
"secret-envs": {
300-
"SECRET_ENV_REPOSITORY_OWNER": "GITHUB_REPOSITORY_OWNER",
301-
"SECRET_ENV_REPOSITORY": "GITHUB_REPOSITORY"
307+
"SECRET_ENV_TEST": "GITHUB_ACTION",
308+
"SECRET_ENV_ANOTHER_TEST": "GITHUB_JOB"
302309
}
303310
}
304311
]

.github/workflows/docker-build-images.yml

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,6 @@ name: Docker build images
66

77
on: # yamllint disable-line rule:truthy
88
workflow_call:
9-
outputs:
10-
built-images:
11-
description: |
12-
Built images data.
13-
Example:
14-
```json
15-
{
16-
"application": {
17-
"name": "application",
18-
"registry": "ghcr.io",
19-
"repository": "my-org/my-repo/application",
20-
"tags": ["pr-63-5222075","pr-63"],
21-
"images": [
22-
"ghcr.io/my-org/my-repo/application:pr-63-5222075@sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d",
23-
"ghcr.io/my-org/my-repo/application:pr-63@sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d"
24-
],
25-
"digest": "sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d",
26-
"annotations": {
27-
"org.opencontainers.image.created": "2021-09-30T14:00:00Z",
28-
"org.opencontainers.image.description": "Application image"
29-
}
30-
}
31-
}
32-
```
33-
value: ${{ jobs.publish-manifests.outputs.built-images }}
349
inputs:
3510
runs-on:
3611
description: |
@@ -141,6 +116,32 @@ on: # yamllint disable-line rule:truthy
141116
GitHub App private key to generate GitHub token to be passed as build secret env.
142117
See https://github.com/actions/create-github-app-token.
143118
required: false
119+
outputs:
120+
built-images:
121+
description: |
122+
Built images data.
123+
Example:
124+
```json
125+
{
126+
"application": {
127+
"name": "application",
128+
"registry": "ghcr.io",
129+
"repository": "my-org/my-repo/application",
130+
"tags": ["pr-63-5222075","pr-63"],
131+
"images": [
132+
"ghcr.io/my-org/my-repo/application:pr-63-5222075@sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d",
133+
"ghcr.io/my-org/my-repo/application:pr-63@sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d"
134+
],
135+
"digest": "sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d",
136+
"annotations": {
137+
"org.opencontainers.image.created": "2021-09-30T14:00:00Z",
138+
"org.opencontainers.image.description": "Application image"
139+
},
140+
"platforms": ["linux/amd64", "linux/arm64"]
141+
}
142+
}
143+
```
144+
value: ${{ jobs.publish-manifests.outputs.built-images }}
144145

145146
permissions:
146147
contents: read
@@ -474,15 +475,17 @@ jobs:
474475
// Group by image name
475476
const images = {};
476477
builtImages.forEach(builtImage => {
477-
const { name, image, ...rest } = builtImage;
478+
const { name, image, platform, ...rest } = builtImage;
478479
if (!images[name]) {
479480
images[name] = {
480481
name,
481482
images: [image],
483+
platforms: [platform],
482484
...rest,
483485
};
484486
} else {
485487
images[name].images = [...new Set([...images[name].images, image])];
488+
images[name].platforms = [...new Set([...images[name].platforms, platform])];
486489
}
487490
});
488491
@@ -512,6 +515,7 @@ jobs:
512515
built-images: ${{ steps.built-images.outputs.built-images }}
513516

514517
- id: get-images-to-sign
518+
if: inputs.sign
515519
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
516520
with:
517521
script: |
@@ -527,7 +531,7 @@ jobs:
527531
const imagesToSign = Object.values(builtImages).map(image => image.images).flat();
528532
core.setOutput('images-to-sign', JSON.stringify(imagesToSign));
529533
- uses: ./self-workflow/actions/docker/sign-images
530-
if: inputs.sign
534+
if: steps.get-images-to-sign.outputs.images-to-sign
531535
with:
532536
images: ${{ steps.get-images-to-sign.outputs.images-to-sign }}
533537
github-token: ${{ secrets.GITHUB_TOKEN }}

actions/docker/build-image/action.yml

Lines changed: 104 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -10,30 +10,6 @@ branding:
1010
icon: package
1111
color: blue
1212

13-
outputs:
14-
built-image:
15-
description: |
16-
Built image data.
17-
Example:
18-
```json
19-
{
20-
"name": "application",
21-
"registry": "ghcr.io",
22-
"repository": "my-org/my-repo/application",
23-
"digest": "sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d",
24-
"image": "ghcr.io/my-org/my-repo/application@sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d",
25-
"tags": [
26-
"pr-63-5222075",
27-
"pr-63"
28-
],
29-
"annotations": {
30-
"org.opencontainers.image.created": "2021-09-30T14:00:00Z",
31-
"org.opencontainers.image.description": "Application image"
32-
}
33-
}
34-
```
35-
value: ${{ steps.get-built-image.outputs.built-image }}
36-
3713
inputs:
3814
oci-registry:
3915
description: "OCI registry where to pull and push images"
@@ -70,7 +46,7 @@ inputs:
7046
required: false
7147
platform:
7248
description: |
73-
Platform to build for.
49+
Platform to build for. Example: `linux/amd64`.
7450
See https://github.com/docker/build-push-action#inputs.
7551
required: true
7652
context:
@@ -112,6 +88,31 @@ inputs:
11288
default: "gha"
11389
required: false
11490

91+
outputs:
92+
built-image:
93+
description: |
94+
Built image data.
95+
Example:
96+
```json
97+
{
98+
"name": "application",
99+
"registry": "ghcr.io",
100+
"repository": "my-org/my-repo/application",
101+
"digest": "sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d",
102+
"image": "ghcr.io/my-org/my-repo/application@sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d",
103+
"tags": [
104+
"pr-63-5222075",
105+
"pr-63"
106+
],
107+
"annotations": {
108+
"org.opencontainers.image.created": "2021-09-30T14:00:00Z",
109+
"org.opencontainers.image.description": "Application image"
110+
},
111+
"platform": "linux/amd64"
112+
}
113+
```
114+
value: ${{ steps.get-built-image.outputs.built-image }}
115+
115116
runs:
116117
using: "composite"
117118
steps:
@@ -142,6 +143,14 @@ runs:
142143
- id: get-docker-config
143144
shell: bash
144145
run: |
146+
DOCKERFILE_PATH="${{ github.workspace }}/${{ inputs.context }}/${{ inputs.dockerfile }}"
147+
if [ ! -f "$DOCKERFILE_PATH" ]; then
148+
echo "::error::Dockerfile not found at path: $DOCKERFILE_PATH"
149+
exit 1
150+
fi
151+
DOCKERFILE_PATH=$(realpath "$DOCKERFILE_PATH")
152+
echo "dockerfile-path=$DOCKERFILE_PATH" >> "$GITHUB_OUTPUT"
153+
145154
TAG_SUFFIX="-${{ steps.slugify-platform.outputs.result }}"
146155
147156
# Add tag suffix flavor
@@ -201,7 +210,24 @@ runs:
201210
fi
202211
fi
203212
204-
- id: cache
213+
- if: steps.get-docker-config.outputs.docker-exists != 'true'
214+
uses: docker/setup-docker-action@3fb92d6d9c634363128c8cce4bc3b2826526370a # v4.4.0
215+
216+
- if: steps.get-docker-config.outputs.platform-exists != 'true'
217+
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
218+
with:
219+
platforms: ${{ inputs.platform }}
220+
221+
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
222+
with:
223+
# FIXME: upgrade version when available (https://github.com/docker/buildx/releases)
224+
version: v0.29.1
225+
# FIXME: upgrade version when available (https://hub.docker.com/r/moby/buildkit)
226+
driver-opts: |
227+
image=moby/buildkit:v0.25.1
228+
229+
# Caching setup
230+
- id: cache-arguments
205231
uses: int128/docker-build-cache-config-action@fb186e80c08f14a2e56ed9105d4594562bff013f # v1.40.0
206232
with:
207233
image: ${{ steps.get-docker-config.outputs.cache-image }}
@@ -216,8 +242,8 @@ runs:
216242
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
217243
with:
218244
script: |
219-
const cacheFrom = `${{ steps.cache.outputs.cache-from }}`;
220-
const cacheTo = `${{ steps.cache.outputs.cache-to }}`;
245+
const cacheFrom = `${{ steps.cache-arguments.outputs.cache-from }}`;
246+
const cacheTo = `${{ steps.cache-arguments.outputs.cache-to }}`;
221247
222248
core.info(`Original cache-from: ${cacheFrom}`);
223249
core.info(`Original cache-to: ${cacheTo}`);
@@ -234,22 +260,19 @@ runs:
234260
core.setOutput('cache-from', transformedCacheFrom);
235261
core.setOutput('cache-to', transformedCacheTo);
236262
237-
- if: steps.get-docker-config.outputs.docker-exists != 'true'
238-
uses: docker/setup-docker-action@3fb92d6d9c634363128c8cce4bc3b2826526370a # v4.4.0
239-
240-
- if: steps.get-docker-config.outputs.platform-exists != 'true'
241-
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
263+
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
264+
id: cache
242265
with:
243-
platforms: ${{ inputs.platform }}
266+
path: cache-mount
267+
key: cache-mount-${{ hashFiles(steps.get-docker-config.outputs.dockerfile-path) }}
244268

245-
# jscpd:ignore-start
246-
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
269+
- name: Restore Docker cache mounts
270+
uses: reproducible-containers/buildkit-cache-dance@5b81f4d29dc8397a7d341dba3aeecc7ec54d6361 # v3.3.0
247271
with:
248-
# FIXME: upgrade version when available (https://github.com/docker/buildx/releases)
249-
version: v0.27.0
250-
# FIXME: upgrade version when available (https://hub.docker.com/r/moby/buildkit)
251-
driver-opts: |
252-
image=moby/buildkit:v0.23.2
272+
builder: ${{ steps.setup-buildx.outputs.name }}
273+
cache-dir: cache-mount
274+
dockerfile: ${{ steps.get-docker-config.outputs.dockerfile-path }}
275+
skip-extraction: ${{ steps.cache.outputs.cache-hit }}
253276

254277
- uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
255278
with:
@@ -264,19 +287,22 @@ runs:
264287
context: ${{ inputs.context }}
265288
build-args: ${{ inputs.build-args }}
266289
target: ${{ inputs.target }}
267-
file: ${{ github.workspace }}/${{ inputs.context }}/${{ inputs.dockerfile }}
290+
file: ${{ steps.get-docker-config.outputs.dockerfile-path }}
268291
secrets: ${{ inputs.secrets }}
269292
secret-envs: ${{ inputs.secret-envs }}
270293
platforms: ${{ inputs.platform }}
271294
# FIXME: Remove 'inputs.cache-type == 'gha' && steps.transform-cache-gha.outputs.cache-from ||'
272295
# when https://github.com/int128/docker-build-cache-config-action/pull/1213 is merged
273-
cache-from: ${{ inputs.cache-type == 'gha' && steps.transform-cache-gha.outputs.cache-from || steps.cache.outputs.cache-from }}
296+
cache-from: ${{ inputs.cache-type == 'gha' && steps.transform-cache-gha.outputs.cache-from || steps.cache-arguments.outputs.cache-from }}
274297
# FIXME: Remove 'inputs.cache-type == 'gha' && steps.transform-cache-gha.outputs.cache-to ||'
275298
# when https://github.com/int128/docker-build-cache-config-action/pull/1213 is merged
276-
cache-to: ${{ inputs.cache-type == 'gha' && steps.transform-cache-gha.outputs.cache-to || steps.cache.outputs.cache-to }}
277-
outputs: "type=image,push=true,push-by-digest=true,name-canonical=true,name=${{ steps.metadata.outputs.image }}"
299+
cache-to: ${{ inputs.cache-type == 'gha' && steps.transform-cache-gha.outputs.cache-to || steps.cache-arguments.outputs.cache-to }}
300+
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
278301
labels: ${{ steps.metadata.outputs.labels }}
279302
annotations: ${{ steps.metadata.outputs.annotations }}
303+
tags: ${{ steps.metadata.outputs.image }}
304+
provenance: false # Disable provenance to avoid unknown/unknown arch
305+
sbom: false # Disable sbom to avoid unknown/unknown arch
280306

281307
- id: get-built-image
282308
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
@@ -288,10 +314,6 @@ runs:
288314
return;
289315
}
290316
291-
if (builtMetadata["containerimage.digest"] === undefined) {
292-
return core.setFailed('Given "metadata"."containerimage.digest" output is undefined.');
293-
}
294-
295317
const name = `${{ inputs.image }}`;
296318
const image = `${{ steps.metadata.outputs.image }}`;
297319
const registryMatch = image.match(/^([^\/]+)\/.*/);
@@ -304,6 +326,37 @@ runs:
304326
.map(tag => tag.replace(/[^\/]+\/[^:]+:(.+)/,'$1').trim())
305327
.filter(tag => tag !== "");
306328
329+
let platform;
330+
331+
const buildxProvenance = builtMetadata?.["buildx.build.provenance"];
332+
if (buildxProvenance !== undefined) {
333+
platform = buildxProvenance.invocation?.environment?.platform;
334+
if (platform === undefined) {
335+
return core.setFailed('Given "metadata"."buildx.build.provenance"."invocation"."environment"."platform" output is undefined.');
336+
}
337+
if (typeof platform !== "string") {
338+
return core.setFailed('Given "metadata"."buildx.build.provenance"."invocation"."environment"."platform" is not a string.');
339+
}
340+
platform = platform.trim();
341+
if (platform === "") {
342+
return core.setFailed('Given "metadata"."buildx.build.provenance"."invocation"."environment"."platform" is empty.');
343+
}
344+
} else {
345+
const descriptor = builtMetadata?.["containerimage.descriptor"];
346+
if (descriptor?.["platform"] === undefined) {
347+
return core.setFailed('Given "metadata"."containerimage.descriptor"."platform" output is undefined.');
348+
}
349+
const platformData = descriptor["platform"];
350+
if (typeof platformData !== 'object' || platformData.os === undefined || platformData.architecture === undefined) {
351+
return core.setFailed('Given "metadata"."containerimage.descriptor"."platform" does not contain required "os" and "architecture" fields.');
352+
}
353+
platform = `${platformData.os}/${platformData.architecture}${platformData.variant ? `/${platformData.variant}` : ''}`;
354+
}
355+
356+
if (builtMetadata?.["containerimage.digest"] === undefined) {
357+
return core.setFailed('Given "metadata"."containerimage.digest" output is undefined.');
358+
}
359+
307360
const digests = builtMetadata["containerimage.digest"]
308361
.split(",")
309362
.map(digest => {
@@ -346,7 +399,8 @@ runs:
346399
registry,
347400
repository,
348401
image: imageWithDigest,
349-
digest
402+
digest,
403+
platform
350404
};
351405
352406
core.setOutput("built-image", JSON.stringify(builtImage));

0 commit comments

Comments
 (0)