@rightkit/git 0.2.23 → 0.2.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/manifest.mjs +334 -55
- package/manifest.schema.json +30 -2
- package/manifest.test.mjs +115 -1
- package/ownership-registration.mjs +33 -0
- package/ownership-registration.test.mjs +20 -0
- package/pack-smoke.test.mjs +8 -8
- package/package.json +1 -1
- package/render.test.mjs +436 -22
- package/sync.test.mjs +3 -0
- package/templates/ci.yml +1 -1
- package/templates/package-smoke.yml +1 -0
- package/templates/profiles/rust-hybrid/ci.yml +2 -0
- package/templates/profiles/rust-hybrid/release-candidate.yml +119 -7
- package/templates/publish-npm.yml +1 -0
- package/templates/publish-package-managers.yml +1 -0
- package/templates/qualification.yml +1 -0
- package/templates/release-candidate.yml +1 -0
package/manifest.mjs
CHANGED
|
@@ -60,6 +60,7 @@ const TOP_LEVEL_KEYS = new Set([
|
|
|
60
60
|
"gate",
|
|
61
61
|
"rustCache",
|
|
62
62
|
"candidate",
|
|
63
|
+
"ownership",
|
|
63
64
|
]);
|
|
64
65
|
const GATE_PATH_RE = /^(?!.*\.\.)(?!.*\s)[A-Za-z0-9][A-Za-z0-9._/-]*$/;
|
|
65
66
|
const RUST_WORKSPACE_PATH_RE = /^(?:\.|(?!.*\.\.)(?!.*\s)[A-Za-z0-9][A-Za-z0-9._/-]*)$/;
|
|
@@ -188,9 +189,17 @@ export function validateManifest(manifest) {
|
|
|
188
189
|
errors.push("candidate must be an object");
|
|
189
190
|
} else {
|
|
190
191
|
for (const key of Object.keys(candidate)) {
|
|
191
|
-
if (!["buildScript", "checkScript", "artifactRoot", "os", "targets", "signWindows", "signMacos", "releaseChain"].includes(key)) errors.push(`unknown candidate field: ${key}`);
|
|
192
|
+
if (!["buildScript", "checkScript", "admissionScript", "stageSummaryScript", "artifactRoot", "os", "targets", "signWindows", "signMacos", "releaseChain"].includes(key)) errors.push(`unknown candidate field: ${key}`);
|
|
192
193
|
}
|
|
193
|
-
|
|
194
|
+
// admissionScript & stageSummaryScript are required for EVERY release-candidate
|
|
195
|
+
// lane, not just signed/full-releaseChain ones: the tag-last workflow's
|
|
196
|
+
// `admission` job (dispatch-envelope validation) and the `candidate` job's
|
|
197
|
+
// stage-summary init/finalize steps are unconditional parts of the rendered
|
|
198
|
+
// template, independent of candidate.signWindows/signMacos/releaseChain. If
|
|
199
|
+
// either script were only required inside the optional releaseChain object,
|
|
200
|
+
// an unsigned release-candidate manifest (no releaseChain at all) would still
|
|
201
|
+
// render those unconditional steps with an unresolved script name.
|
|
202
|
+
for (const key of ["buildScript", "checkScript", "admissionScript", "stageSummaryScript"]) {
|
|
194
203
|
if (typeof candidate[key] !== "string" || !PACKAGE_SCRIPT_RE.test(candidate[key])) {
|
|
195
204
|
errors.push(`candidate.${key} must be one safe package script name`);
|
|
196
205
|
}
|
|
@@ -252,19 +261,36 @@ export function validateManifest(manifest) {
|
|
|
252
261
|
}
|
|
253
262
|
if (candidate.releaseChain !== undefined) {
|
|
254
263
|
const chain = candidate.releaseChain;
|
|
255
|
-
|
|
264
|
+
// admissionScript & stageSummaryScript live on candidate directly (see
|
|
265
|
+
// above) — the full signed release chain adds evidenceVerificationScript
|
|
266
|
+
// (runs before publish, verifying aggregated same-run evidence) plus the
|
|
267
|
+
// finalize/qualify/publish scripts.
|
|
268
|
+
const allowedChainKeys = ["evidenceVerificationScript", "windowsFinalizeScript", "macosFinalizeScript", "installedQualificationScript", "publishScript", "qualificationRunnerVariable", "publishSecrets", "environments"];
|
|
256
269
|
if (chain === null || typeof chain !== "object" || Array.isArray(chain)) {
|
|
257
270
|
errors.push("candidate.releaseChain must be an object");
|
|
258
271
|
} else {
|
|
259
272
|
for (const key of Object.keys(chain)) {
|
|
260
273
|
if (!allowedChainKeys.includes(key)) errors.push(`unknown candidate.releaseChain field: ${key}`);
|
|
261
274
|
}
|
|
262
|
-
for (const key of ["windowsFinalizeScript", "installedQualificationScript", "publishScript"]) {
|
|
275
|
+
for (const key of ["evidenceVerificationScript", "windowsFinalizeScript", "installedQualificationScript", "publishScript"]) {
|
|
263
276
|
if (typeof chain[key] !== "string" || !PACKAGE_SCRIPT_RE.test(chain[key])) {
|
|
264
277
|
errors.push(`candidate.releaseChain.${key} must be one safe package script name`);
|
|
265
278
|
}
|
|
266
279
|
}
|
|
267
280
|
if (chain.macosFinalizeScript !== undefined && (typeof chain.macosFinalizeScript !== "string" || !PACKAGE_SCRIPT_RE.test(chain.macosFinalizeScript))) {
|
|
281
|
+
if (chain.environments !== undefined) {
|
|
282
|
+
const environments = chain.environments;
|
|
283
|
+
if (environments === null || typeof environments !== "object" || Array.isArray(environments)) {
|
|
284
|
+
errors.push("candidate.releaseChain.environments must be an object");
|
|
285
|
+
} else {
|
|
286
|
+
for (const key of Object.keys(environments)) {
|
|
287
|
+
if (!["signing", "qualification", "publication"].includes(key)) errors.push(`unknown candidate.releaseChain.environments field: ${key}`);
|
|
288
|
+
else if (typeof environments[key] !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,62}$/.test(environments[key])) {
|
|
289
|
+
errors.push(`candidate.releaseChain.environments.${key} must be one safe GitHub environment name`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
268
294
|
errors.push("candidate.releaseChain.macosFinalizeScript must be one safe package script name");
|
|
269
295
|
}
|
|
270
296
|
if (typeof chain.qualificationRunnerVariable !== "string" || !GITHUB_VARIABLE_RE.test(chain.qualificationRunnerVariable)) {
|
|
@@ -286,6 +312,18 @@ export function validateManifest(manifest) {
|
|
|
286
312
|
if (profile === "rust-hybrid" && lanes.includes("release-candidate") && candidate === undefined) {
|
|
287
313
|
errors.push("rust-hybrid release-candidate lane requires candidate { buildScript, checkScript, artifactRoot }");
|
|
288
314
|
}
|
|
315
|
+
|
|
316
|
+
if (manifest.ownership !== undefined) {
|
|
317
|
+
if (!manifest.ownership || typeof manifest.ownership !== "object" || Array.isArray(manifest.ownership)
|
|
318
|
+
|| Object.keys(manifest.ownership).some((key) => key !== "roots")) {
|
|
319
|
+
errors.push("ownership must be an object containing only roots");
|
|
320
|
+
} else if (!Array.isArray(manifest.ownership.roots) || manifest.ownership.roots.length === 0
|
|
321
|
+
|| manifest.ownership.roots.some((root) => typeof root !== "string" || (root !== "." && !/^(?!.*\.\.)(?!.*\\)(?!.*\s)[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(root)) || root.startsWith("/"))) {
|
|
322
|
+
errors.push("ownership.roots must contain safe, non-empty repository-relative paths");
|
|
323
|
+
} else if (new Set(manifest.ownership.roots).size !== manifest.ownership.roots.length) {
|
|
324
|
+
errors.push("ownership.roots must not contain duplicates");
|
|
325
|
+
}
|
|
326
|
+
}
|
|
289
327
|
if (profile !== "rust-hybrid" && candidate !== undefined) {
|
|
290
328
|
errors.push("candidate is only valid for profile 'rust-hybrid'");
|
|
291
329
|
}
|
|
@@ -452,6 +490,15 @@ const ARTIFACT_SIGNING_CLIENT_SHA256 = "74BD7D27E6CE1051409C38D9B46BC8DF0400ECD6
|
|
|
452
490
|
const WINDOWS_SDK_BUILD_TOOLS_VERSION = "10.0.26100.4188";
|
|
453
491
|
const WINDOWS_SDK_BUILD_TOOLS_SHA256 = "180DEB372659029864C10A0C04787833234D64AACD1D2C0661D2C00295D8E022";
|
|
454
492
|
|
|
493
|
+
// Signing and publication credentials must be reachable behind distinct
|
|
494
|
+
// protected environments, so a job that may sign cannot also publish. Products
|
|
495
|
+
// opt in per environment; absent configuration keeps the single `release`
|
|
496
|
+
// environment they use today, so enabling the split cannot strand secrets.
|
|
497
|
+
function releaseEnvironment(manifest, role) {
|
|
498
|
+
const configured = manifest?.candidate?.releaseChain?.environments?.[role];
|
|
499
|
+
return typeof configured === "string" && configured.length > 0 ? configured : "release";
|
|
500
|
+
}
|
|
501
|
+
|
|
455
502
|
function windowsSigningJob(manifest, pm, artifactPrefix) {
|
|
456
503
|
if (manifest.candidate?.signWindows !== true) return "";
|
|
457
504
|
const nodeVersion = String(manifest.matrix.node[0]);
|
|
@@ -462,7 +509,7 @@ function windowsSigningJob(manifest, pm, artifactPrefix) {
|
|
|
462
509
|
const finalizeEnvironment = releaseChain
|
|
463
510
|
? ` RIGHT_GIT_UNSIGNED_CANDIDATE_ROOT: \${{ runner.temp }}/unsigned-windows-candidate
|
|
464
511
|
RIGHT_GIT_FINALIZED_WINDOWS_ROOT: \${{ runner.temp }}/finalized-windows
|
|
465
|
-
RIGHT_GIT_SOURCE_REVISION: \${{
|
|
512
|
+
RIGHT_GIT_SOURCE_REVISION: \${{ needs.admission.outputs.source_revision }}`
|
|
466
513
|
: ` LEGION_UNSIGNED_CANDIDATE_ROOT: \${{ runner.temp }}/unsigned-windows-candidate
|
|
467
514
|
LEGION_SOURCE_REVISION: \${{ env.RIGHT_GIT_SOURCE_REVISION }}`;
|
|
468
515
|
const uploadPath = releaseChain
|
|
@@ -470,9 +517,9 @@ function windowsSigningJob(manifest, pm, artifactPrefix) {
|
|
|
470
517
|
: " dist/releases/windows/**\n .right-release/receipts/windows-*.json";
|
|
471
518
|
return `
|
|
472
519
|
windows-sign:
|
|
473
|
-
needs: [candidate]
|
|
474
|
-
if: \${{
|
|
475
|
-
environment:
|
|
520
|
+
needs: [admission, candidate]
|
|
521
|
+
if: \${{ needs.admission.outputs.signed_qualification == 'true' }}
|
|
522
|
+
environment: ${releaseEnvironment(manifest, "signing")}
|
|
476
523
|
strategy:
|
|
477
524
|
fail-fast: false
|
|
478
525
|
matrix:
|
|
@@ -483,9 +530,10 @@ ${signingMatrix(manifest, "windows")}
|
|
|
483
530
|
id-token: write
|
|
484
531
|
steps:
|
|
485
532
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
533
|
+
with:
|
|
534
|
+
ref: \${{ needs.admission.outputs.source_revision }}
|
|
535
|
+
fetch-depth: 0
|
|
536
|
+
persist-credentials: false
|
|
489
537
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
490
538
|
with:
|
|
491
539
|
node-version: ${nodeVersion}
|
|
@@ -495,10 +543,24 @@ ${pm.setup}
|
|
|
495
543
|
with:
|
|
496
544
|
targets: \${{ matrix.targetTriple }}
|
|
497
545
|
- run: ${pm.installCommand}
|
|
546
|
+
- name: Initialize Windows signing stage summary
|
|
547
|
+
run: ${pm.runPrefix} ${manifest.candidate.stageSummaryScript}
|
|
548
|
+
env:
|
|
549
|
+
RIGHT_GIT_STAGE_ACTION: init
|
|
550
|
+
RIGHT_GIT_STAGE_ROOT: \${{ runner.temp }}/right-git-stage-summary
|
|
551
|
+
RIGHT_GIT_STAGE_EVIDENCE_ROOT: \${{ runner.temp }}/finalized-windows
|
|
552
|
+
RIGHT_GIT_STAGE: windows-sign
|
|
553
|
+
RIGHT_GIT_STAGE_PRODUCER: windows-sign-\${{ matrix.architecture }}
|
|
554
|
+
RIGHT_GIT_RELEASE_VERSION: \${{ needs.admission.outputs.version }}
|
|
555
|
+
RIGHT_GIT_SOURCE_REVISION: \${{ needs.admission.outputs.source_revision }}
|
|
556
|
+
RIGHT_GIT_RELEASE_PLATFORM: \${{ matrix.platform }}
|
|
557
|
+
RIGHT_GIT_RELEASE_ARCHITECTURE: \${{ matrix.architecture }}
|
|
558
|
+
RIGHT_GIT_RUN_ID: \${{ github.run_id }}
|
|
559
|
+
RIGHT_GIT_RUN_ATTEMPT: \${{ github.run_attempt }}
|
|
498
560
|
- name: Download exact unsigned Windows candidate
|
|
499
561
|
uses: actions/download-artifact@${DOWNLOAD_ARTIFACT_SHA} # v4.3.0
|
|
500
562
|
with:
|
|
501
|
-
name: ${artifactPrefix}-
|
|
563
|
+
name: ${artifactPrefix}-\${{ github.run_id }}-\${{ github.run_attempt }}-candidate-\${{ matrix.platform }}-\${{ matrix.architecture }}-\${{ needs.admission.outputs.version }}-\${{ needs.admission.outputs.source_revision }}
|
|
502
564
|
path: \${{ runner.temp }}/unsigned-windows-candidate
|
|
503
565
|
- name: Authenticate to Azure with GitHub OIDC
|
|
504
566
|
uses: azure/login@${AZURE_LOGIN_SHA} # v3
|
|
@@ -531,13 +593,38 @@ ${finalizeEnvironment}
|
|
|
531
593
|
AZURE_ARTIFACT_SIGNING_ENDPOINT: \${{ vars.AZURE_ARTIFACT_SIGNING_ENDPOINT }}
|
|
532
594
|
AZURE_ARTIFACT_SIGNING_ACCOUNT: \${{ vars.AZURE_ARTIFACT_SIGNING_ACCOUNT }}
|
|
533
595
|
AZURE_ARTIFACT_SIGNING_PROFILE: \${{ vars.AZURE_ARTIFACT_SIGNING_PROFILE }}
|
|
596
|
+
- name: Finalize Windows signing stage summary
|
|
597
|
+
if: \${{ always() }}
|
|
598
|
+
run: ${pm.runPrefix} ${manifest.candidate.stageSummaryScript}
|
|
599
|
+
env:
|
|
600
|
+
RIGHT_GIT_STAGE_ACTION: finalize
|
|
601
|
+
RIGHT_GIT_STAGE_ROOT: \${{ runner.temp }}/right-git-stage-summary
|
|
602
|
+
RIGHT_GIT_STAGE_EVIDENCE_ROOT: \${{ runner.temp }}/finalized-windows
|
|
603
|
+
RIGHT_GIT_STAGE: windows-sign
|
|
604
|
+
RIGHT_GIT_STAGE_PRODUCER: windows-sign-\${{ matrix.architecture }}
|
|
605
|
+
RIGHT_GIT_STAGE_STATUS: \${{ job.status == 'success' && 'SUCCEEDED' || 'FAILED' }}
|
|
606
|
+
RIGHT_GIT_STAGE_EXIT_CODE: \${{ job.status == 'success' && '0' || '1' }}
|
|
607
|
+
RIGHT_GIT_RELEASE_VERSION: \${{ needs.admission.outputs.version }}
|
|
608
|
+
RIGHT_GIT_SOURCE_REVISION: \${{ needs.admission.outputs.source_revision }}
|
|
609
|
+
RIGHT_GIT_RELEASE_PLATFORM: \${{ matrix.platform }}
|
|
610
|
+
RIGHT_GIT_RELEASE_ARCHITECTURE: \${{ matrix.architecture }}
|
|
611
|
+
RIGHT_GIT_RUN_ID: \${{ github.run_id }}
|
|
612
|
+
RIGHT_GIT_RUN_ATTEMPT: \${{ github.run_attempt }}
|
|
534
613
|
- name: Upload signed Windows candidate
|
|
614
|
+
if: \${{ always() }}
|
|
535
615
|
uses: actions/upload-artifact@${UPLOAD_ARTIFACT_SHA} # v4
|
|
536
616
|
with:
|
|
537
|
-
name: ${artifactPrefix}-signed-windows-\${{ matrix.architecture }}
|
|
617
|
+
name: ${artifactPrefix}-\${{ github.run_id }}-\${{ github.run_attempt }}-signed-windows-\${{ matrix.architecture }}-\${{ needs.admission.outputs.version }}-\${{ needs.admission.outputs.source_revision }}
|
|
538
618
|
path: |
|
|
539
619
|
${uploadPath}
|
|
540
620
|
if-no-files-found: error
|
|
621
|
+
- name: Upload Windows signing stage summary
|
|
622
|
+
if: \${{ always() }}
|
|
623
|
+
uses: actions/upload-artifact@${UPLOAD_ARTIFACT_SHA} # v4
|
|
624
|
+
with:
|
|
625
|
+
name: ${artifactPrefix}-\${{ github.run_id }}-\${{ github.run_attempt }}-summary-windows-sign-\${{ matrix.architecture }}-\${{ needs.admission.outputs.version }}-\${{ needs.admission.outputs.source_revision }}
|
|
626
|
+
path: \${{ runner.temp }}/right-git-stage-summary/stage-summary.json
|
|
627
|
+
if-no-files-found: error
|
|
541
628
|
`.trimEnd();
|
|
542
629
|
}
|
|
543
630
|
|
|
@@ -551,7 +638,7 @@ function macosSigningJob(manifest, pm, artifactPrefix) {
|
|
|
551
638
|
const finalizeEnvironment = releaseChain
|
|
552
639
|
? ` RIGHT_GIT_UNSIGNED_CANDIDATE_ROOT: \${{ runner.temp }}/unsigned-macos-candidate
|
|
553
640
|
RIGHT_GIT_FINALIZED_MACOS_ROOT: \${{ runner.temp }}/finalized-macos
|
|
554
|
-
RIGHT_GIT_SOURCE_REVISION: \${{
|
|
641
|
+
RIGHT_GIT_SOURCE_REVISION: \${{ needs.admission.outputs.source_revision }}`
|
|
555
642
|
: ` LEGION_UNSIGNED_CANDIDATE_ROOT: \${{ runner.temp }}/unsigned-macos-candidate
|
|
556
643
|
LEGION_SOURCE_REVISION: \${{ env.RIGHT_GIT_SOURCE_REVISION }}`;
|
|
557
644
|
const uploadPath = releaseChain
|
|
@@ -559,9 +646,9 @@ function macosSigningJob(manifest, pm, artifactPrefix) {
|
|
|
559
646
|
: " dist/releases/mac/**\n .right-release/receipts/macos-*.json";
|
|
560
647
|
return `
|
|
561
648
|
macos-sign:
|
|
562
|
-
needs: [candidate]
|
|
563
|
-
if: \${{
|
|
564
|
-
environment:
|
|
649
|
+
needs: [admission, candidate]
|
|
650
|
+
if: \${{ needs.admission.outputs.signed_qualification == 'true' }}
|
|
651
|
+
environment: ${releaseEnvironment(manifest, "signing")}
|
|
565
652
|
strategy:
|
|
566
653
|
fail-fast: false
|
|
567
654
|
matrix:
|
|
@@ -571,9 +658,10 @@ ${signingMatrix(manifest, "macos")}
|
|
|
571
658
|
contents: read
|
|
572
659
|
steps:
|
|
573
660
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
661
|
+
with:
|
|
662
|
+
ref: \${{ needs.admission.outputs.source_revision }}
|
|
663
|
+
fetch-depth: 0
|
|
664
|
+
persist-credentials: false
|
|
577
665
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
578
666
|
with:
|
|
579
667
|
node-version: ${nodeVersion}
|
|
@@ -583,10 +671,24 @@ ${pm.setup}
|
|
|
583
671
|
with:
|
|
584
672
|
targets: \${{ matrix.targetTriple }}
|
|
585
673
|
- run: ${pm.installCommand}
|
|
674
|
+
- name: Initialize macOS signing stage summary
|
|
675
|
+
run: ${pm.runPrefix} ${manifest.candidate.stageSummaryScript}
|
|
676
|
+
env:
|
|
677
|
+
RIGHT_GIT_STAGE_ACTION: init
|
|
678
|
+
RIGHT_GIT_STAGE_ROOT: \${{ runner.temp }}/right-git-stage-summary
|
|
679
|
+
RIGHT_GIT_STAGE_EVIDENCE_ROOT: \${{ runner.temp }}/finalized-macos
|
|
680
|
+
RIGHT_GIT_STAGE: macos-sign
|
|
681
|
+
RIGHT_GIT_STAGE_PRODUCER: macos-sign-\${{ matrix.architecture }}
|
|
682
|
+
RIGHT_GIT_RELEASE_VERSION: \${{ needs.admission.outputs.version }}
|
|
683
|
+
RIGHT_GIT_SOURCE_REVISION: \${{ needs.admission.outputs.source_revision }}
|
|
684
|
+
RIGHT_GIT_RELEASE_PLATFORM: \${{ matrix.platform }}
|
|
685
|
+
RIGHT_GIT_RELEASE_ARCHITECTURE: \${{ matrix.architecture }}
|
|
686
|
+
RIGHT_GIT_RUN_ID: \${{ github.run_id }}
|
|
687
|
+
RIGHT_GIT_RUN_ATTEMPT: \${{ github.run_attempt }}
|
|
586
688
|
- name: Download exact unsigned macOS candidate
|
|
587
689
|
uses: actions/download-artifact@${DOWNLOAD_ARTIFACT_SHA} # v4.3.0
|
|
588
690
|
with:
|
|
589
|
-
name: ${artifactPrefix}-
|
|
691
|
+
name: ${artifactPrefix}-\${{ github.run_id }}-\${{ github.run_attempt }}-candidate-\${{ matrix.platform }}-\${{ matrix.architecture }}-\${{ needs.admission.outputs.version }}-\${{ needs.admission.outputs.source_revision }}
|
|
590
692
|
path: \${{ runner.temp }}/unsigned-macos-candidate
|
|
591
693
|
- name: Install Apple signing identity in temporary keychain
|
|
592
694
|
shell: bash
|
|
@@ -618,13 +720,38 @@ ${finalizeEnvironment}
|
|
|
618
720
|
APPLE_DEVELOPER_ID: \${{ vars.APPLE_DEVELOPER_ID }}
|
|
619
721
|
APPLE_API_KEY: \${{ secrets.APPLE_API_KEY }}
|
|
620
722
|
APPLE_API_ISSUER: \${{ secrets.APPLE_API_ISSUER }}
|
|
723
|
+
- name: Finalize macOS signing stage summary
|
|
724
|
+
if: \${{ always() }}
|
|
725
|
+
run: ${pm.runPrefix} ${manifest.candidate.stageSummaryScript}
|
|
726
|
+
env:
|
|
727
|
+
RIGHT_GIT_STAGE_ACTION: finalize
|
|
728
|
+
RIGHT_GIT_STAGE_ROOT: \${{ runner.temp }}/right-git-stage-summary
|
|
729
|
+
RIGHT_GIT_STAGE_EVIDENCE_ROOT: \${{ runner.temp }}/finalized-macos
|
|
730
|
+
RIGHT_GIT_STAGE: macos-sign
|
|
731
|
+
RIGHT_GIT_STAGE_PRODUCER: macos-sign-\${{ matrix.architecture }}
|
|
732
|
+
RIGHT_GIT_STAGE_STATUS: \${{ job.status == 'success' && 'SUCCEEDED' || 'FAILED' }}
|
|
733
|
+
RIGHT_GIT_STAGE_EXIT_CODE: \${{ job.status == 'success' && '0' || '1' }}
|
|
734
|
+
RIGHT_GIT_RELEASE_VERSION: \${{ needs.admission.outputs.version }}
|
|
735
|
+
RIGHT_GIT_SOURCE_REVISION: \${{ needs.admission.outputs.source_revision }}
|
|
736
|
+
RIGHT_GIT_RELEASE_PLATFORM: \${{ matrix.platform }}
|
|
737
|
+
RIGHT_GIT_RELEASE_ARCHITECTURE: \${{ matrix.architecture }}
|
|
738
|
+
RIGHT_GIT_RUN_ID: \${{ github.run_id }}
|
|
739
|
+
RIGHT_GIT_RUN_ATTEMPT: \${{ github.run_attempt }}
|
|
621
740
|
- name: Upload signed & notarized macOS candidate
|
|
741
|
+
if: \${{ always() }}
|
|
622
742
|
uses: actions/upload-artifact@${UPLOAD_ARTIFACT_SHA} # v4
|
|
623
743
|
with:
|
|
624
|
-
name: ${artifactPrefix}-signed-macos-\${{ matrix.architecture }}
|
|
744
|
+
name: ${artifactPrefix}-\${{ github.run_id }}-\${{ github.run_attempt }}-signed-macos-\${{ matrix.architecture }}-\${{ needs.admission.outputs.version }}-\${{ needs.admission.outputs.source_revision }}
|
|
625
745
|
path: |
|
|
626
746
|
${uploadPath}
|
|
627
747
|
if-no-files-found: error
|
|
748
|
+
- name: Upload macOS signing stage summary
|
|
749
|
+
if: \${{ always() }}
|
|
750
|
+
uses: actions/upload-artifact@${UPLOAD_ARTIFACT_SHA} # v4
|
|
751
|
+
with:
|
|
752
|
+
name: ${artifactPrefix}-\${{ github.run_id }}-\${{ github.run_attempt }}-summary-macos-sign-\${{ matrix.architecture }}-\${{ needs.admission.outputs.version }}-\${{ needs.admission.outputs.source_revision }}
|
|
753
|
+
path: \${{ runner.temp }}/right-git-stage-summary/stage-summary.json
|
|
754
|
+
if-no-files-found: error
|
|
628
755
|
- name: Remove Apple signing material
|
|
629
756
|
if: \${{ always() }}
|
|
630
757
|
shell: bash
|
|
@@ -641,30 +768,72 @@ function releaseChainJobs(manifest, pm, artifactPrefix) {
|
|
|
641
768
|
const windowsRunner = windowsTarget.os;
|
|
642
769
|
const windowsArchitecture = windowsTarget.architecture;
|
|
643
770
|
const macosTarget = candidateTargets(manifest.candidate).find((target) => target.platform === "macos");
|
|
644
|
-
const
|
|
645
|
-
const macosDependency =
|
|
646
|
-
const macosSuccess =
|
|
647
|
-
|
|
771
|
+
const signMacos = manifest.candidate.signMacos === true;
|
|
772
|
+
const macosDependency = signMacos ? ", macos-sign" : "";
|
|
773
|
+
const macosSuccess = signMacos ? " && needs.macos-sign.result == 'success'" : "";
|
|
774
|
+
// Run- and identity-bound: matches exactly what windows-sign/macos-sign
|
|
775
|
+
// uploaded (see windowsSigningJob/macosSigningJob) — no matrix context is
|
|
776
|
+
// available in these non-matrixed jobs, so the platform architecture is a
|
|
777
|
+
// literal derived from the manifest's single windows/macos target.
|
|
778
|
+
const VERSION = "${{ needs.admission.outputs.version }}";
|
|
779
|
+
const SOURCE_REVISION = "${{ needs.admission.outputs.source_revision }}";
|
|
780
|
+
const windowsSignedArtifact = `${artifactPrefix}-\${{ github.run_id }}-\${{ github.run_attempt }}-signed-windows-${windowsArchitecture}-${VERSION}-${SOURCE_REVISION}`;
|
|
781
|
+
const windowsSummaryArtifact = `${artifactPrefix}-\${{ github.run_id }}-\${{ github.run_attempt }}-summary-windows-sign-${windowsArchitecture}-${VERSION}-${SOURCE_REVISION}`;
|
|
782
|
+
const macosSignedArtifact = signMacos ? `${artifactPrefix}-\${{ github.run_id }}-\${{ github.run_attempt }}-signed-macos-${macosTarget.architecture}-${VERSION}-${SOURCE_REVISION}` : "";
|
|
783
|
+
const macosSummaryArtifact = signMacos ? `${artifactPrefix}-\${{ github.run_id }}-\${{ github.run_attempt }}-summary-macos-sign-${macosTarget.architecture}-${VERSION}-${SOURCE_REVISION}` : "";
|
|
784
|
+
// Candidate stage summaries are per (platform, architecture) — see the
|
|
785
|
+
// `Upload candidate stage summary` step in the template. releaseChain
|
|
786
|
+
// requires at most one target per signing platform (validateManifest), so
|
|
787
|
+
// exactly one candidate-windows summary and at most one candidate-macos
|
|
788
|
+
// summary exist for this manifest.
|
|
789
|
+
const candidateWindowsSummaryArtifact = `${artifactPrefix}-\${{ github.run_id }}-\${{ github.run_attempt }}-summary-candidate-windows-${windowsArchitecture}-${VERSION}-${SOURCE_REVISION}`;
|
|
790
|
+
const candidateMacosSummaryArtifact = signMacos ? `${artifactPrefix}-\${{ github.run_id }}-\${{ github.run_attempt }}-summary-candidate-macos-${macosTarget.architecture}-${VERSION}-${SOURCE_REVISION}` : "";
|
|
791
|
+
const qualificationArtifact = `${artifactPrefix}-\${{ github.run_id }}-\${{ github.run_attempt }}-installed-qualification-${VERSION}-${SOURCE_REVISION}`;
|
|
792
|
+
const qualificationSummaryArtifact = `${artifactPrefix}-\${{ github.run_id }}-\${{ github.run_attempt }}-summary-installed-qualification-${VERSION}-${SOURCE_REVISION}`;
|
|
793
|
+
const macosDownload = signMacos
|
|
648
794
|
? ` - name: Download same-run signed & notarized macOS release
|
|
649
795
|
uses: actions/download-artifact@${DOWNLOAD_ARTIFACT_SHA} # v4.3.0
|
|
650
796
|
with:
|
|
651
|
-
name: ${
|
|
797
|
+
name: ${macosSignedArtifact}
|
|
652
798
|
path: \${{ runner.temp }}/right-git-release/macos`
|
|
653
799
|
: "";
|
|
800
|
+
const macosSummaryDownload = signMacos
|
|
801
|
+
? ` - name: Download same-run macOS signing stage summary
|
|
802
|
+
uses: actions/download-artifact@${DOWNLOAD_ARTIFACT_SHA} # v4.3.0
|
|
803
|
+
with:
|
|
804
|
+
name: ${macosSummaryArtifact}
|
|
805
|
+
path: \${{ runner.temp }}/right-git-release/stage-summaries/macos-sign`
|
|
806
|
+
: "";
|
|
807
|
+
const candidateMacosSummaryDownload = signMacos
|
|
808
|
+
? ` - name: Download same-run candidate (macOS) stage summary
|
|
809
|
+
uses: actions/download-artifact@${DOWNLOAD_ARTIFACT_SHA} # v4.3.0
|
|
810
|
+
with:
|
|
811
|
+
name: ${candidateMacosSummaryArtifact}
|
|
812
|
+
path: \${{ runner.temp }}/right-git-release/stage-summaries/candidate-macos`
|
|
813
|
+
: "";
|
|
814
|
+
const finalizedMacosEnv = signMacos ? `\n RIGHT_GIT_FINALIZED_MACOS_ROOT: \${{ runner.temp }}/right-git-release/macos` : "";
|
|
815
|
+
// Evidence must be bound to this exact run — not merely this
|
|
816
|
+
// version+source_revision — so a stale artifact from a different run
|
|
817
|
+
// cannot satisfy verification. Every job/step from installed-qualification
|
|
818
|
+
// onward carries RIGHT_GIT_RUN_ID/RIGHT_GIT_RUN_ATTEMPT alongside version
|
|
819
|
+
// and source_revision.
|
|
820
|
+
const runIdentityEnv = ` RIGHT_GIT_RUN_ID: \${{ github.run_id }}
|
|
821
|
+
RIGHT_GIT_RUN_ATTEMPT: \${{ github.run_attempt }}`;
|
|
654
822
|
const publishSecretEnvironment = (chain.publishSecrets ?? [])
|
|
655
823
|
.map((name) => ` ${name}: \${{ secrets.${name} }}`)
|
|
656
824
|
.join("\n");
|
|
657
825
|
return `
|
|
658
826
|
installed-qualification:
|
|
659
|
-
needs: [candidate, windows-sign]
|
|
660
|
-
if: \${{ always() &&
|
|
661
|
-
environment:
|
|
827
|
+
needs: [admission, candidate, windows-sign]
|
|
828
|
+
if: \${{ always() && needs.admission.outputs.signed_qualification == 'true' && needs.candidate.result == 'success' }}
|
|
829
|
+
environment: ${releaseEnvironment(manifest, "qualification")}
|
|
662
830
|
runs-on: \${{ vars.${chain.qualificationRunnerVariable} || '${windowsRunner}' }}
|
|
663
831
|
steps:
|
|
664
832
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
833
|
+
with:
|
|
834
|
+
ref: \${{ needs.admission.outputs.source_revision }}
|
|
835
|
+
fetch-depth: 0
|
|
836
|
+
persist-credentials: false
|
|
668
837
|
- name: Fail closed when protected finalization is absent
|
|
669
838
|
if: \${{ needs.windows-sign.result != 'success' }}
|
|
670
839
|
shell: pwsh
|
|
@@ -673,64 +842,128 @@ function releaseChainJobs(manifest, pm, artifactPrefix) {
|
|
|
673
842
|
- name: Download same-run finalized Windows release
|
|
674
843
|
uses: actions/download-artifact@${DOWNLOAD_ARTIFACT_SHA} # v4.3.0
|
|
675
844
|
with:
|
|
676
|
-
name: ${
|
|
845
|
+
name: ${windowsSignedArtifact}
|
|
677
846
|
path: \${{ runner.temp }}/right-git-release/windows
|
|
678
847
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
679
848
|
with:
|
|
680
|
-
node-version: ${
|
|
849
|
+
node-version: ${String(manifest.matrix.node[0])}
|
|
681
850
|
package-manager-cache: false
|
|
682
851
|
${pm.setup}
|
|
683
852
|
- run: ${pm.installCommand}
|
|
853
|
+
- name: Initialize installed-qualification stage summary
|
|
854
|
+
run: ${pm.runPrefix} ${manifest.candidate.stageSummaryScript}
|
|
855
|
+
env:
|
|
856
|
+
RIGHT_GIT_STAGE_ACTION: init
|
|
857
|
+
RIGHT_GIT_STAGE_ROOT: \${{ runner.temp }}/right-git-stage-summary
|
|
858
|
+
RIGHT_GIT_STAGE_EVIDENCE_ROOT: \${{ runner.temp }}/right-git-release/qualification
|
|
859
|
+
RIGHT_GIT_STAGE: installed-qualification
|
|
860
|
+
RIGHT_GIT_STAGE_PRODUCER: installed-qualification
|
|
861
|
+
RIGHT_GIT_RELEASE_VERSION: \${{ needs.admission.outputs.version }}
|
|
862
|
+
RIGHT_GIT_SOURCE_REVISION: \${{ needs.admission.outputs.source_revision }}
|
|
863
|
+
${runIdentityEnv}
|
|
684
864
|
- name: Run installed-artifact qualification
|
|
685
865
|
run: ${pm.runPrefix} ${chain.installedQualificationScript}
|
|
686
866
|
env:
|
|
687
867
|
GH_TOKEN: \${{ github.token }}
|
|
688
868
|
RIGHT_GIT_FINALIZED_WINDOWS_ROOT: \${{ runner.temp }}/right-git-release/windows
|
|
689
869
|
RIGHT_GIT_QUALIFICATION_EVIDENCE_ROOT: \${{ runner.temp }}/right-git-release/qualification
|
|
690
|
-
|
|
870
|
+
RIGHT_GIT_RELEASE_VERSION: \${{ needs.admission.outputs.version }}
|
|
871
|
+
RIGHT_GIT_SOURCE_REVISION: \${{ needs.admission.outputs.source_revision }}
|
|
872
|
+
- name: Finalize installed-qualification stage summary
|
|
873
|
+
if: \${{ always() }}
|
|
874
|
+
run: ${pm.runPrefix} ${manifest.candidate.stageSummaryScript}
|
|
875
|
+
env:
|
|
876
|
+
RIGHT_GIT_STAGE_ACTION: finalize
|
|
877
|
+
RIGHT_GIT_STAGE_ROOT: \${{ runner.temp }}/right-git-stage-summary
|
|
878
|
+
RIGHT_GIT_STAGE_EVIDENCE_ROOT: \${{ runner.temp }}/right-git-release/qualification
|
|
879
|
+
RIGHT_GIT_STAGE: installed-qualification
|
|
880
|
+
RIGHT_GIT_STAGE_PRODUCER: installed-qualification
|
|
881
|
+
RIGHT_GIT_STAGE_STATUS: \${{ job.status == 'success' && 'SUCCEEDED' || 'FAILED' }}
|
|
882
|
+
RIGHT_GIT_STAGE_EXIT_CODE: \${{ job.status == 'success' && '0' || '1' }}
|
|
883
|
+
RIGHT_GIT_RELEASE_VERSION: \${{ needs.admission.outputs.version }}
|
|
884
|
+
RIGHT_GIT_SOURCE_REVISION: \${{ needs.admission.outputs.source_revision }}
|
|
885
|
+
${runIdentityEnv}
|
|
691
886
|
- name: Upload installed qualification evidence
|
|
887
|
+
if: \${{ always() }}
|
|
692
888
|
uses: actions/upload-artifact@${UPLOAD_ARTIFACT_SHA} # v4
|
|
693
889
|
with:
|
|
694
|
-
name: ${
|
|
890
|
+
name: ${qualificationArtifact}
|
|
695
891
|
path: \${{ runner.temp }}/right-git-release/qualification/**
|
|
696
892
|
if-no-files-found: error
|
|
893
|
+
- name: Upload installed-qualification stage summary
|
|
894
|
+
if: \${{ always() }}
|
|
895
|
+
uses: actions/upload-artifact@${UPLOAD_ARTIFACT_SHA} # v4
|
|
896
|
+
with:
|
|
897
|
+
name: ${qualificationSummaryArtifact}
|
|
898
|
+
path: \${{ runner.temp }}/right-git-stage-summary/stage-summary.json
|
|
899
|
+
if-no-files-found: error
|
|
697
900
|
publish:
|
|
698
|
-
needs: [installed-qualification${macosDependency}]
|
|
699
|
-
if: \${{
|
|
700
|
-
environment:
|
|
901
|
+
needs: [admission, installed-qualification${macosDependency}]
|
|
902
|
+
if: \${{ needs.admission.outputs.publish == 'true' && needs.installed-qualification.result == 'success'${macosSuccess} }}
|
|
903
|
+
environment: ${releaseEnvironment(manifest, "publication")}
|
|
701
904
|
runs-on: \${{ vars.${chain.qualificationRunnerVariable} || '${windowsRunner}' }}
|
|
702
905
|
permissions:
|
|
703
906
|
contents: write
|
|
704
907
|
steps:
|
|
705
908
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
909
|
+
with:
|
|
910
|
+
ref: \${{ needs.admission.outputs.source_revision }}
|
|
911
|
+
fetch-depth: 0
|
|
912
|
+
persist-credentials: false
|
|
709
913
|
- name: Download same-run finalized Windows release
|
|
710
914
|
uses: actions/download-artifact@${DOWNLOAD_ARTIFACT_SHA} # v4.3.0
|
|
711
915
|
with:
|
|
712
|
-
name: ${
|
|
916
|
+
name: ${windowsSignedArtifact}
|
|
713
917
|
path: \${{ runner.temp }}/right-git-release/windows
|
|
714
918
|
${macosDownload}
|
|
715
919
|
- name: Download same-run installed qualification evidence
|
|
716
920
|
uses: actions/download-artifact@${DOWNLOAD_ARTIFACT_SHA} # v4.3.0
|
|
717
921
|
with:
|
|
718
|
-
name: ${
|
|
922
|
+
name: ${qualificationArtifact}
|
|
719
923
|
path: \${{ runner.temp }}/right-git-release/qualification
|
|
924
|
+
- name: Download same-run candidate (Windows) stage summary
|
|
925
|
+
uses: actions/download-artifact@${DOWNLOAD_ARTIFACT_SHA} # v4.3.0
|
|
926
|
+
with:
|
|
927
|
+
name: ${candidateWindowsSummaryArtifact}
|
|
928
|
+
path: \${{ runner.temp }}/right-git-release/stage-summaries/candidate-windows
|
|
929
|
+
${candidateMacosSummaryDownload}
|
|
930
|
+
- name: Download same-run Windows signing stage summary
|
|
931
|
+
uses: actions/download-artifact@${DOWNLOAD_ARTIFACT_SHA} # v4.3.0
|
|
932
|
+
with:
|
|
933
|
+
name: ${windowsSummaryArtifact}
|
|
934
|
+
path: \${{ runner.temp }}/right-git-release/stage-summaries/windows-sign
|
|
935
|
+
${macosSummaryDownload}
|
|
936
|
+
- name: Download same-run installed-qualification stage summary
|
|
937
|
+
uses: actions/download-artifact@${DOWNLOAD_ARTIFACT_SHA} # v4.3.0
|
|
938
|
+
with:
|
|
939
|
+
name: ${qualificationSummaryArtifact}
|
|
940
|
+
path: \${{ runner.temp }}/right-git-release/stage-summaries/installed-qualification
|
|
720
941
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
721
942
|
with:
|
|
722
|
-
node-version: ${
|
|
943
|
+
node-version: ${String(manifest.matrix.node[0])}
|
|
723
944
|
package-manager-cache: false
|
|
724
945
|
${pm.setup}
|
|
725
946
|
- run: ${pm.installCommand}
|
|
947
|
+
- name: Verify aggregated release evidence
|
|
948
|
+
run: ${pm.runPrefix} ${chain.evidenceVerificationScript}
|
|
949
|
+
env:
|
|
950
|
+
GH_TOKEN: \${{ github.token }}
|
|
951
|
+
RIGHT_GIT_FINALIZED_WINDOWS_ROOT: \${{ runner.temp }}/right-git-release/windows${finalizedMacosEnv}
|
|
952
|
+
RIGHT_GIT_QUALIFICATION_EVIDENCE_ROOT: \${{ runner.temp }}/right-git-release/qualification
|
|
953
|
+
RIGHT_GIT_STAGE_SUMMARY_ROOT: \${{ runner.temp }}/right-git-release/stage-summaries
|
|
954
|
+
RIGHT_GIT_RELEASE_VERSION: \${{ needs.admission.outputs.version }}
|
|
955
|
+
RIGHT_GIT_SOURCE_REVISION: \${{ needs.admission.outputs.source_revision }}
|
|
956
|
+
${runIdentityEnv}
|
|
726
957
|
- name: Publish qualified signed release
|
|
727
958
|
run: ${pm.runPrefix} ${chain.publishScript}
|
|
728
959
|
env:
|
|
729
960
|
GH_TOKEN: \${{ github.token }}
|
|
730
|
-
RIGHT_GIT_FINALIZED_WINDOWS_ROOT: \${{ runner.temp }}/right-git-release/windows
|
|
731
|
-
RIGHT_GIT_FINALIZED_MACOS_ROOT: \${{ runner.temp }}/right-git-release/macos
|
|
961
|
+
RIGHT_GIT_FINALIZED_WINDOWS_ROOT: \${{ runner.temp }}/right-git-release/windows${finalizedMacosEnv}
|
|
732
962
|
RIGHT_GIT_QUALIFICATION_EVIDENCE_ROOT: \${{ runner.temp }}/right-git-release/qualification
|
|
733
|
-
|
|
963
|
+
RIGHT_GIT_STAGE_SUMMARY_ROOT: \${{ runner.temp }}/right-git-release/stage-summaries
|
|
964
|
+
RIGHT_GIT_RELEASE_VERSION: \${{ needs.admission.outputs.version }}
|
|
965
|
+
RIGHT_GIT_SOURCE_REVISION: \${{ needs.admission.outputs.source_revision }}
|
|
966
|
+
${runIdentityEnv}
|
|
734
967
|
${publishSecretEnvironment}
|
|
735
968
|
`.trimEnd();
|
|
736
969
|
}
|
|
@@ -756,6 +989,15 @@ export function deriveProjectName(repoPath) {
|
|
|
756
989
|
* Build the flat placeholder -> value map used by render.mjs, from a
|
|
757
990
|
* validated manifest plus the target repo's derived project name.
|
|
758
991
|
*/
|
|
992
|
+
/** Every managed public workflow runs ownership scanning; repository metadata cannot opt it out. */
|
|
993
|
+
export function ownershipStep(manifest) {
|
|
994
|
+
const { name } = parsePackageManager(manifest.packageManager);
|
|
995
|
+
const command = name === "pnpm" ? "pnpm exec right-release ownership --app ."
|
|
996
|
+
: name === "npm" ? "npm exec -- right-release ownership --app ."
|
|
997
|
+
: "yarn exec right-release ownership --app .";
|
|
998
|
+
return `\n - name: RightKit ownership (protected registry roots)\n shell: bash\n run: test "$(git rev-parse HEAD)" = "\${{ github.event_name == 'workflow_dispatch' && inputs.source_revision || github.sha }}" && ${command}`;
|
|
999
|
+
}
|
|
1000
|
+
|
|
759
1001
|
export function buildRenderContext(manifest, repoPath) {
|
|
760
1002
|
const pm = parsePackageManager(manifest.packageManager);
|
|
761
1003
|
const projectName = deriveProjectName(repoPath);
|
|
@@ -777,19 +1019,40 @@ export function buildRenderContext(manifest, repoPath) {
|
|
|
777
1019
|
PACKAGE_MANAGER_SETUP: pm.setup,
|
|
778
1020
|
ARTIFACT_PREFIX: artifactPrefix,
|
|
779
1021
|
TEMPLATE_VERSION: manifest.templateVersion,
|
|
780
|
-
GATE_PATH: manifest.gate?.path,
|
|
781
1022
|
RUST_CACHE_WORKSPACES: rustWorkspaces.map((workspace) => ` ${workspace} -> target`).join("\n"),
|
|
782
1023
|
RUST_REQUIRED_FOR_GATE: manifest.rustCache?.requiredForGate === true ? "true" : "false",
|
|
783
|
-
CANDIDATE_BUILD_SCRIPT: manifest.candidate?.buildScript,
|
|
784
|
-
CANDIDATE_CHECK_SCRIPT: manifest.candidate?.checkScript,
|
|
785
|
-
CANDIDATE_ARTIFACT_ROOT: manifest.candidate?.artifactRoot,
|
|
786
|
-
CANDIDATE_OS_MATRIX: yamlFlowStringList(manifest.candidate?.os ?? manifest.matrix.os),
|
|
787
|
-
CANDIDATE_MATRIX: candidateMatrix(manifest),
|
|
788
1024
|
WINDOWS_SIGNING_JOB: windowsSigningJob(manifest, pm, artifactPrefix),
|
|
789
1025
|
MACOS_SIGNING_JOB: macosSigningJob(manifest, pm, artifactPrefix),
|
|
790
1026
|
RELEASE_CHAIN_JOBS: releaseChainJobs(manifest, pm, artifactPrefix),
|
|
1027
|
+
OWNERSHIP_STEP: ownershipStep(manifest),
|
|
791
1028
|
};
|
|
792
1029
|
|
|
1030
|
+
// Every key below is populated ONLY when its source manifest section is
|
|
1031
|
+
// actually present — mirroring the publish.npm/homebrew/winget pattern.
|
|
1032
|
+
// This keeps the fail-closed check just below honest: a key that is
|
|
1033
|
+
// legitimately inapplicable to this manifest (e.g. CANDIDATE_* for a
|
|
1034
|
+
// non-rust-hybrid manifest, GATE_PATH for a non-rust-hybrid profile) is
|
|
1035
|
+
// simply ABSENT from the context rather than present-with-undefined, so
|
|
1036
|
+
// render.mjs's own `Object.hasOwn` check does the right thing if a
|
|
1037
|
+
// template ever references a token this manifest didn't opt into.
|
|
1038
|
+
if (manifest.gate) {
|
|
1039
|
+
context.GATE_PATH = manifest.gate.path;
|
|
1040
|
+
}
|
|
1041
|
+
if (manifest.candidate) {
|
|
1042
|
+
context.CANDIDATE_BUILD_SCRIPT = manifest.candidate.buildScript;
|
|
1043
|
+
context.CANDIDATE_CHECK_SCRIPT = manifest.candidate.checkScript;
|
|
1044
|
+
context.CANDIDATE_ARTIFACT_ROOT = manifest.candidate.artifactRoot;
|
|
1045
|
+
context.CANDIDATE_OS_MATRIX = yamlFlowStringList(manifest.candidate.os ?? manifest.matrix.os);
|
|
1046
|
+
context.CANDIDATE_MATRIX = candidateMatrix(manifest);
|
|
1047
|
+
// admissionScript & stageSummaryScript are required candidate fields
|
|
1048
|
+
// (see validateManifest) whenever release-candidate is configured at
|
|
1049
|
+
// all — the admission job and the candidate job's stage-summary steps
|
|
1050
|
+
// are unconditional parts of the rendered template, independent of
|
|
1051
|
+
// signing/releaseChain.
|
|
1052
|
+
context.RELEASE_ADMISSION_SCRIPT = manifest.candidate.admissionScript;
|
|
1053
|
+
context.RELEASE_STAGE_SUMMARY_SCRIPT = manifest.candidate.stageSummaryScript;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
793
1056
|
if (manifest.publish?.npm) {
|
|
794
1057
|
context.NPM_PROVENANCE_FLAG = manifest.publish.npm.provenance ? " --provenance" : "";
|
|
795
1058
|
}
|
|
@@ -801,5 +1064,21 @@ export function buildRenderContext(manifest, repoPath) {
|
|
|
801
1064
|
context.WINGET_PACKAGE_IDENTIFIER = manifest.publish.winget.packageIdentifier;
|
|
802
1065
|
}
|
|
803
1066
|
|
|
1067
|
+
// F-undefined-script fix: render.mjs's `Object.hasOwn` check only proves a
|
|
1068
|
+
// key EXISTS in the context — it does not prove the key resolved to a real
|
|
1069
|
+
// value. Every field above that is sourced from an optional manifest path
|
|
1070
|
+
// (candidate.*, candidate.releaseChain.* via the job-builder functions,
|
|
1071
|
+
// gate.path) must resolve to a real string once its guarding `if` above
|
|
1072
|
+
// decided the section applies; a bug in one of those guards (or a
|
|
1073
|
+
// mis-nested manifest field) must fail the render loudly here rather than
|
|
1074
|
+
// silently emit a literal "undefined" into a workflow file that GitHub
|
|
1075
|
+
// Actions will then try to execute as a real step (e.g. `run: pnpm run
|
|
1076
|
+
// undefined`).
|
|
1077
|
+
for (const [key, value] of Object.entries(context)) {
|
|
1078
|
+
if (value === undefined || value === null) {
|
|
1079
|
+
throw new Error(`buildRenderContext: placeholder \${${key}} resolved to ${value} — check the manifest's candidate/releaseChain/gate/publish configuration`);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
804
1083
|
return context;
|
|
805
1084
|
}
|