Skip to content

Commit 6999deb

Browse files
committed
bake: allow named contexts target dependencies
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
1 parent f24d2e9 commit 6999deb

File tree

4 files changed

+94
-36
lines changed

4 files changed

+94
-36
lines changed

.github/workflows/.test-bake.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,3 +547,17 @@ jobs:
547547
- registry: registry-1-stage.docker.io
548548
username: ${{ vars.DOCKERHUB_STAGE_USERNAME }}
549549
password: ${{ secrets.DOCKERHUB_STAGE_TOKEN }}
550+
551+
bake-namedcontexts:
552+
uses: ./.github/workflows/bake.yml
553+
permissions:
554+
contents: read
555+
id-token: write
556+
with:
557+
artifact-name: bake-namedcontexts-output
558+
artifact-upload: true
559+
context: test
560+
output: local
561+
sbom: true
562+
sign: ${{ github.event_name != 'pull_request' }}
563+
target: go-cross-with-contexts

.github/workflows/bake.yml

Lines changed: 47 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -335,11 +335,46 @@ jobs:
335335
if (!def) {
336336
throw new Error('Bake definition not set');
337337
}
338-
const targets = Object.keys(def.target);
339-
if (targets.length > 1) {
340-
throw new Error(`Only one target can be built at once, found: ${targets.join(', ')}`);
338+
const targetDefs = def.target || {};
339+
const targets = Object.keys(targetDefs);
340+
if (targets.length === 0) {
341+
throw new Error('Bake definition does not contain any targets');
342+
}
343+
const parseContextTarget = value => {
344+
if (typeof value !== 'string') {
345+
return undefined;
346+
}
347+
const match = value.match(/^target:(.+)$/);
348+
return match ? match[1] : undefined;
349+
};
350+
const resolveTarget = () => {
351+
if (targetDefs[inpTarget]) {
352+
return inpTarget;
353+
}
354+
throw new Error(`Unable to resolve ${inpTarget} target, found: ${targets.join(', ')}`);
355+
};
356+
target = resolveTarget();
357+
const allowedTargets = new Set([target]);
358+
const stack = [target];
359+
while (stack.length > 0) {
360+
const current = stack.pop();
361+
const contexts = targetDefs[current]?.contexts || {};
362+
for (const contextValue of Object.values(contexts)) {
363+
const dependencyTarget = parseContextTarget(contextValue);
364+
if (!dependencyTarget || allowedTargets.has(dependencyTarget)) {
365+
continue;
366+
}
367+
if (!targetDefs[dependencyTarget]) {
368+
throw new Error(`Target ${current} uses unknown named context target ${dependencyTarget}`);
369+
}
370+
allowedTargets.add(dependencyTarget);
371+
stack.push(dependencyTarget);
372+
}
373+
}
374+
const unsupportedTargets = targets.filter(name => !allowedTargets.has(name));
375+
if (unsupportedTargets.length > 0) {
376+
throw new Error(`Only one target can be built at once, found unsupported targets: ${unsupportedTargets.join(', ')}`);
341377
}
342-
target = targets[0];
343378
});
344379
} catch (error) {
345380
core.setFailed(error);
@@ -581,7 +616,6 @@ jobs:
581616
with:
582617
script: |
583618
const os = require('os');
584-
const { Bake } = require('@docker/actions-toolkit/lib/buildx/bake');
585619
const { Build } = require('@docker/actions-toolkit/lib/buildx/build');
586620
const { GitHub } = require('@docker/actions-toolkit/lib/github');
587621
const { Util } = require('@docker/actions-toolkit/lib/util');
@@ -613,11 +647,16 @@ jobs:
613647
const inpGitHubToken = core.getInput('github-token');
614648
615649
const bakeSource = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}.git#${process.env.GITHUB_REF}:${inpContext}`;
616-
await core.group(`Set bake source`, async () => {
650+
await core.group(`Set source output`, async () => {
617651
core.info(bakeSource);
618652
core.setOutput('source', bakeSource);
619653
});
620654
655+
await core.group(`Set target output`, async () => {
656+
core.info(inpTarget);
657+
core.setOutput('target', inpTarget);
658+
});
659+
621660
const sbom = inpSbom ? `generator=${inpSbomImage}` : 'false';
622661
await core.group(`Set sbom`, async () => {
623662
core.info(sbom);
@@ -642,34 +681,6 @@ jobs:
642681
core.setOutput('envs', JSON.stringify(envs));
643682
});
644683
645-
let target;
646-
try {
647-
await core.group(`Validating definition`, async () => {
648-
const bake = new Bake();
649-
const def = await bake.getDefinition({
650-
files: inpFiles,
651-
overrides: inpSet,
652-
sbom: sbom,
653-
source: bakeSource,
654-
targets: [inpTarget]
655-
}, {
656-
env: Object.keys(envs).length > 0 ? envs : undefined
657-
});
658-
if (!def) {
659-
throw new Error('Bake definition not set');
660-
}
661-
const targets = Object.keys(def.target);
662-
if (targets.length > 1) {
663-
throw new Error(`Only one target can be built at once, found: ${targets.join(', ')}`);
664-
}
665-
target = targets[0];
666-
core.setOutput('target', target);
667-
});
668-
} catch (error) {
669-
core.setFailed(error);
670-
return;
671-
}
672-
673684
let bakeFiles = inpFiles;
674685
await core.group(`Set bake files`, async () => {
675686
if (bakeFiles.length === 0) {
@@ -719,8 +730,8 @@ jobs:
719730
bakeOverrides.push(`*.platform=${inpPlatform}`);
720731
}
721732
if (inpCache) {
722-
bakeOverrides.push(`*.cache-from=type=gha,scope=${inpCacheScope || target}${platformPairSuffix}`);
723-
bakeOverrides.push(`*.cache-to=type=gha,ignore-error=true,scope=${inpCacheScope || target}${platformPairSuffix},mode=${inpCacheMode}`);
733+
bakeOverrides.push(`*.cache-from=type=gha,scope=${inpCacheScope || inpTarget}${platformPairSuffix}`);
734+
bakeOverrides.push(`*.cache-to=type=gha,ignore-error=true,scope=${inpCacheScope || inpTarget}${platformPairSuffix},mode=${inpCacheMode}`);
724735
}
725736
core.info(JSON.stringify(bakeOverrides, null, 2));
726737
core.setOutput('overrides', bakeOverrides.join(os.EOL));

test/docker-bake.hcl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,33 @@ target "hello-cross" {
3737
inherits = ["hello"]
3838
platforms = ["linux/amd64", "linux/arm64"]
3939
}
40+
41+
target "go-cross-with-contexts" {
42+
inherits = ["go-cross"]
43+
contexts = {
44+
gen = "target:generated-files"
45+
}
46+
}
47+
48+
target "generated-files" {
49+
contexts = {
50+
generated-hello1 = "target:generated-hello1"
51+
generated-hello2 = "target:generated-hello2"
52+
}
53+
dockerfile-inline = <<-EOT
54+
FROM scratch AS generated-files
55+
COPY --from=generated-hello1 / /hello1
56+
COPY --from=generated-hello2 / /hello2
57+
EOT
58+
output = ["type=cacheonly"]
59+
}
60+
61+
target "generated-hello1" {
62+
dockerfile = "hello.Dockerfile"
63+
output = ["type=cacheonly"]
64+
}
65+
66+
target "generated-hello2" {
67+
dockerfile = "hello.Dockerfile"
68+
output = ["type=cacheonly"]
69+
}

test/go.Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ ARG XX_VERSION="1.7.0"
66
# xx is a helper for cross-compilation
77
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
88

9+
FROM scratch AS gen
10+
911
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS base
1012
COPY --from=xx / /
1113
RUN apk add --no-cache file git
@@ -14,6 +16,7 @@ WORKDIR /src
1416

1517
FROM base AS build
1618
ARG TARGETPLATFORM
19+
COPY --from=gen / /out
1720
RUN --mount=type=bind,target=. \
1821
--mount=target=/root/.cache,type=cache \
1922
xx-go build -trimpath -o /out/myapp . \

0 commit comments

Comments
 (0)