Skip to content

Commit 959dbe2

Browse files
committed
bake: require declared secrets
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
1 parent adedb35 commit 959dbe2

2 files changed

Lines changed: 45 additions & 3 deletions

File tree

.github/workflows/bake.yml

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ jobs:
212212
metaImages: ${{ steps.set.outputs.metaImages }}
213213
sign: ${{ steps.set.outputs.sign }}
214214
privateRepo: ${{ steps.set.outputs.privateRepo }}
215+
secretIds: ${{ steps.set.outputs.secretIds }}
215216
ghaCacheSign: ${{ steps.set.outputs.ghaCacheSign }}
216217
steps:
217218
-
@@ -522,6 +523,16 @@ jobs:
522523
const match = value.match(/^target:(.+)$/);
523524
return match ? match[1] : undefined;
524525
};
526+
const parseSecretId = secret => {
527+
if (typeof secret === 'string') {
528+
const idAttr = secret.split(',').map(attr => attr.trim()).find(attr => attr.startsWith('id='));
529+
return idAttr ? idAttr.substring(3) : undefined;
530+
}
531+
if (secret && typeof secret === 'object' && typeof secret.id === 'string') {
532+
return secret.id;
533+
}
534+
return undefined;
535+
};
525536
const resolveTarget = () => {
526537
if (targetDefs[inpTarget]) {
527538
return inpTarget;
@@ -550,6 +561,11 @@ jobs:
550561
if (unsupportedTargets.length > 0) {
551562
throw new Error(`Only one target can be built at once, found unsupported targets: ${unsupportedTargets.join(', ')}`);
552563
}
564+
const secretIds = {};
565+
for (const name of allowedTargets) {
566+
secretIds[name] = (targetDefs[name]?.secret || []).map(parseSecretId).filter(Boolean);
567+
}
568+
core.setOutput('secretIds', JSON.stringify(secretIds));
553569
});
554570
} catch (error) {
555571
core.setFailed(error);
@@ -836,6 +852,7 @@ jobs:
836852
INPUT_CACHE-SCOPE: ${{ inputs.cache-scope }}
837853
INPUT_CACHE-MODE: ${{ inputs.cache-mode }}
838854
INPUT_BUILD-SECRETS: ${{ secrets.build-secrets }}
855+
INPUT_SECRET-IDS: ${{ needs.prepare.outputs.secretIds }}
839856
INPUT_CONTEXT: ${{ inputs.context }}
840857
INPUT_FILES: ${{ inputs.files }}
841858
INPUT_OUTPUT: ${{ inputs.output }}
@@ -878,6 +895,7 @@ jobs:
878895
const inpCacheScope = core.getInput('cache-scope');
879896
const inpCacheMode = core.getInput('cache-mode');
880897
const inpBuildSecrets = core.getInput('build-secrets');
898+
const inpSecretIds = core.getInput('secret-ids');
881899
const inpContext = core.getInput('context');
882900
const inpFiles = Util.getInputList('files');
883901
const inpOutput = core.getInput('output');
@@ -901,6 +919,7 @@ jobs:
901919
tags: inpMetaTags
902920
};
903921
const renderTemplate = value => Util.compileHandlebars(value, {noEscape: true}, {meta});
922+
904923
const parseBuildSecrets = value => {
905924
const normalized = value.trim();
906925
if (!normalized) {
@@ -950,6 +969,19 @@ jobs:
950969
}
951970
return secrets;
952971
};
972+
973+
const validateBuildSecrets = (secrets, secretIds) => {
974+
for (const {target, id} of secrets) {
975+
const targetSecretIds = secretIds[target];
976+
if (!targetSecretIds) {
977+
throw new Error(`Build secret target "${target}" is not part of the resolved Bake definition`);
978+
}
979+
if (!Array.isArray(targetSecretIds) || !targetSecretIds.includes(id)) {
980+
throw new Error(`Build secret "${id}" must be declared in Bake target "${target}" before it can be provided through build-secrets`);
981+
}
982+
}
983+
};
984+
953985
const toBuildSecretEnvName = (id, index) => `BUILD_SECRET_${index}_${id.toUpperCase().replace(/[^A-Z0-9_]/g, '_')}`;
954986
955987
const gitContextAttrs = GitHub.context.ref.startsWith('refs/tags/') ? {checksum: GitHub.context.sha} : {'fetch-by-commit': 'true'};
@@ -977,7 +1009,16 @@ jobs:
9771009
core.setFailed(err.message);
9781010
return;
9791011
}
980-
1012+
1013+
let secretIds;
1014+
try {
1015+
secretIds = JSON.parse(inpSecretIds || '{}');
1016+
validateBuildSecrets(buildSecrets, secretIds);
1017+
} catch (err) {
1018+
core.setFailed(err.message);
1019+
return;
1020+
}
1021+
9811022
const envs = Object.assign({},
9821023
inpVars ? inpVars.reduce((acc, curr) => {
9831024
const idx = curr.indexOf('=');

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,8 @@ passes these values through `docker/build-push-action` `secret-envs`, and the
418418
bake workflow appends matching `target.secrets+=id=...,env=...` overrides. For
419419
the bake workflow, an unqualified key applies to the workflow `target` input. A
420420
key written as `target.secret_id` applies only to that Bake target. This
421-
target-scoped key form is only accepted by the bake workflow:
421+
target-scoped key form is only accepted by the bake workflow. The target must
422+
already declare a matching secret ID in the Bake definition:
422423

423424
```yaml
424425
secrets:
@@ -429,7 +430,7 @@ secrets:
429430

430431
Bake targets can declare local secret sources for direct `docker buildx bake`
431432
usage. When the reusable workflow receives a matching `build-secrets` entry, it
432-
overrides that source with the workflow-provided secret value:
433+
overrides that declared source with the workflow-provided secret value:
433434

434435
```hcl
435436
target "default" {

0 commit comments

Comments
 (0)