Skip to content

Commit dc3c945

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

File tree

3 files changed

+143
-24
lines changed

3 files changed

+143
-24
lines changed

.github/workflows/.test-bake.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: bake-aws-single
3436
context: test
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: bake-aws
8387
context: test
8488
output: image
8589
push: ${{ github.event_name != 'pull_request' }}
@@ -129,6 +133,8 @@ jobs:
129133
contents: read
130134
id-token: write
131135
with:
136+
cache: true
137+
cache-scope: bake-aws-nosign
132138
context: test
133139
output: image
134140
push: ${{ github.event_name != 'pull_request' }}

.github/workflows/bake.yml

Lines changed: 128 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,20 @@ on:
2323
description: "Upload build output GitHub artifact (for local output)"
2424
required: false
2525
default: false
26+
cache:
27+
type: boolean
28+
description: "Enable cache to GitHub Actions cache backend"
29+
required: false
30+
default: false
31+
cache-scope:
32+
type: string
33+
description: "Which scope cache object belongs to if cache enabled (defaults to target name)"
34+
required: false
35+
cache-mode:
36+
type: string
37+
description: "Cache layers to export if cache enabled (min or max)"
38+
required: false
39+
default: 'min'
2640
context:
2741
type: string
2842
description: "Context to build from in the Git working tree"
@@ -136,6 +150,7 @@ jobs:
136150
outputs:
137151
includes: ${{ steps.set.outputs.includes }}
138152
sign: ${{ steps.set.outputs.sign }}
153+
privateRepo: ${{ steps.set.outputs.privateRepo }}
139154
steps:
140155
-
141156
name: Install @docker/actions-toolkit
@@ -247,6 +262,11 @@ jobs:
247262
}
248263
249264
const privateRepo = GitHub.context.payload.repository?.private ?? false;
265+
await core.group(`Set privateRepo output`, async () => {
266+
core.info(`privateRepo: ${privateRepo}`);
267+
core.setOutput('privateRepo', privateRepo);
268+
});
269+
250270
await core.group(`Set includes output`, async () => {
251271
let includes = [];
252272
if (platforms.length === 0) {
@@ -329,21 +349,120 @@ jobs:
329349
if: ${{ inputs.setup-qemu }}
330350
with:
331351
cache-image: false
352+
-
353+
name: Expose GitHub Runtime
354+
uses: crazy-max/ghaction-github-runtime@3cb05d89e1f492524af3d41a1c98c83bc3025124 # v3.1.0
332355
-
333356
name: Set up Docker Buildx
357+
id: buildx
334358
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
335359
with:
336360
version: ${{ env.BUILDX_VERSION }}
337-
buildkitd-flags: --debug
338-
driver-opts: image=${{ env.BUILDKIT_IMAGE }}
339361
cache-binary: false
362+
buildkitd-flags: --debug
363+
driver-opts: |
364+
image=${{ env.BUILDKIT_IMAGE }}
365+
env.ACTIONS_ID_TOKEN_REQUEST_TOKEN=${{ env.ACTIONS_ID_TOKEN_REQUEST_TOKEN }}
366+
env.ACTIONS_ID_TOKEN_REQUEST_URL=${{ env.ACTIONS_ID_TOKEN_REQUEST_URL }}
367+
buildkitd-config-inline: |
368+
[cache]
369+
[cache.gha]
370+
[cache.gha.sign]
371+
command = ["ghacache-sign-script.sh"]
372+
[cache.gha.verify]
373+
required = true
374+
[cache.gha.verify.policy]
375+
timestampThreshold = 1
376+
tlogThreshold = ${{ matrix.privateRepo == 'true' && '0' || '1' }}
377+
subjectAlternativeName = "https://github.com/docker/github-builder-experimental/.github/workflows/bake.yml*"
378+
issuer = "https://token.actions.githubusercontent.com"
379+
runnerEnvironment = "github-hosted"
380+
-
381+
name: Install Cosign
382+
if: ${{ needs.prepare.outputs.sign == 'true' || inputs.cache }}
383+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
384+
env:
385+
INPUT_COSIGN-VERSION: ${{ env.COSIGN_VERSION }}
386+
INPUT_BUILDER-NAME: ${{ steps.buildx.outputs.name }}
387+
INPUT_GHA-CACHE-SIGN-SCRIPT: |
388+
#!/bin/sh
389+
set -e
390+
391+
# Create temporary files
392+
out_file=$(mktemp)
393+
in_file=$(mktemp)
394+
trap 'rm -f "$in_file" "$out_file"' EXIT
395+
cat > "$in_file"
396+
397+
set -x
398+
399+
# Sign with cosign
400+
cosign sign-blob \
401+
--yes \
402+
--oidc-provider github-actions \
403+
--new-bundle-format \
404+
--use-signing-config \
405+
--bundle "$out_file" \
406+
--tlog-upload=${{ matrix.privateRepo == 'false' }} \
407+
"$in_file"
408+
409+
# Output bundle to stdout
410+
cat "$out_file"
411+
with:
412+
script: |
413+
const fs = require('fs');
414+
const os = require('os');
415+
const path = require('path');
416+
417+
const { Buildx } = require('@docker/actions-toolkit/lib/buildx/buildx');
418+
const { Cosign } = require('@docker/actions-toolkit/lib/cosign/cosign');
419+
const { Install } = require('@docker/actions-toolkit/lib/cosign/install');
420+
421+
const inpCosignVersion = core.getInput('cosign-version');
422+
const inpBuilderName = core.getInput('builder-name');
423+
const inpGHACacheSignScript = core.getInput('gha-cache-sign-script');
424+
425+
const cosignInstall = new Install();
426+
const cosignBinPath = await cosignInstall.download({
427+
version: core.getInput('cosign-version'),
428+
ghaNoCache: true,
429+
skipState: true,
430+
verifySignature: true
431+
});
432+
const cosignPath = await cosignInstall.install(cosignBinPath);
433+
434+
const cosign = new Cosign();
435+
await cosign.printVersion();
436+
437+
const containerName = `${Buildx.containerNamePrefix}${inpBuilderName}0`;
438+
439+
const ghaCacheSignScriptPath = path.join(os.tmpdir(), `ghacache-sign-script.sh`);
440+
core.info(`Writing GitHub Actions cache sign script to ${ghaCacheSignScriptPath}`);
441+
await fs.writeFileSync(ghaCacheSignScriptPath, inpGHACacheSignScript, {mode: 0o700});
442+
443+
core.info(`Copying GitHub Actions cache sign script to BuildKit container ${containerName}`);
444+
await exec.exec('docker', [
445+
'cp',
446+
ghaCacheSignScriptPath,
447+
`${containerName}:/usr/bin/ghacache-sign-script.sh`
448+
]);
449+
450+
core.info(`Copying cosign binary to BuildKit container ${containerName}`);
451+
await exec.exec('docker', [
452+
'cp',
453+
cosignPath,
454+
`${containerName}:/usr/bin/cosign`
455+
]);
340456
-
341457
name: Prepare
342458
id: prepare
343459
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
344460
env:
345461
INPUT_PLATFORM: ${{ matrix.platform }}
346462
INPUT_LOCAL-EXPORT-DIR: ${{ env.LOCAL_EXPORT_DIR }}
463+
INPUT_CACHE: ${{ inputs.cache }}
464+
INPUT_CACHE-SCOPE: ${{ inputs.cache-scope }}
465+
INPUT_CACHE-MODE: ${{ inputs.cache-mode }}
347466
INPUT_CONTEXT: ${{ inputs.context }}
348467
INPUT_FILES: ${{ inputs.files }}
349468
INPUT_OUTPUT: ${{ inputs.output }}
@@ -371,6 +490,9 @@ jobs:
371490
372491
const inpLocalExportDir = core.getInput('local-export-dir');
373492
493+
const inpCache = core.getBooleanInput('cache');
494+
const inpCacheScope = core.getInput('cache-scope');
495+
const inpCacheMode = core.getInput('cache-mode');
374496
const inpContext = core.getInput('context');
375497
const inpFiles = Util.getInputList('files');
376498
const inpOutput = core.getInput('output');
@@ -468,6 +590,10 @@ jobs:
468590
if (inpPlatform) {
469591
bakeOverrides.push(`*.platform=${inpPlatform}`);
470592
}
593+
if (inpCache) {
594+
bakeOverrides.push(`*.cache-from=type=gha,scope=${inpCacheScope || target}${platformPairSuffix}`);
595+
bakeOverrides.push(`*.cache-to=type=gha,scope=${inpCacheScope || target}${platformPairSuffix},mode=${inpCacheMode}`);
596+
}
471597
core.info(JSON.stringify(bakeOverrides, null, 2));
472598
core.setOutput('overrides', bakeOverrides.join(os.EOL));
473599
});
@@ -505,28 +631,6 @@ jobs:
505631
const imageDigest = inpMetadata[inpTarget]['containerimage.digest'];
506632
core.info(imageDigest);
507633
core.setOutput('digest', imageDigest);
508-
-
509-
name: Install Cosign
510-
if: ${{ needs.prepare.outputs.sign == 'true' }}
511-
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
512-
env:
513-
INPUT_COSIGN-VERSION: ${{ env.COSIGN_VERSION }}
514-
with:
515-
script: |
516-
const { Cosign } = require('@docker/actions-toolkit/lib/cosign/cosign');
517-
const { Install } = require('@docker/actions-toolkit/lib/cosign/install');
518-
519-
const cosignInstall = new Install();
520-
const cosignBinPath = await cosignInstall.download({
521-
version: core.getInput('cosign-version'),
522-
ghaNoCache: true,
523-
skipState: true,
524-
verifySignature: true
525-
});
526-
await cosignInstall.install(cosignBinPath);
527-
528-
const cosign = new Cosign();
529-
await cosign.printVersion();
530634
-
531635
name: Signing attestation manifests
532636
id: signing-attestation-manifests

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ toward higher levels of security and trust.
8080
requiring emulation or [custom CI logic](https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners)
8181
or self-managed runners.
8282
83+
* **Optimized cache warming & reuse.**
84+
The builder can use the GitHub Actions cache backend to persist layers across
85+
branches, PRs, and rebuilds. This significantly reduces cold-start times and
86+
avoids repeating expensive dependency installations, even for external
87+
contributors' pull requests.
88+
8389
* **Centralized build configuration.**
8490
Repositories no longer need to configure buildx drivers, tune storage, or
8591
adjust resource limits. The reusable workflows encapsulate the recommended
@@ -332,6 +338,9 @@ on:
332338
| `setup-qemu` | Bool | `false` | Runs the `setup-qemu-action` step to install QEMU static binaries |
333339
| `artifact-name` | String | `docker-github-builder-assets` | Name of the uploaded GitHub artifact (for `local` output) |
334340
| `artifact-upload` | Bool | `false` | Upload build output GitHub artifact (for `local` output) |
341+
| `cache` | Bool | `false` | Enable [GitHub Actions cache](https://docs.docker.com/build/cache/backends/gha/) exporter |
342+
| `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 |
343+
| `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 |
335344
| `context` | String | `.` | Context to build from in the Git working tree |
336345
| `files` | List | `{context}/docker-bake.hcl` | List of bake definition files |
337346
| `output` | String | | Build output destination (one of [`image`](https://docs.docker.com/build/exporters/image-registry/) or [`local`](https://docs.docker.com/build/exporters/local-tar/)). |

0 commit comments

Comments
 (0)