Skip to content

Commit d71b1df

Browse files
committed
build: add github actions cache and sign blobs
Signed-off-by: CrazyMax <[email protected]>
1 parent 328daf0 commit d71b1df

File tree

3 files changed

+140
-24
lines changed

3 files changed

+140
-24
lines changed

.github/workflows/.test-build.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ jobs:
3131
contents: read
3232
id-token: write
3333
with:
34+
cache: true
35+
cache-scope: build-aws-single
3436
file: test/hello.Dockerfile
3537
output: image
3638
push: ${{ github.event_name != 'pull_request' }}
@@ -80,6 +82,8 @@ jobs:
8082
contents: read
8183
id-token: write
8284
with:
85+
cache: true
86+
cache-scope: build-aws
8387
file: test/hello.Dockerfile
8488
output: image
8589
platforms: linux/amd64,linux/arm64
@@ -129,6 +133,8 @@ jobs:
129133
contents: read
130134
id-token: write
131135
with:
136+
cache: true
137+
cache-scope: build-aws-nosign
132138
file: test/hello.Dockerfile
133139
output: image
134140
platforms: linux/amd64,linux/arm64

.github/workflows/build.yml

Lines changed: 131 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,20 @@ on:
3131
type: string
3232
description: "List of build-time variables"
3333
required: false
34+
cache:
35+
type: boolean
36+
description: "Enable cache to GitHub Actions cache backend"
37+
required: false
38+
default: false
39+
cache-scope:
40+
type: string
41+
description: "Which scope cache object belongs to if cache enabled (defaults to target name if set)"
42+
required: false
43+
cache-mode:
44+
type: string
45+
description: "Cache layers to export if cache enabled (min or max)"
46+
required: false
47+
default: 'min'
3448
context:
3549
type: string
3650
description: "Context to build from in the Git working tree"
@@ -143,6 +157,7 @@ jobs:
143157
outputs:
144158
includes: ${{ steps.set.outputs.includes }}
145159
sign: ${{ steps.set.outputs.sign }}
160+
privateRepo: ${{ steps.set.outputs.privateRepo }}
146161
steps:
147162
-
148163
name: Install @docker/actions-toolkit
@@ -209,6 +224,11 @@ jobs:
209224
}
210225
211226
const privateRepo = GitHub.context.payload.repository?.private ?? false;
227+
await core.group(`Set privateRepo output`, async () => {
228+
core.info(`privateRepo: ${privateRepo}`);
229+
core.setOutput('privateRepo', privateRepo);
230+
});
231+
212232
await core.group(`Set includes output`, async () => {
213233
let includes = [];
214234
if (inpPlatforms.length === 0) {
@@ -290,14 +310,110 @@ jobs:
290310
if: ${{ inputs.setup-qemu }}
291311
with:
292312
cache-image: false
313+
-
314+
name: Expose GitHub Runtime
315+
uses: crazy-max/ghaction-github-runtime@3cb05d89e1f492524af3d41a1c98c83bc3025124 # v3.1.0
293316
-
294317
name: Set up Docker Buildx
318+
id: buildx
295319
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
296320
with:
297321
version: ${{ env.BUILDX_VERSION }}
298-
buildkitd-flags: --debug
299-
driver-opts: image=${{ env.BUILDKIT_IMAGE }}
300322
cache-binary: false
323+
buildkitd-flags: --debug
324+
driver-opts: |
325+
image=${{ env.BUILDKIT_IMAGE }}
326+
env.ACTIONS_ID_TOKEN_REQUEST_TOKEN=${{ env.ACTIONS_ID_TOKEN_REQUEST_TOKEN }}
327+
env.ACTIONS_ID_TOKEN_REQUEST_URL=${{ env.ACTIONS_ID_TOKEN_REQUEST_URL }}
328+
buildkitd-config-inline: |
329+
[cache]
330+
[cache.gha]
331+
[cache.gha.sign]
332+
command = ["ghacache-sign-script.sh"]
333+
[cache.gha.verify]
334+
required = true
335+
[cache.gha.verify.policy]
336+
timestampThreshold = 1
337+
tlogThreshold = ${{ matrix.privateRepo == 'true' && '0' || '1' }}
338+
subjectAlternativeName = "https://github.com/docker/github-builder-experimental/.github/workflows/build.yml*"
339+
issuer = "https://token.actions.githubusercontent.com"
340+
runnerEnvironment = "github-hosted"
341+
-
342+
name: Install Cosign
343+
if: ${{ needs.prepare.outputs.sign == 'true' || inputs.cache }}
344+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
345+
env:
346+
INPUT_COSIGN-VERSION: ${{ env.COSIGN_VERSION }}
347+
INPUT_BUILDER-NAME: ${{ steps.buildx.outputs.name }}
348+
INPUT_GHA-CACHE-SIGN-SCRIPT: |
349+
#!/bin/sh
350+
set -e
351+
352+
# Create temporary files
353+
out_file=$(mktemp)
354+
in_file=$(mktemp)
355+
trap 'rm -f "$in_file" "$out_file"' EXIT
356+
cat > "$in_file"
357+
358+
set -x
359+
360+
# Sign with cosign
361+
cosign sign-blob \
362+
--yes \
363+
--oidc-provider github-actions \
364+
--new-bundle-format \
365+
--use-signing-config \
366+
--bundle "$out_file" \
367+
--tlog-upload=${{ matrix.privateRepo == 'false' }} \
368+
"$in_file"
369+
370+
# Output bundle to stdout
371+
cat "$out_file"
372+
with:
373+
script: |
374+
const fs = require('fs');
375+
const os = require('os');
376+
const path = require('path');
377+
378+
const { Buildx } = require('@docker/actions-toolkit/lib/buildx/buildx');
379+
const { Cosign } = require('@docker/actions-toolkit/lib/cosign/cosign');
380+
const { Install } = require('@docker/actions-toolkit/lib/cosign/install');
381+
382+
const inpCosignVersion = core.getInput('cosign-version');
383+
const inpBuilderName = core.getInput('builder-name');
384+
const inpGHACacheSignScript = core.getInput('gha-cache-sign-script');
385+
386+
const cosignInstall = new Install();
387+
const cosignBinPath = await cosignInstall.download({
388+
version: core.getInput('cosign-version'),
389+
ghaNoCache: true,
390+
skipState: true,
391+
verifySignature: true
392+
});
393+
const cosignPath = await cosignInstall.install(cosignBinPath);
394+
395+
const cosign = new Cosign();
396+
await cosign.printVersion();
397+
398+
const containerName = `${Buildx.containerNamePrefix}${inpBuilderName}0`;
399+
400+
const ghaCacheSignScriptPath = path.join(os.tmpdir(), `ghacache-sign-script.sh`);
401+
core.info(`Writing GitHub Actions cache sign script to ${ghaCacheSignScriptPath}`);
402+
await fs.writeFileSync(ghaCacheSignScriptPath, inpGHACacheSignScript, {mode: 0o700});
403+
404+
core.info(`Copying GitHub Actions cache sign script to BuildKit container ${containerName}`);
405+
await exec.exec('docker', [
406+
'cp',
407+
ghaCacheSignScriptPath,
408+
`${containerName}:/usr/bin/ghacache-sign-script.sh`
409+
]);
410+
411+
core.info(`Copying cosign binary to BuildKit container ${containerName}`);
412+
await exec.exec('docker', [
413+
'cp',
414+
cosignPath,
415+
`${containerName}:/usr/bin/cosign`
416+
]);
301417
-
302418
name: Prepare
303419
id: prepare
@@ -306,6 +422,9 @@ jobs:
306422
INPUT_PLATFORM: ${{ matrix.platform }}
307423
INPUT_LOCAL-EXPORT-DIR: ${{ env.LOCAL_EXPORT_DIR }}
308424
INPUT_ANNOTATIONS: ${{ inputs.annotations }}
425+
INPUT_CACHE: ${{ inputs.cache }}
426+
INPUT_CACHE-SCOPE: ${{ inputs.cache-scope }}
427+
INPUT_CACHE-MODE: ${{ inputs.cache-mode }}
309428
INPUT_LABELS: ${{ inputs.labels }}
310429
INPUT_CONTEXT: ${{ inputs.context }}
311430
INPUT_OUTPUT: ${{ inputs.output }}
@@ -326,6 +445,9 @@ jobs:
326445
const inpLocalExportDir = core.getInput('local-export-dir');
327446
328447
const inpAnnotations = core.getMultilineInput('annotations');
448+
const inpCache = core.getBooleanInput('cache');
449+
const inpCacheScope = core.getInput('cache-scope');
450+
const inpCacheMode = core.getInput('cache-mode');
329451
const inpContext = core.getInput('context');
330452
const inpLabels = core.getMultilineInput('labels');
331453
const inpOutput = core.getInput('output');
@@ -361,6 +483,11 @@ jobs:
361483
core.setOutput('platform', inpPlatform);
362484
}
363485
486+
if (inpCache) {
487+
core.setOutput('cache-from', `type=gha,scope=${inpCacheScope || inpTarget || 'buildkit'}${platformPairSuffix}`);
488+
core.setOutput('cache-to', `type=gha,scope=${inpCacheScope || inpTarget || 'buildkit'}${platformPairSuffix},mode=${inpCacheMode}`);
489+
}
490+
364491
if (inpSetMetaAnnotations && inpMetaAnnotations.length > 0) {
365492
inpAnnotations.push(...inpMetaAnnotations);
366493
}
@@ -392,6 +519,8 @@ jobs:
392519
with:
393520
annotations: ${{ steps.prepare.outputs.annotations }}
394521
build-args: ${{ inputs.build-args }}
522+
cache-from: ${{ steps.prepare.outputs.cache-from }}
523+
cache-to: ${{ steps.prepare.outputs.cache-to }}
395524
context: ${{ steps.prepare.outputs.context }}
396525
file: ${{ inputs.file }}
397526
labels: ${{ steps.prepare.outputs.labels }}
@@ -406,28 +535,6 @@ jobs:
406535
env:
407536
BUILDKIT_MULTI_PLATFORM: 1
408537
GIT_AUTH_TOKEN: ${{ secrets.github-token || github.token }}
409-
-
410-
name: Install Cosign
411-
if: ${{ needs.prepare.outputs.sign == 'true' }}
412-
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
413-
env:
414-
INPUT_COSIGN-VERSION: ${{ env.COSIGN_VERSION }}
415-
with:
416-
script: |
417-
const { Cosign } = require('@docker/actions-toolkit/lib/cosign/cosign');
418-
const { Install } = require('@docker/actions-toolkit/lib/cosign/install');
419-
420-
const cosignInstall = new Install();
421-
const cosignBinPath = await cosignInstall.download({
422-
version: core.getInput('cosign-version'),
423-
ghaNoCache: true,
424-
skipState: true,
425-
verifySignature: true
426-
});
427-
await cosignInstall.install(cosignBinPath);
428-
429-
const cosign = new Cosign();
430-
await cosign.printVersion();
431538
-
432539
name: Signing attestation manifests
433540
id: signing-attestation-manifests

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,9 @@ on:
227227
| `artifact-upload` | Bool | `false` | Upload build output GitHub artifact (for `local` output) |
228228
| `annotations` | List | | List of annotations to set to the image (for `image` output) |
229229
| `build-args` | List | `auto` | List of [build-time variables](https://docs.docker.com/engine/reference/commandline/buildx_build/#build-arg). If you want to set a build-arg through an environment variable, use the `envs` input |
230+
| `cache` | Bool | `false` | Enable [GitHub Actions cache](https://docs.docker.com/build/cache/backends/gha/) exporter |
231+
| `cache-scope` | String | target name or `buildkit` | Which [scope cache object belongs to](https://docs.docker.com/build/cache/backends/gha/#scope) if `cache` is enabled. This is the cache blob prefix name used when pushing cache to GitHub Actions cache backend |
232+
| `cache-mode` | String | `min` | [Cache layers to export](https://docs.docker.com/build/cache/backends/#cache-mode) if cache enabled (`min` or `max`). In `min` cache mode, only layers that are exported into the resulting image are cached, while in `max` cache mode, all layers are cached, even those of intermediate steps |
230233
| `context` | String | `.` | Context to build from in the Git working tree |
231234
| `file` | String | `{context}/Dockerfile` | Path to the Dockerfile |
232235
| `labels` | List | | List of labels for an image (for `image` output) |

0 commit comments

Comments
 (0)