133133 registry-auths :
134134 description : " Raw authentication to registries, defined as YAML objects (for image output)"
135135 required : false
136+ build-secrets :
137+ description : " YAML object mapping BuildKit secret IDs to secret values"
138+ required : false
136139 github-token :
137140 description : " GitHub Token used to authenticate against the repository for Git context"
138141 required : false
@@ -691,6 +694,7 @@ jobs:
691694 INPUT_CACHE : ${{ inputs.cache }}
692695 INPUT_CACHE-SCOPE : ${{ inputs.cache-scope }}
693696 INPUT_CACHE-MODE : ${{ inputs.cache-mode }}
697+ INPUT_BUILD-SECRETS : ${{ secrets.build-secrets }}
694698 INPUT_LABELS : ${{ inputs.labels }}
695699 INPUT_CONTEXT : ${{ inputs.context }}
696700 INPUT_OUTPUT : ${{ inputs.output }}
@@ -705,12 +709,20 @@ jobs:
705709 INPUT_META-ANNOTATIONS : ${{ steps.meta.outputs.annotations }}
706710 INPUT_SET-META-LABELS : ${{ inputs.set-meta-labels }}
707711 INPUT_META-LABELS : ${{ steps.meta.outputs.labels }}
712+ INPUT_GITHUB-TOKEN : ${{ secrets.github-token || github.token }}
708713 with :
709714 script : |
710715 const { Build } = require('@docker/github-builder-runtime/lib/buildx/build');
711716 const { GitHub } = require('@docker/github-builder-runtime/lib/github/github');
712717 const { Util } = require('@docker/github-builder-runtime/lib/util');
713-
718+
719+ let yaml;
720+ try {
721+ yaml = require('js-yaml');
722+ } catch {
723+ yaml = require('@docker/github-builder-runtime/node_modules/js-yaml');
724+ }
725+
714726 const inpPlatform = core.getInput('platform');
715727 const platformPairSuffix = inpPlatform ? `-${inpPlatform.replace(/\//g, '-')}` : '';
716728 core.setOutput('platform-pair-suffix', platformPairSuffix);
@@ -724,6 +736,7 @@ jobs:
724736 const inpCache = core.getBooleanInput('cache');
725737 const inpCacheScope = core.getInput('cache-scope');
726738 const inpCacheMode = core.getInput('cache-mode');
739+ const inpBuildSecrets = core.getInput('build-secrets');
727740 const inpContext = core.getInput('context');
728741 const inpLabels = core.getInput('labels');
729742 const inpOutput = core.getInput('output');
@@ -739,6 +752,7 @@ jobs:
739752 const inpMetaAnnotations = core.getMultilineInput('meta-annotations');
740753 const inpSetMetaLabels = core.getBooleanInput('set-meta-labels');
741754 const inpMetaLabels = core.getMultilineInput('meta-labels');
755+ const inpGitHubToken = core.getInput('github-token');
742756
743757 const meta = {
744758 version: inpMetaVersion,
@@ -747,6 +761,40 @@ jobs:
747761
748762 const renderTemplate = value => Util.compileHandlebars(value, {noEscape: true}, {meta});
749763 const toMultilineInput = value => value.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
764+ const parseBuildSecrets = value => {
765+ const normalized = value.trim();
766+ if (!normalized) {
767+ return {};
768+ }
769+ let parsed;
770+ try {
771+ parsed = yaml.load(normalized, {schema: yaml.FAILSAFE_SCHEMA});
772+ } catch (err) {
773+ throw new Error(`Failed to parse build-secrets YAML: ${err.message}`);
774+ }
775+ if (!parsed) {
776+ return {};
777+ }
778+ if (!parsed || Array.isArray(parsed) || typeof parsed !== 'object') {
779+ throw new Error('build-secrets must be a YAML object');
780+ }
781+ const secrets = {};
782+ for (const [id, secret] of Object.entries(parsed)) {
783+ if (!/^[A-Za-z0-9_.-]+$/.test(id)) {
784+ throw new Error(`Invalid build secret id "${id}": use letters, digits, dots, underscores or dashes`);
785+ }
786+ if (typeof secret !== 'string') {
787+ throw new Error(`build-secrets value for "${id}" must be a string`);
788+ }
789+ if (secret.length === 0) {
790+ throw new Error(`build-secrets value for "${id}" must not be empty`);
791+ }
792+ core.setSecret(secret);
793+ secrets[id] = secret;
794+ }
795+ return secrets;
796+ };
797+ const toBuildSecretEnvName = (id, index) => `BUILD_SECRET_${index}_${id.toUpperCase().replace(/[^A-Z0-9_]/g, '_')}`;
750798
751799 const gitContextAttrs = GitHub.context.ref.startsWith('refs/tags/') ? {checksum: GitHub.context.sha} : {'fetch-by-commit': 'true'};
752800 const buildContext = await new Build().gitContext({subdir: inpContext, attrs: gitContextAttrs});
@@ -803,6 +851,26 @@ jobs:
803851 }
804852 core.setOutput('labels', labels.join('\n'));
805853 core.setOutput('build-args', buildArgs);
854+
855+ let buildSecrets;
856+ try {
857+ buildSecrets = parseBuildSecrets(inpBuildSecrets);
858+ } catch (err) {
859+ core.setFailed(err.message);
860+ return;
861+ }
862+ const envs = {
863+ BUILDKIT_MULTI_PLATFORM: '1',
864+ GIT_AUTH_TOKEN: inpGitHubToken
865+ };
866+ const secretEnvs = ['GIT_AUTH_TOKEN=GIT_AUTH_TOKEN'];
867+ Object.entries(buildSecrets).forEach(([id, secret], index) => {
868+ const envName = toBuildSecretEnvName(id, index);
869+ envs[envName] = secret;
870+ secretEnvs.push(`${id}=${envName}`);
871+ });
872+ core.setOutput('envs', JSON.stringify(envs));
873+ core.setOutput('secret-envs', secretEnvs.join('\n'));
806874
807875 if (GitHub.context.payload.repository?.private ?? false) {
808876 // if this is a private repository, we set min provenance mode
@@ -833,13 +901,11 @@ jobs:
833901 platforms : ${{ steps.prepare.outputs.platform }}
834902 provenance : ${{ steps.prepare.outputs.provenance }}
835903 sbom : ${{ steps.prepare.outputs.sbom }}
836- secret-envs : GIT_AUTH_TOKEN=GIT_AUTH_TOKEN
904+ secret-envs : ${{ steps.prepare.outputs.secret-envs }}
837905 shm-size : ${{ inputs.shm-size }}
838906 target : ${{ inputs.target }}
839907 ulimit : ${{ inputs.ulimit }}
840- env :
841- BUILDKIT_MULTI_PLATFORM : 1
842- GIT_AUTH_TOKEN : ${{ secrets.github-token || github.token }}
908+ env : ${{ fromJson(steps.prepare.outputs.envs || '{}') }}
843909 -
844910 name : Login to registry for signing
845911 if : ${{ needs.prepare.outputs.sign == 'true' && inputs.output == 'image' }}
0 commit comments