@kungfu-tech/buildchain 4.0.2-alpha.48 → 4.0.2-alpha.49
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/architecture/ci-lane-change-budget.json +18 -0
- package/architecture/maintainability-policy.json +4 -4
- package/architecture/v4-release-topology.json +56 -7
- package/architecture/v4-universal-workflow-bootstrap.json +6 -1
- package/architecture/v4-universal-workflow-train-admission.json +1 -1
- package/architecture/workflow-taxonomy.json +11 -0
- package/dist/site/buildchain-contract.json +4 -4
- package/dist/site/buildchain-site.json +21 -11
- package/dist/site/capability-registry.json +1 -1
- package/dist/site/kfd-claims.json +21 -5
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +1 -1
- package/dist/site/node-api-registry.json +9 -9
- package/dist/site/page-registry.json +16 -6
- package/dist/site/public-surface-audit.json +18 -3
- package/dist/site/publication-authority-registry.json +23 -1
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +5 -5
- package/dist/site/workflow-registry.json +39 -5
- package/docs/node-api-reference.md +10 -10
- package/docs/oci-publication.md +75 -1
- package/docs/workflow-catalog.md +1 -0
- package/package.json +1 -1
- package/packages/core/buildchain-publication-authority.js +1 -0
- package/packages/core/oci-compose-qualification.js +140 -0
- package/packages/core/oci-publication-bundle.js +59 -25
- package/packages/core/oci-publication-graph.js +206 -0
- package/scripts/check-v4-release-topology.mjs +12 -0
- package/scripts/generate-v4-universal-workflow-facades.mjs +53 -20
- package/scripts/oci-compose-preview.mjs +250 -0
- package/scripts/publication-candidate-kind.mjs +2 -1
- package/scripts/universal-facade-maintainability.mjs +10 -1
|
@@ -2,6 +2,10 @@ import crypto from "node:crypto";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { v4ContentRoot } from "./v4-canonical-contracts.js";
|
|
5
|
+
import {
|
|
6
|
+
verifyOciGraph,
|
|
7
|
+
verifyOciDestination,
|
|
8
|
+
} from "./oci-publication-graph.js";
|
|
5
9
|
|
|
6
10
|
export const OCI_FAMILY_SCHEMA = "kungfu-buildchain-oci-family/v1";
|
|
7
11
|
const digestPattern = /^sha256:[0-9a-f]{64}$/u;
|
|
@@ -119,7 +123,9 @@ export function verifyOciPublicationBundle({
|
|
|
119
123
|
version,
|
|
120
124
|
}) {
|
|
121
125
|
requireValue(
|
|
122
|
-
|
|
126
|
+
[OCI_FAMILY_SCHEMA, "kungfu-buildchain-oci-family/v2"].includes(
|
|
127
|
+
manifest?.schema,
|
|
128
|
+
),
|
|
123
129
|
"unsupported manifest schema",
|
|
124
130
|
);
|
|
125
131
|
const { root, ...body } = manifest;
|
|
@@ -152,18 +158,18 @@ export function verifyOciPublicationBundle({
|
|
|
152
158
|
"incomplete or duplicate image family",
|
|
153
159
|
);
|
|
154
160
|
const files = new Map();
|
|
161
|
+
const graphs = new Map();
|
|
155
162
|
for (const image of manifest.images) {
|
|
156
|
-
requireValue
|
|
157
|
-
image.repository === `ghcr.io/${repository}/${image.name}`,
|
|
158
|
-
"destination must belong to the consumer repository",
|
|
159
|
-
);
|
|
163
|
+
verifyOciDestination(image, manifest, requireValue);
|
|
160
164
|
requireValue(
|
|
161
165
|
digestPattern.test(image.digest) &&
|
|
162
166
|
["built", "reused"].includes(image.action),
|
|
163
167
|
"invalid image identity",
|
|
164
168
|
);
|
|
165
169
|
requireValue(
|
|
166
|
-
/^linux\/(amd64|arm64)$/u.test(image.platform)
|
|
170
|
+
/^linux\/(amd64|arm64)$/u.test(image.platform) ||
|
|
171
|
+
(manifest.schema.endsWith("/v2") &&
|
|
172
|
+
["multi-platform", "compose"].includes(image.platform)),
|
|
167
173
|
"unsupported image platform",
|
|
168
174
|
);
|
|
169
175
|
const prefix = image.layout;
|
|
@@ -171,7 +177,9 @@ export function verifyOciPublicationBundle({
|
|
|
171
177
|
requireValue(
|
|
172
178
|
digestPattern.test(descriptor.digest) &&
|
|
173
179
|
Number.isSafeInteger(descriptor.size) &&
|
|
174
|
-
descriptor.size >= 0
|
|
180
|
+
descriptor.size >= 0 &&
|
|
181
|
+
!descriptor.urls &&
|
|
182
|
+
!descriptor.data,
|
|
175
183
|
"invalid OCI descriptor",
|
|
176
184
|
);
|
|
177
185
|
const relative = `${prefix}/blobs/sha256/${descriptor.digest.slice(7)}`;
|
|
@@ -209,23 +217,44 @@ export function verifyOciPublicationBundle({
|
|
|
209
217
|
index.schemaVersion === 2 && selected.length === 1,
|
|
210
218
|
"OCI index must select one exact image manifest",
|
|
211
219
|
);
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
220
|
+
if (manifest.schema.endsWith("/v2")) {
|
|
221
|
+
graphs.set(
|
|
222
|
+
image.name,
|
|
223
|
+
verifyOciGraph({
|
|
224
|
+
image,
|
|
225
|
+
manifest,
|
|
226
|
+
descriptor: selected[0],
|
|
227
|
+
descriptorFile,
|
|
228
|
+
requireValue,
|
|
229
|
+
verifyProvenance: (config) =>
|
|
230
|
+
verifyImageProvenance(image, config, sourceSha, version),
|
|
231
|
+
}),
|
|
232
|
+
);
|
|
233
|
+
} else {
|
|
234
|
+
const document = JSON.parse(fs.readFileSync(descriptorFile(selected[0])));
|
|
235
|
+
requireValue(
|
|
236
|
+
document.schemaVersion === 2 &&
|
|
237
|
+
[
|
|
238
|
+
"application/vnd.oci.image.manifest.v1+json",
|
|
239
|
+
"application/vnd.docker.distribution.manifest.v2+json",
|
|
240
|
+
].includes(document.mediaType) &&
|
|
241
|
+
Array.isArray(document.layers),
|
|
242
|
+
"unsupported OCI image manifest",
|
|
243
|
+
);
|
|
244
|
+
const config = JSON.parse(
|
|
245
|
+
fs.readFileSync(descriptorFile(document.config)),
|
|
246
|
+
);
|
|
247
|
+
requireValue(
|
|
248
|
+
`${config.os}/${config.architecture}` === image.platform,
|
|
249
|
+
"OCI platform mismatch",
|
|
250
|
+
);
|
|
251
|
+
verifyImageProvenance(image, config, sourceSha, version);
|
|
252
|
+
for (const layer of document.layers) descriptorFile(layer);
|
|
253
|
+
graphs.set(image.name, {
|
|
254
|
+
manifests: [selected[0]],
|
|
255
|
+
blobs: [document.config, ...document.layers],
|
|
256
|
+
});
|
|
257
|
+
}
|
|
229
258
|
const smokeFile = ociBundleFile(bundleRoot, image.smoke.path);
|
|
230
259
|
requireValue(
|
|
231
260
|
fileDigest(smokeFile) === image.smoke.sha256,
|
|
@@ -237,5 +266,10 @@ export function verifyOciPublicationBundle({
|
|
|
237
266
|
"smoke qualification failed",
|
|
238
267
|
);
|
|
239
268
|
}
|
|
240
|
-
return {
|
|
269
|
+
return {
|
|
270
|
+
root: computed,
|
|
271
|
+
manifest,
|
|
272
|
+
graphs,
|
|
273
|
+
bundleRoot: fs.realpathSync(bundleRoot),
|
|
274
|
+
};
|
|
241
275
|
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
|
|
3
|
+
const imageTypes = [
|
|
4
|
+
"application/vnd.oci.image.manifest.v1+json",
|
|
5
|
+
"application/vnd.docker.distribution.manifest.v2+json",
|
|
6
|
+
];
|
|
7
|
+
const indexTypes = [
|
|
8
|
+
"application/vnd.oci.image.index.v1+json",
|
|
9
|
+
"application/vnd.docker.distribution.manifest.list.v2+json",
|
|
10
|
+
];
|
|
11
|
+
const composeType = "application/vnd.docker.compose.project";
|
|
12
|
+
|
|
13
|
+
export function ociPublicationTag(image, version) {
|
|
14
|
+
return `${image.kind === "compose" ? "compose-" : ""}v${version}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function verifyOciDestination(image, manifest, requireValue) {
|
|
18
|
+
const compose = manifest.schema.endsWith("/v2") && image.kind === "compose";
|
|
19
|
+
requireValue(
|
|
20
|
+
manifest.schema.endsWith("/v2") || !image.kind,
|
|
21
|
+
"artifact kinds require family v2",
|
|
22
|
+
);
|
|
23
|
+
const target =
|
|
24
|
+
compose &&
|
|
25
|
+
manifest.images.find(
|
|
26
|
+
(entry) => entry.name === image.targetImage && entry.kind !== "compose",
|
|
27
|
+
);
|
|
28
|
+
requireValue(
|
|
29
|
+
!image.kind || ["image", "compose"].includes(image.kind),
|
|
30
|
+
"unsupported artifact kind",
|
|
31
|
+
);
|
|
32
|
+
requireValue(!compose || target, "Compose target image is absent");
|
|
33
|
+
requireValue(
|
|
34
|
+
image.repository ===
|
|
35
|
+
`ghcr.io/${manifest.repository}/${compose ? target.name : image.name}`,
|
|
36
|
+
"destination must belong to the consumer repository",
|
|
37
|
+
);
|
|
38
|
+
requireValue(
|
|
39
|
+
manifest.images.filter(
|
|
40
|
+
(entry) =>
|
|
41
|
+
entry.repository === image.repository &&
|
|
42
|
+
ociPublicationTag(entry, manifest.version) ===
|
|
43
|
+
ociPublicationTag(image, manifest.version),
|
|
44
|
+
).length === 1,
|
|
45
|
+
"duplicate publication destination",
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function verifyOciGraph({
|
|
50
|
+
image,
|
|
51
|
+
manifest,
|
|
52
|
+
descriptor,
|
|
53
|
+
descriptorFile,
|
|
54
|
+
requireValue: check,
|
|
55
|
+
verifyProvenance,
|
|
56
|
+
}) {
|
|
57
|
+
const manifests = [],
|
|
58
|
+
blobs = new Map(),
|
|
59
|
+
platforms = [],
|
|
60
|
+
attestations = [];
|
|
61
|
+
const visited = new Set();
|
|
62
|
+
const read = (entry) => JSON.parse(fs.readFileSync(descriptorFile(entry)));
|
|
63
|
+
function blob(entry) {
|
|
64
|
+
descriptorFile(entry);
|
|
65
|
+
blobs.set(entry.digest, entry);
|
|
66
|
+
}
|
|
67
|
+
function visit(entry, depth = 0) {
|
|
68
|
+
check(
|
|
69
|
+
depth < 4 && visited.size < 256 && !visited.has(entry.digest),
|
|
70
|
+
"duplicate or excessive OCI graph",
|
|
71
|
+
);
|
|
72
|
+
visited.add(entry.digest);
|
|
73
|
+
const document = read(entry);
|
|
74
|
+
check(
|
|
75
|
+
document.schemaVersion === 2 && document.mediaType === entry.mediaType,
|
|
76
|
+
"OCI descriptor media type mismatch",
|
|
77
|
+
);
|
|
78
|
+
if (indexTypes.includes(document.mediaType)) {
|
|
79
|
+
check(
|
|
80
|
+
image.kind !== "compose" &&
|
|
81
|
+
Array.isArray(document.manifests) &&
|
|
82
|
+
document.manifests.length > 0,
|
|
83
|
+
"invalid OCI index",
|
|
84
|
+
);
|
|
85
|
+
for (const child of document.manifests) visit(child, depth + 1);
|
|
86
|
+
} else {
|
|
87
|
+
check(
|
|
88
|
+
imageTypes.includes(document.mediaType) &&
|
|
89
|
+
Array.isArray(document.layers),
|
|
90
|
+
"unsupported OCI manifest",
|
|
91
|
+
);
|
|
92
|
+
const config = read(document.config);
|
|
93
|
+
blob(document.config);
|
|
94
|
+
for (const layer of document.layers) blob(layer);
|
|
95
|
+
if (image.kind === "compose") {
|
|
96
|
+
verifyCompose(document, image, manifest, read, check);
|
|
97
|
+
} else if (
|
|
98
|
+
entry.annotations?.["vnd.docker.reference.type"] ===
|
|
99
|
+
"attestation-manifest"
|
|
100
|
+
) {
|
|
101
|
+
check(
|
|
102
|
+
entry.platform?.os === "unknown" &&
|
|
103
|
+
entry.platform?.architecture === "unknown" &&
|
|
104
|
+
document.layers.length > 0 &&
|
|
105
|
+
document.layers.every(
|
|
106
|
+
(layer) => layer.mediaType === "application/vnd.in-toto+json",
|
|
107
|
+
),
|
|
108
|
+
"invalid image attestation",
|
|
109
|
+
);
|
|
110
|
+
attestations.push({
|
|
111
|
+
target: entry.annotations["vnd.docker.reference.digest"],
|
|
112
|
+
statements: document.layers.map(read),
|
|
113
|
+
});
|
|
114
|
+
} else {
|
|
115
|
+
const platform = `${config.os}/${config.architecture}`;
|
|
116
|
+
check(
|
|
117
|
+
/^linux\/(amd64|arm64)$/u.test(platform) &&
|
|
118
|
+
(!entry.platform ||
|
|
119
|
+
platform ===
|
|
120
|
+
`${entry.platform.os}/${entry.platform.architecture}`),
|
|
121
|
+
"OCI platform mismatch",
|
|
122
|
+
);
|
|
123
|
+
check(
|
|
124
|
+
!platforms.some((found) => found.platform === platform),
|
|
125
|
+
"duplicate image platform",
|
|
126
|
+
);
|
|
127
|
+
platforms.push({ platform, digest: entry.digest });
|
|
128
|
+
verifyProvenance(config);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
manifests.push(entry);
|
|
132
|
+
}
|
|
133
|
+
visit(descriptor);
|
|
134
|
+
if (image.kind !== "compose") {
|
|
135
|
+
const expected =
|
|
136
|
+
image.platform === "multi-platform" ? image.platforms : [image.platform];
|
|
137
|
+
check(
|
|
138
|
+
Array.isArray(expected) &&
|
|
139
|
+
expected.length > 0 &&
|
|
140
|
+
new Set(expected).size === expected.length &&
|
|
141
|
+
JSON.stringify([...expected].sort()) ===
|
|
142
|
+
JSON.stringify(platforms.map((entry) => entry.platform).sort()),
|
|
143
|
+
"incomplete image platform family",
|
|
144
|
+
);
|
|
145
|
+
for (const attestation of attestations) {
|
|
146
|
+
check(
|
|
147
|
+
platforms.some((entry) => entry.digest === attestation.target) &&
|
|
148
|
+
attestation.statements.every((statement) =>
|
|
149
|
+
statement.subject?.some(
|
|
150
|
+
(subject) =>
|
|
151
|
+
`sha256:${subject.digest?.sha256}` === attestation.target,
|
|
152
|
+
),
|
|
153
|
+
),
|
|
154
|
+
"image attestation subject mismatch",
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return { manifests, blobs: [...blobs.values()], platforms };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function verifyCompose(document, image, manifest, read, check) {
|
|
162
|
+
if (image.preview)
|
|
163
|
+
check(
|
|
164
|
+
image.preview.alias === "compose-preview" &&
|
|
165
|
+
/^\.github\/workflows\/[a-z0-9-]+\.yml$/u.test(
|
|
166
|
+
image.preview.qualificationWorkflow,
|
|
167
|
+
) &&
|
|
168
|
+
/^(none|sha256:[0-9a-f]{64})$/u.test(image.preview.previousDigest),
|
|
169
|
+
"invalid Compose preview policy",
|
|
170
|
+
);
|
|
171
|
+
check(
|
|
172
|
+
image.platform === "compose" &&
|
|
173
|
+
document.artifactType === composeType &&
|
|
174
|
+
document.config.mediaType === "application/vnd.oci.empty.v1+json" &&
|
|
175
|
+
document.layers.length === 1 &&
|
|
176
|
+
document.layers[0].mediaType ===
|
|
177
|
+
"application/vnd.docker.compose.file+yaml",
|
|
178
|
+
"unsupported Compose artifact",
|
|
179
|
+
);
|
|
180
|
+
// JSON is a YAML subset; this bounded public form needs no executable YAML loader.
|
|
181
|
+
const project = read(document.layers[0]);
|
|
182
|
+
check(
|
|
183
|
+
project.services && Object.keys(project.services).length > 0,
|
|
184
|
+
"empty Compose application",
|
|
185
|
+
);
|
|
186
|
+
const target = manifest.images.find(
|
|
187
|
+
(entry) => entry.name === image.targetImage,
|
|
188
|
+
);
|
|
189
|
+
const references = Object.values(project.services).map(
|
|
190
|
+
(service) => service.image,
|
|
191
|
+
);
|
|
192
|
+
check(
|
|
193
|
+
references.every(
|
|
194
|
+
(ref) =>
|
|
195
|
+
typeof ref === "string" &&
|
|
196
|
+
/^[a-z0-9./:_-]+@sha256:[0-9a-f]{64}$/u.test(ref),
|
|
197
|
+
) && references.includes(`${target.repository}@${target.digest}`),
|
|
198
|
+
"Compose images must bind exact digests",
|
|
199
|
+
);
|
|
200
|
+
check(
|
|
201
|
+
image.content?.sourceSha === manifest.sourceSha &&
|
|
202
|
+
image.content?.version === manifest.version &&
|
|
203
|
+
image.action === "built",
|
|
204
|
+
"Compose candidate provenance mismatch",
|
|
205
|
+
);
|
|
206
|
+
}
|
|
@@ -25,6 +25,7 @@ const PRIVILEGED_ENTRYPOINTS = [
|
|
|
25
25
|
"actions/v4-release-candidate-promote/index.js",
|
|
26
26
|
"scripts/binary-publication-evidence.mjs",
|
|
27
27
|
"scripts/next-development-review.mjs",
|
|
28
|
+
"scripts/oci-compose-preview.mjs",
|
|
28
29
|
"scripts/v4-publication-settlement.mjs",
|
|
29
30
|
];
|
|
30
31
|
|
|
@@ -502,6 +503,17 @@ export function checkV4ReleaseTopology() {
|
|
|
502
503
|
assert.equal(ledger.contract, "kungfu-buildchain-v4-release-topology/v1");
|
|
503
504
|
assertClosedWorld(ledger.closedWorld.workflowPaths);
|
|
504
505
|
assertAuthorityClosure(ledger);
|
|
506
|
+
const preview = ledger.postPublicationScope;
|
|
507
|
+
assert.deepEqual(preview.workflowPaths, [
|
|
508
|
+
".github/workflows/public-release-oci-compose-preview.yml",
|
|
509
|
+
]);
|
|
510
|
+
const previewTopology = discoverV4ReleaseTopology(
|
|
511
|
+
preview.workflowPaths,
|
|
512
|
+
preview.workflowPaths,
|
|
513
|
+
);
|
|
514
|
+
assert.equal(previewTopology.semanticMetrics.contentsWriteJobCount, 1);
|
|
515
|
+
assert.equal(previewTopology.semanticMetrics.oidcWriteJobCount, 0);
|
|
516
|
+
assert.deepEqual(preview.observedTopology, previewTopology);
|
|
505
517
|
const actual = discoverV4ReleaseTopology(
|
|
506
518
|
ledger.closedWorld.workflowPaths,
|
|
507
519
|
ledger.semanticScope.workflowPaths,
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import crypto from "node:crypto";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
currentWorkflowPath,
|
|
6
|
+
writeWorkflowSource,
|
|
7
|
+
rewriteRepositoryWorkflowPaths,
|
|
8
|
+
} from "./workflow-taxonomy.mjs";
|
|
5
9
|
import path from "node:path";
|
|
6
10
|
import { execFileSync } from "node:child_process";
|
|
7
11
|
import { fileURLToPath } from "node:url";
|
|
@@ -96,6 +100,7 @@ function frozenFacadeSource(relative) {
|
|
|
96
100
|
const mutableFacades = new Set([
|
|
97
101
|
...(contract.migration.channelGeneratedFacades || []),
|
|
98
102
|
".github/workflows/dev-pr-auto-merge.yml",
|
|
103
|
+
...(contract.migration.postMigrationWorkflows || []),
|
|
99
104
|
]);
|
|
100
105
|
|
|
101
106
|
function isMutableFacade(relative) {
|
|
@@ -187,9 +192,7 @@ function guardCompatibilityJobs(source, relative) {
|
|
|
187
192
|
function addRuntimeBootstrapDependencies(source, relative) {
|
|
188
193
|
if (
|
|
189
194
|
relative !== ".github/workflows/.build.yml" ||
|
|
190
|
-
source.includes(
|
|
191
|
-
".buildchain/workflow-shell/scripts/github-output.mjs\n",
|
|
192
|
-
)
|
|
195
|
+
source.includes(".buildchain/workflow-shell/scripts/github-output.mjs\n")
|
|
193
196
|
)
|
|
194
197
|
return source;
|
|
195
198
|
const anchor =
|
|
@@ -201,32 +204,60 @@ function addRuntimeBootstrapDependencies(source, relative) {
|
|
|
201
204
|
}
|
|
202
205
|
|
|
203
206
|
function applyPublicationFacadePatches(source, relative) {
|
|
204
|
-
const document = JSON.parse(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
+
const document = JSON.parse(
|
|
208
|
+
fs.readFileSync(
|
|
209
|
+
path.join(root, "architecture/v4-publication-facade-patches.json"),
|
|
210
|
+
"utf8",
|
|
211
|
+
),
|
|
212
|
+
);
|
|
213
|
+
const patch = document.facades.find((entry) => entry.workflow === relative);
|
|
207
214
|
if (!patch) return source;
|
|
208
|
-
const digest = value =>
|
|
215
|
+
const digest = (value) =>
|
|
216
|
+
crypto.createHash("sha256").update(value).digest("hex");
|
|
209
217
|
if (digest(source) === patch.afterSha256) return source;
|
|
210
|
-
if (digest(source) !== patch.beforeSha256)
|
|
218
|
+
if (digest(source) !== patch.beforeSha256)
|
|
219
|
+
fail(`${relative}: publication facade source drift`);
|
|
211
220
|
for (const { before, after } of patch.replacements) {
|
|
212
|
-
if (source.split(before).length !== 2)
|
|
221
|
+
if (source.split(before).length !== 2)
|
|
222
|
+
fail(`${relative}: ambiguous publication patch`);
|
|
213
223
|
source = source.replace(before, () => after);
|
|
214
224
|
}
|
|
215
|
-
if (digest(source) !== patch.afterSha256)
|
|
225
|
+
if (digest(source) !== patch.afterSha256)
|
|
226
|
+
fail(`${relative}: publication facade result drift`);
|
|
216
227
|
return source;
|
|
217
228
|
}
|
|
218
229
|
|
|
219
230
|
function inheritPublicationProviderAuthority(source, relative) {
|
|
220
|
-
if (relative !== ".github/workflows/.release-candidate-promote.yml")
|
|
221
|
-
|
|
222
|
-
|
|
231
|
+
if (relative !== ".github/workflows/.release-candidate-promote.yml")
|
|
232
|
+
return source;
|
|
233
|
+
return source
|
|
234
|
+
.replace(
|
|
235
|
+
"permissions:\n contents: read\n",
|
|
236
|
+
"# Provider authority is inherited from the explicit caller envelope.\n",
|
|
237
|
+
)
|
|
238
|
+
.replace(
|
|
239
|
+
" permissions:\n actions: read\n checks: write\n contents: write\n id-token: write\n pull-requests: write\n",
|
|
240
|
+
" # Provider authority is inherited from the explicit caller envelope.\n",
|
|
241
|
+
);
|
|
223
242
|
}
|
|
224
243
|
|
|
225
244
|
export function migrateV4UniversalWorkflowFacade(source, relative) {
|
|
226
|
-
return rewriteRepositoryWorkflowPaths(
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
245
|
+
return rewriteRepositoryWorkflowPaths(
|
|
246
|
+
root,
|
|
247
|
+
inheritPublicationProviderAuthority(
|
|
248
|
+
applyPublicationFacadePatches(
|
|
249
|
+
guardCompatibilityJobs(
|
|
250
|
+
addUniversalInput(
|
|
251
|
+
addRuntimeBootstrapDependencies(source, relative),
|
|
252
|
+
relative,
|
|
253
|
+
),
|
|
254
|
+
relative,
|
|
255
|
+
),
|
|
256
|
+
relative,
|
|
257
|
+
),
|
|
258
|
+
relative,
|
|
259
|
+
),
|
|
260
|
+
);
|
|
230
261
|
}
|
|
231
262
|
|
|
232
263
|
function verify(source, relative) {
|
|
@@ -427,7 +458,8 @@ function main() {
|
|
|
427
458
|
);
|
|
428
459
|
} else {
|
|
429
460
|
writeWorkflowSource(
|
|
430
|
-
root,
|
|
461
|
+
root,
|
|
462
|
+
path.relative(root, recoveryWorkflowPath),
|
|
431
463
|
fs.readFileSync(recoveryTemplatePath, "utf8"),
|
|
432
464
|
);
|
|
433
465
|
}
|
|
@@ -440,7 +472,8 @@ function main() {
|
|
|
440
472
|
if (check) verify(source, relative);
|
|
441
473
|
else
|
|
442
474
|
writeWorkflowSource(
|
|
443
|
-
root,
|
|
475
|
+
root,
|
|
476
|
+
current,
|
|
444
477
|
migrateV4UniversalWorkflowFacade(source, relative),
|
|
445
478
|
);
|
|
446
479
|
}
|