Skip to content

Commit 76b0b48

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

File tree

3 files changed

+99
-36
lines changed

3 files changed

+99
-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: hello-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: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ group "grp" {
1111
targets = ["go", "hello"]
1212
}
1313

14+
group "named-contexts" {
15+
targets = ["hello-cross-with-contexts"]
16+
}
17+
1418
variable "XX_VERSION" {
1519
default = null
1620
}
@@ -37,3 +41,37 @@ target "hello-cross" {
3741
inherits = ["hello"]
3842
platforms = ["linux/amd64", "linux/arm64"]
3943
}
44+
45+
target "hello-cross-with-contexts" {
46+
inherits = ["hello-cross"]
47+
contexts = {
48+
gen = "target:generated-files"
49+
}
50+
}
51+
52+
target "generated-files" {
53+
contexts = {
54+
generated-oapi = "target:generated-oapi"
55+
generated-proto = "target:generated-proto"
56+
}
57+
dockerfile-inline = <<-EOT
58+
FROM scratch AS generated-files
59+
COPY --from=generated-proto / /
60+
COPY --from=generated-oapi / /
61+
EOT
62+
output = ["type=cacheonly"]
63+
}
64+
65+
target "generated-oapi" {
66+
dockerfile-inline = <<-EOT
67+
FROM scratch
68+
EOT
69+
output = ["type=cacheonly"]
70+
}
71+
72+
target "generated-proto" {
73+
dockerfile-inline = <<-EOT
74+
FROM scratch
75+
EOT
76+
output = ["type=cacheonly"]
77+
}

0 commit comments

Comments
 (0)