@kungfu-tech/buildchain 3.0.2 → 3.0.3-alpha.1
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/AGENTS.md +1 -1
- package/CONTRIBUTING.md +1 -1
- package/README.md +27 -1
- package/actions/promote-buildchain-ref/README.md +90 -19
- package/actions/report-buildchain-issue/README.md +20 -2
- package/actions/run-lifecycle/README.md +5 -0
- package/actions/validate-config/README.md +19 -1
- package/bin/buildchain.mjs +56 -2
- package/bin/internal/command-registry.mjs +82 -0
- package/bin/internal/trust-release-cli.mjs +1 -0
- package/contracts/auditable-demo-media-profiles-v1.json +116 -1
- package/contracts/buildchain-v2-residuals-v1.json +146 -0
- package/contracts/evidence/auditable-demo-responsive-web-delivery-v1.json +143 -0
- package/contracts/evidence/auditable-demo-web-delivery-v1.json +7 -3
- package/contracts/fixtures/auditable-demo-responsive-web-delivery-v1/complete-transcript.txt +2 -0
- package/contracts/fixtures/auditable-demo-responsive-web-delivery-v1/public-projection.json +16 -0
- package/contracts/fixtures/auditable-demo-responsive-web-delivery-v1/scene.json +12 -0
- package/dist/site/buildchain-contract.json +188 -46
- package/dist/site/buildchain-site.json +181 -90
- package/dist/site/capability-registry.json +5 -5
- package/dist/site/cli-registry.json +860 -98
- package/dist/site/controller-registry.json +118 -5
- package/dist/site/kfd-claims.json +233 -19
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +20 -20
- package/dist/site/node-api-registry.json +374 -59
- package/dist/site/page-registry.json +156 -65
- package/dist/site/public-surface-audit.json +231 -22
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +3 -0
- package/dist/site/site-manifest.json +24 -24
- package/dist/site/workflow-registry.json +575 -79
- package/docs/MAP.md +22 -4
- package/docs/auditable-demo.md +54 -13
- package/docs/aws-us-elastic-runner-burst-plane.md +381 -0
- package/docs/cli.md +119 -12
- package/docs/consumer-issue-reporting.md +20 -2
- package/docs/dev-alpha-candidate-patrol.md +24 -10
- package/docs/github-governance-authority.md +24 -31
- package/docs/lifecycle-protocol.md +20 -2
- package/docs/migration-inventory.md +24 -5
- package/docs/observed-evidence-patrol.md +28 -12
- package/docs/ownership.md +19 -1
- package/docs/publication-artifacts.md +158 -18
- package/docs/publication-authority.md +4 -4
- package/docs/publish-transaction.md +78 -31
- package/docs/release-candidate.md +24 -0
- package/docs/release-flow.md +50 -32
- package/docs/release-governance.md +39 -32
- package/docs/release-passport.md +4 -0
- package/docs/release-propagation.md +57 -12
- package/docs/reusable-build-surface.md +218 -90
- package/docs/runtime-train-validation.md +20 -2
- package/docs/shifu-gate-profiles.md +7 -1
- package/docs/stable-candidate-patrol.md +6 -6
- package/docs/toolkit-observability.md +22 -0
- package/docs/versioning.md +46 -24
- package/docs/web-surface-deployments.md +82 -1
- package/package.json +7 -5
- package/packages/core/README.md +60 -4
- package/packages/core/artifact-signing.js +1 -0
- package/packages/core/buildchain-contract.js +9 -4
- package/packages/core/cache-evidence.js +1 -0
- package/packages/core/controller-evidence.js +2 -0
- package/packages/core/diagnostics.js +105 -30
- package/packages/core/github-governance-authority.js +25 -17
- package/packages/core/index.js +40 -0
- package/packages/core/paper.js +3226 -0
- package/packages/core/public-surface-audit.js +3 -0
- package/packages/core/publication-artifact.js +1 -6
- package/packages/core/publication-authority.js +1 -0
- package/packages/core/publication-control-plane-audit.js +28 -0
- package/packages/core/publication-reproducibility.js +947 -0
- package/packages/core/publication-sealed-bundle.js +190 -0
- package/packages/core/publish-transaction.js +147 -17
- package/packages/core/release-candidate.js +234 -0
- package/packages/core/release-passport.js +519 -228
- package/packages/core/release-propagation.js +175 -1
- package/packages/core/stable-release-gate.js +4 -1
- package/scripts/artifact-signing-delegation.mjs +268 -0
- package/scripts/audit-github-governance.mjs +32 -13
- package/scripts/audit-publication-control-plane.mjs +17 -8
- package/scripts/auditable-demo.mjs +266 -7
- package/scripts/aws-codebuild-toolchain.mjs +285 -0
- package/scripts/aws-macos-jit-core.mjs +378 -0
- package/scripts/aws-macos-jit.mjs +123 -0
- package/scripts/aws-runner-burst-core.mjs +248 -0
- package/scripts/aws-runner-burst.mjs +79 -0
- package/scripts/aws-windows-jit-core.mjs +374 -0
- package/scripts/aws-windows-jit.mjs +121 -0
- package/scripts/build-contract-core.mjs +69 -6
- package/scripts/build-standalone-binary.mjs +23 -0
- package/scripts/buildchain-contract-lock.mjs +1 -1
- package/scripts/buildchain-patrol.mjs +1 -1
- package/scripts/check-internal-architecture.mjs +69 -2
- package/scripts/check-inventory.mjs +70 -2
- package/scripts/check-javascript-syntax.mjs +31 -0
- package/scripts/check-maintainability.mjs +371 -0
- package/scripts/compiler-cache-evidence.mjs +221 -0
- package/scripts/dev-alpha-candidate-patrol.mjs +288 -43
- package/scripts/dispatch-artifact-signing-authority.mjs +7 -1
- package/scripts/finalize-native-artifact-signing-result.mjs +185 -31
- package/scripts/gate-profile-core.mjs +6 -1
- package/scripts/generate-channel-promotion-workflow.mjs +3 -12
- package/scripts/generate-release-candidate-passport.mjs +4 -0
- package/scripts/generate-site-bundle.mjs +84 -18
- package/scripts/import-artifact-signing-results.mjs +64 -5
- package/scripts/inspect-artifact-signing-requests.mjs +52 -14
- package/scripts/maintainability-metrics.mjs +339 -0
- package/scripts/npm-publish-transaction.mjs +57 -6
- package/scripts/observed-evidence.mjs +151 -33
- package/scripts/paper.mjs +645 -0
- package/scripts/promotion-channel-router.mjs +16 -4
- package/scripts/publication-reproducibility.mjs +62 -0
- package/scripts/reconcile-github-governance.mjs +8 -1
- package/scripts/release-propagation.mjs +39 -0
- package/scripts/resolve-artifact-signing-upload-route.mjs +55 -0
- package/scripts/resolve-build-contract.mjs +6 -0
- package/scripts/route-offline-runners.mjs +236 -0
- package/scripts/run-candidate-body-prefix-renderer.mjs +187 -0
- package/scripts/run-lifecycle-core.mjs +62 -1
- package/scripts/runtime-ref-core.mjs +36 -4
- package/scripts/seal-artifact-signing-requests.mjs +64 -12
- package/scripts/stable-candidate-qualification.mjs +50 -0
- package/scripts/verify-artifact-signing-results.mjs +26 -1
- package/scripts/web-surface-core.mjs +272 -86
- package/scripts/web-surface-production-decision.mjs +19 -3
|
@@ -317,6 +317,7 @@ function mutableCacheControlArgs({ artifactRoot, bucket, objectPrefix, cacheCont
|
|
|
317
317
|
|
|
318
318
|
const PUBLICATION_ARCHIVE_POLICY_CONTRACT = "kungfu-buildchain-publication-archive-policy";
|
|
319
319
|
const OBSERVED_EVIDENCE_OWNERSHIP_CONTRACT = "kungfu-buildchain-observed-evidence-ownership";
|
|
320
|
+
const PUBLICATION_FAST_PATH_CONTRACT = "kungfu-buildchain-publication-package-pin-fast-path";
|
|
320
321
|
|
|
321
322
|
function immutablePrefix(value, label) {
|
|
322
323
|
const raw = String(value || "").trim().replaceAll("\\", "/");
|
|
@@ -330,6 +331,98 @@ function immutablePrefix(value, label) {
|
|
|
330
331
|
return normalized;
|
|
331
332
|
}
|
|
332
333
|
|
|
334
|
+
function publicationFastPathScope({ artifactRoot, bindings }) {
|
|
335
|
+
const manifestFile = path.join(artifactRoot, "manifest.json");
|
|
336
|
+
if (!fs.existsSync(manifestFile)) return null;
|
|
337
|
+
let manifest;
|
|
338
|
+
try {
|
|
339
|
+
manifest = JSON.parse(fs.readFileSync(manifestFile, "utf8"));
|
|
340
|
+
} catch (error) {
|
|
341
|
+
throw new Error(`invalid web-surface artifact manifest: ${error.message}`);
|
|
342
|
+
}
|
|
343
|
+
const raw = manifest?.publicationFastPath;
|
|
344
|
+
if (raw === undefined || raw === null) return null;
|
|
345
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
346
|
+
throw new Error("publicationFastPath must be an object");
|
|
347
|
+
}
|
|
348
|
+
if (raw.contract !== PUBLICATION_FAST_PATH_CONTRACT || raw.mode !== "package-pin-only") {
|
|
349
|
+
throw new Error("publicationFastPath contract or mode is unsupported");
|
|
350
|
+
}
|
|
351
|
+
const targetSurface = String(raw.targetSurface || "").trim();
|
|
352
|
+
const binding = bindings.find((entry) => entry.surface === targetSurface);
|
|
353
|
+
if (!binding) {
|
|
354
|
+
throw new Error(`publicationFastPath target surface is not declared: ${targetSurface}`);
|
|
355
|
+
}
|
|
356
|
+
const qualificationRoot = String(raw.qualificationRoot || "").trim();
|
|
357
|
+
if (!/^sha256:[a-f0-9]{64}$/.test(qualificationRoot)) {
|
|
358
|
+
throw new Error("publicationFastPath qualificationRoot must be an exact SHA-256 root");
|
|
359
|
+
}
|
|
360
|
+
const surfaceRoot = surfaceArtifactRootFor({ artifactRoot, binding });
|
|
361
|
+
const immutablePrefixes = [...new Set((raw.immutablePrefixes || []).map((entry) =>
|
|
362
|
+
immutablePrefix(entry, "publicationFastPath immutable prefix"),
|
|
363
|
+
))].sort();
|
|
364
|
+
if (immutablePrefixes.length === 0) {
|
|
365
|
+
throw new Error("publicationFastPath must declare immutablePrefixes");
|
|
366
|
+
}
|
|
367
|
+
const mutableFiles = [...new Set((raw.mutableFiles || []).map((entry) => {
|
|
368
|
+
const normalized = normalizeS3Key(entry);
|
|
369
|
+
const candidate = path.join(surfaceRoot, normalized);
|
|
370
|
+
if (
|
|
371
|
+
!normalized
|
|
372
|
+
|| String(entry).includes("..")
|
|
373
|
+
|| !fs.statSync(candidate, { throwIfNoEntry: false })?.isFile()
|
|
374
|
+
) {
|
|
375
|
+
throw new Error(`invalid or missing publicationFastPath mutable file: ${entry}`);
|
|
376
|
+
}
|
|
377
|
+
return normalized;
|
|
378
|
+
}))].sort();
|
|
379
|
+
if (mutableFiles.length === 0) {
|
|
380
|
+
throw new Error("publicationFastPath must declare mutableFiles");
|
|
381
|
+
}
|
|
382
|
+
if (mutableFiles.some((file) =>
|
|
383
|
+
immutablePrefixes.some((prefix) => file === prefix || file.startsWith(`${prefix}/`)))) {
|
|
384
|
+
throw new Error("publicationFastPath mutable files cannot overlap immutable prefixes");
|
|
385
|
+
}
|
|
386
|
+
for (const prefix of immutablePrefixes) {
|
|
387
|
+
if (!fs.statSync(path.join(surfaceRoot, prefix), { throwIfNoEntry: false })?.isDirectory()) {
|
|
388
|
+
throw new Error(`publicationFastPath immutable prefix does not exist: ${prefix}`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const invalidationPaths = [...new Set((raw.invalidationPaths || []).map((entry) => {
|
|
392
|
+
const value = String(entry || "").trim();
|
|
393
|
+
if (!value.startsWith("/") || value.includes("..")) {
|
|
394
|
+
throw new Error(`invalid publicationFastPath invalidation path: ${entry}`);
|
|
395
|
+
}
|
|
396
|
+
return value;
|
|
397
|
+
}))].sort();
|
|
398
|
+
if (invalidationPaths.length === 0) {
|
|
399
|
+
throw new Error("publicationFastPath must declare invalidationPaths");
|
|
400
|
+
}
|
|
401
|
+
return {
|
|
402
|
+
contract: PUBLICATION_FAST_PATH_CONTRACT,
|
|
403
|
+
mode: "package-pin-only",
|
|
404
|
+
targetSurface,
|
|
405
|
+
qualificationRoot,
|
|
406
|
+
immutablePrefixes,
|
|
407
|
+
mutableFiles,
|
|
408
|
+
invalidationPaths,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function applyPublicationFastPathScope(bindings, { cwd, artifactPath }) {
|
|
413
|
+
const artifactRoot = path.resolve(cwd, artifactPath);
|
|
414
|
+
const scope = publicationFastPathScope({ artifactRoot, bindings });
|
|
415
|
+
if (!scope) {
|
|
416
|
+
return { bindings, scope: null };
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
bindings: bindings
|
|
420
|
+
.filter((binding) => binding.surface === scope.targetSurface)
|
|
421
|
+
.map((binding) => ({ ...binding, publicationFastPath: scope })),
|
|
422
|
+
scope,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
333
426
|
function publicationImmutablePolicy({ artifactRoot, binding }) {
|
|
334
427
|
const surfaceRoot = surfaceArtifactRootFor({ artifactRoot, binding });
|
|
335
428
|
const manifestFile = path.join(surfaceRoot, "manifest.json");
|
|
@@ -341,22 +434,41 @@ function publicationImmutablePolicy({ artifactRoot, binding }) {
|
|
|
341
434
|
throw new Error(`invalid publication archive manifest ${toPosix(path.relative(artifactRoot, manifestFile))}: ${error.message}`);
|
|
342
435
|
}
|
|
343
436
|
if (manifest?.archivePolicy?.contract !== PUBLICATION_ARCHIVE_POLICY_CONTRACT) return null;
|
|
344
|
-
const
|
|
345
|
-
(publication.versions || []).map((version) =>
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
437
|
+
const declaredVersions = (manifest.publications || []).flatMap((publication) =>
|
|
438
|
+
(publication.versions || []).map((version) => ({
|
|
439
|
+
prefix: immutablePrefix(
|
|
440
|
+
version.immutablePath,
|
|
441
|
+
`publication ${publication.id || "unknown"} immutablePath`,
|
|
442
|
+
),
|
|
443
|
+
materialized: Object.hasOwn(version, "immutableIndex"),
|
|
444
|
+
})),
|
|
445
|
+
);
|
|
446
|
+
const declaredPrefixes = [...new Set(declaredVersions.map((entry) => entry.prefix))].sort();
|
|
350
447
|
if (declaredPrefixes.length === 0) {
|
|
351
448
|
throw new Error("publication archive policy must declare at least one immutable version prefix");
|
|
352
449
|
}
|
|
353
|
-
|
|
450
|
+
const hasMaterializationEnvelope = declaredVersions.some((entry) => entry.materialized);
|
|
451
|
+
const materializedPrefixes = [...new Set(
|
|
452
|
+
(hasMaterializationEnvelope
|
|
453
|
+
? declaredVersions.filter((entry) => entry.materialized)
|
|
454
|
+
: declaredVersions
|
|
455
|
+
).map((entry) => entry.prefix),
|
|
456
|
+
)].sort();
|
|
457
|
+
for (const prefix of materializedPrefixes) {
|
|
354
458
|
if (!fs.existsSync(path.join(surfaceRoot, prefix))) {
|
|
355
|
-
throw new Error(`
|
|
459
|
+
throw new Error(`materialized immutable publication prefix does not exist in artifact: ${prefix}`);
|
|
356
460
|
}
|
|
357
461
|
}
|
|
358
462
|
const preservedRoots = declaredPrefixes;
|
|
359
|
-
const
|
|
463
|
+
const uploadRoots = binding.publicationFastPath?.immutablePrefixes || materializedPrefixes;
|
|
464
|
+
if (binding.publicationFastPath) {
|
|
465
|
+
for (const prefix of uploadRoots) {
|
|
466
|
+
if (!declaredPrefixes.includes(prefix)) {
|
|
467
|
+
throw new Error(`publicationFastPath immutable prefix is not declared by the archive manifest: ${prefix}`);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
const files = uploadRoots
|
|
360
472
|
.flatMap((root) => listFiles(surfaceRoot, root))
|
|
361
473
|
.map((filePath) => ({
|
|
362
474
|
path: toPosix(path.relative(surfaceRoot, filePath)),
|
|
@@ -371,7 +483,10 @@ function publicationImmutablePolicy({ artifactRoot, binding }) {
|
|
|
371
483
|
manifestPath: toPosix(path.relative(artifactRoot, manifestFile)),
|
|
372
484
|
preservedRoots,
|
|
373
485
|
declaredPrefixes,
|
|
486
|
+
materializedPrefixes,
|
|
487
|
+
uploadRoots,
|
|
374
488
|
files,
|
|
489
|
+
fastPath: binding.publicationFastPath || undefined,
|
|
375
490
|
};
|
|
376
491
|
}
|
|
377
492
|
|
|
@@ -1152,7 +1267,11 @@ export function planWebSurfaceDeploy({
|
|
|
1152
1267
|
if (installerEvidence) {
|
|
1153
1268
|
manifest.installerPublicationEvidence = installerEvidence;
|
|
1154
1269
|
}
|
|
1155
|
-
const
|
|
1270
|
+
const scoped = applyPublicationFastPathScope(manifest.surfaceBindings, {
|
|
1271
|
+
cwd,
|
|
1272
|
+
artifactPath: artifactPath || deployConfig.artifactPath || ".",
|
|
1273
|
+
});
|
|
1274
|
+
const surfaceBindings = withImmutablePublicationPolicies(withSurfaceRoutingEvidence(scoped.bindings, {
|
|
1156
1275
|
artifactPath: artifactPath || deployConfig.artifactPath || ".",
|
|
1157
1276
|
files: resolvedArtifact.files,
|
|
1158
1277
|
}), {
|
|
@@ -1169,6 +1288,10 @@ export function planWebSurfaceDeploy({
|
|
|
1169
1288
|
}
|
|
1170
1289
|
}
|
|
1171
1290
|
manifest.surfaceBindings = surfaceBindings;
|
|
1291
|
+
if (scoped.scope) {
|
|
1292
|
+
manifest.publicationFastPath = scoped.scope;
|
|
1293
|
+
manifest.url = surfaceBindings[0].url;
|
|
1294
|
+
}
|
|
1172
1295
|
return {
|
|
1173
1296
|
schemaVersion: 1,
|
|
1174
1297
|
contract: "kungfu-buildchain-web-surface-deploy-plan",
|
|
@@ -1185,6 +1308,7 @@ export function planWebSurfaceDeploy({
|
|
|
1185
1308
|
},
|
|
1186
1309
|
manifest,
|
|
1187
1310
|
surfaceBindings,
|
|
1311
|
+
publicationFastPath: scoped.scope || undefined,
|
|
1188
1312
|
steps: planAdapterSteps(deployConfig.adapter, deployConfig, manifest),
|
|
1189
1313
|
};
|
|
1190
1314
|
}
|
|
@@ -1196,7 +1320,18 @@ export function verifyWebSurfaceArtifactChannelFacts({ cwd = process.cwd(), plan
|
|
|
1196
1320
|
try { return new URL(binding.url).host; } catch { return ""; }
|
|
1197
1321
|
}).filter(Boolean));
|
|
1198
1322
|
const observed = [];
|
|
1199
|
-
|
|
1323
|
+
const scopedPrefix = normalizeS3Key(
|
|
1324
|
+
resolvedPlan.publicationFastPath
|
|
1325
|
+
? resolvedPlan.surfaceBindings?.[0]?.artifactPathPrefix || ""
|
|
1326
|
+
: "",
|
|
1327
|
+
);
|
|
1328
|
+
for (const filePath of listFiles(artifactRoot, artifactRoot)
|
|
1329
|
+
.filter((entry) => entry.endsWith(".json"))
|
|
1330
|
+
.filter((entry) => {
|
|
1331
|
+
if (!scopedPrefix) return true;
|
|
1332
|
+
const relative = normalizeS3Key(path.relative(artifactRoot, entry));
|
|
1333
|
+
return relative === scopedPrefix || relative.startsWith(`${scopedPrefix}/`);
|
|
1334
|
+
})) {
|
|
1200
1335
|
let value;
|
|
1201
1336
|
try { value = JSON.parse(fs.readFileSync(filePath, "utf8")); } catch { continue; }
|
|
1202
1337
|
if (!value || typeof value !== "object" || Array.isArray(value) || !String(value.contract || "").includes("manifest")) continue;
|
|
@@ -1281,7 +1416,7 @@ export function applyWebSurfaceDeploy({
|
|
|
1281
1416
|
...bindingOperations.filter((operation) => operation.action === "verify-immutable-artifact-before-upload"),
|
|
1282
1417
|
...bindingOperations.filter((operation) => operation.action === "sync-immutable-artifact"),
|
|
1283
1418
|
...bindingOperations.filter((operation) => operation.action === "verify-immutable-artifact-after-upload"),
|
|
1284
|
-
...cloudFrontDirectoryIndexRewriteOperations(bindings),
|
|
1419
|
+
...cloudFrontDirectoryIndexRewriteOperations(bindings.filter((binding) => !binding.publicationFastPath)),
|
|
1285
1420
|
...bindingOperations.filter((operation) => ![
|
|
1286
1421
|
"verify-immutable-artifact-before-upload",
|
|
1287
1422
|
"sync-immutable-artifact",
|
|
@@ -1291,7 +1426,10 @@ export function applyWebSurfaceDeploy({
|
|
|
1291
1426
|
const primaryBinding = bindings[0] || {};
|
|
1292
1427
|
const objectPrefix = primaryBinding.objectPrefix || objectPrefixFor(deployConfig, resolvedPlan.manifest.alias || resolvedPlan.manifest.channel);
|
|
1293
1428
|
const manifestKey = primaryBinding.manifestKey || deployManifestKey(deployConfig, resolvedPlan.manifest);
|
|
1294
|
-
const invalidationPaths = bindings.flatMap((binding) =>
|
|
1429
|
+
const invalidationPaths = bindings.flatMap((binding) =>
|
|
1430
|
+
binding.publicationFastPath
|
|
1431
|
+
? [...binding.publicationFastPath.invalidationPaths, cdnPath(binding.manifestKey)]
|
|
1432
|
+
: [viewerWildcardPath(binding), cdnPath(binding.manifestKey)]);
|
|
1295
1433
|
const operationResults = runAdapterOperations({ operations, dryRun, commandRunner });
|
|
1296
1434
|
const immutablePreservation = bindings
|
|
1297
1435
|
.filter((binding) => binding.immutablePublication)
|
|
@@ -1303,10 +1441,12 @@ export function applyWebSurfaceDeploy({
|
|
|
1303
1441
|
"sync-immutable-artifact",
|
|
1304
1442
|
"verify-immutable-artifact-after-upload",
|
|
1305
1443
|
"sync-static-artifact",
|
|
1444
|
+
"sync-publication-fast-path",
|
|
1306
1445
|
].includes(operation.action),
|
|
1307
1446
|
);
|
|
1308
1447
|
const expectedOperationCount = (binding.immutablePublication.files.length * 2) +
|
|
1309
|
-
binding.immutablePublication.
|
|
1448
|
+
binding.immutablePublication.uploadRoots.length +
|
|
1449
|
+
(binding.publicationFastPath ? binding.publicationFastPath.mutableFiles.length : 1);
|
|
1310
1450
|
const complete = relevant.length === expectedOperationCount;
|
|
1311
1451
|
return {
|
|
1312
1452
|
surface: binding.surface,
|
|
@@ -1341,6 +1481,7 @@ export function applyWebSurfaceDeploy({
|
|
|
1341
1481
|
objectPrefix,
|
|
1342
1482
|
manifestKey,
|
|
1343
1483
|
invalidationPaths,
|
|
1484
|
+
publicationFastPath: resolvedPlan.publicationFastPath || resolvedPlan.manifest.publicationFastPath,
|
|
1344
1485
|
manifest: resolvedPlan.manifest,
|
|
1345
1486
|
surfaceBindings: bindings,
|
|
1346
1487
|
immutablePreservation,
|
|
@@ -1612,11 +1753,6 @@ function htmlLikeResponse(response, url = "") {
|
|
|
1612
1753
|
return String(contentType).toLowerCase().includes("text/html") || /\/$|\.html(?:$|[?#])/.test(String(url || ""));
|
|
1613
1754
|
}
|
|
1614
1755
|
|
|
1615
|
-
const managedNetworkRequiredEvidence = [
|
|
1616
|
-
"sync-static-artifact",
|
|
1617
|
-
"write-deployment-manifest",
|
|
1618
|
-
];
|
|
1619
|
-
|
|
1620
1756
|
function operationEvidenceStatus(operation, { plannedEvidence = false } = {}) {
|
|
1621
1757
|
if (plannedEvidence && operation.status === undefined) {
|
|
1622
1758
|
return true;
|
|
@@ -1625,6 +1761,12 @@ function operationEvidenceStatus(operation, { plannedEvidence = false } = {}) {
|
|
|
1625
1761
|
}
|
|
1626
1762
|
|
|
1627
1763
|
function managedNetworkHealthEvidence({ target, result, plan }) {
|
|
1764
|
+
const requiredActions = [
|
|
1765
|
+
target.publicationFastPath
|
|
1766
|
+
? "sync-publication-fast-path"
|
|
1767
|
+
: "sync-static-artifact",
|
|
1768
|
+
"write-deployment-manifest",
|
|
1769
|
+
];
|
|
1628
1770
|
const operationSource = Array.isArray(result?.operations) && result.operations.length > 0
|
|
1629
1771
|
? "apply-result"
|
|
1630
1772
|
: "deploy-plan";
|
|
@@ -1637,7 +1779,7 @@ function managedNetworkHealthEvidence({ target, result, plan }) {
|
|
|
1637
1779
|
.filter((operation) => operationEvidenceStatus(operation, { plannedEvidence: operationSource === "deploy-plan" }))
|
|
1638
1780
|
.map((operation) => operation.action),
|
|
1639
1781
|
);
|
|
1640
|
-
const missingActions =
|
|
1782
|
+
const missingActions = requiredActions.filter((action) => !presentActions.has(action));
|
|
1641
1783
|
const missingFields = [];
|
|
1642
1784
|
if (!target.manifestKey) missingFields.push("manifestKey");
|
|
1643
1785
|
if (!target.bucket) missingFields.push("bucket");
|
|
@@ -1645,7 +1787,7 @@ function managedNetworkHealthEvidence({ target, result, plan }) {
|
|
|
1645
1787
|
return {
|
|
1646
1788
|
source: operationSource,
|
|
1647
1789
|
actions: [...presentActions].sort(),
|
|
1648
|
-
requiredActions
|
|
1790
|
+
requiredActions,
|
|
1649
1791
|
missingActions,
|
|
1650
1792
|
missingFields,
|
|
1651
1793
|
status: missingActions.length === 0 && missingFields.length === 0 ? "pass" : "fail",
|
|
@@ -1825,6 +1967,7 @@ export async function checkWebSurfaceHealth({
|
|
|
1825
1967
|
manifestKey: binding.manifestKey || "",
|
|
1826
1968
|
bucket: binding.bucket || "",
|
|
1827
1969
|
objectPrefix: binding.objectPrefix || "",
|
|
1970
|
+
publicationFastPath: binding.publicationFastPath || null,
|
|
1828
1971
|
}));
|
|
1829
1972
|
})
|
|
1830
1973
|
: Object.entries(urls).map(([surface, url]) => ({
|
|
@@ -1928,19 +2071,23 @@ export async function checkWebSurfaceHealth({
|
|
|
1928
2071
|
const operationSource = Array.isArray(result?.operations) && result.operations.length > 0
|
|
1929
2072
|
? result.operations
|
|
1930
2073
|
: (Array.isArray(plan?.steps) ? plan.steps : []);
|
|
1931
|
-
const requiredActions = [
|
|
1932
|
-
"verify-immutable-artifact-before-upload",
|
|
1933
|
-
"sync-immutable-artifact",
|
|
1934
|
-
"verify-immutable-artifact-after-upload",
|
|
1935
|
-
"sync-static-artifact",
|
|
1936
|
-
];
|
|
1937
2074
|
const preservationBindings = immutableBindings.map((binding) => {
|
|
2075
|
+
const requiredActions = [
|
|
2076
|
+
"verify-immutable-artifact-before-upload",
|
|
2077
|
+
"sync-immutable-artifact",
|
|
2078
|
+
"verify-immutable-artifact-after-upload",
|
|
2079
|
+
binding.publicationFastPath
|
|
2080
|
+
? "sync-publication-fast-path"
|
|
2081
|
+
: "sync-static-artifact",
|
|
2082
|
+
];
|
|
1938
2083
|
const actions = new Set(operationSource
|
|
1939
2084
|
.filter((operation) => operation.surface === binding.surface)
|
|
1940
2085
|
.filter((operation) => operationEvidenceStatus(operation, { plannedEvidence: !result }))
|
|
1941
2086
|
.map((operation) => operation.action));
|
|
1942
2087
|
const missingActions = requiredActions.filter((action) => !actions.has(action));
|
|
1943
|
-
const coveringBindings =
|
|
2088
|
+
const coveringBindings = binding.publicationFastPath
|
|
2089
|
+
? []
|
|
2090
|
+
: (binding.immutablePublication.coveringBindings || []).map((covering) => {
|
|
1944
2091
|
const sync = operationSource.find((operation) =>
|
|
1945
2092
|
operation.surface === covering.surface &&
|
|
1946
2093
|
operation.action === "sync-static-artifact" &&
|
|
@@ -1953,7 +2100,7 @@ export async function checkWebSurfaceHealth({
|
|
|
1953
2100
|
? "pass"
|
|
1954
2101
|
: "fail",
|
|
1955
2102
|
};
|
|
1956
|
-
|
|
2103
|
+
});
|
|
1957
2104
|
return {
|
|
1958
2105
|
surface: binding.surface,
|
|
1959
2106
|
manifestPath: binding.immutablePublication.manifestPath,
|
|
@@ -2085,7 +2232,7 @@ function deployBindingOperations({ artifactRoot, deployConfig, manifest, binding
|
|
|
2085
2232
|
sha256: file.sha256,
|
|
2086
2233
|
},
|
|
2087
2234
|
}));
|
|
2088
|
-
const syncImmutable = (immutable?.preservedRoots || []).map((root) => {
|
|
2235
|
+
const syncImmutable = (immutable?.uploadRoots || immutable?.preservedRoots || []).map((root) => {
|
|
2089
2236
|
const isInstallerRoot = Boolean(installerBinding && root === installerRoot);
|
|
2090
2237
|
const immutableMetadataArgs = isInstallerRoot
|
|
2091
2238
|
? [
|
|
@@ -2140,48 +2287,73 @@ function deployBindingOperations({ artifactRoot, deployConfig, manifest, binding
|
|
|
2140
2287
|
cacheControl: "public,max-age=300,must-revalidate",
|
|
2141
2288
|
},
|
|
2142
2289
|
}));
|
|
2290
|
+
const fastPathMutable = (binding.publicationFastPath?.mutableFiles || []).map((file) => ({
|
|
2291
|
+
action: "sync-publication-fast-path",
|
|
2292
|
+
surface: binding.surface,
|
|
2293
|
+
command: "aws",
|
|
2294
|
+
args: [
|
|
2295
|
+
"s3",
|
|
2296
|
+
"cp",
|
|
2297
|
+
path.join(surfaceArtifactRoot, file),
|
|
2298
|
+
s3Uri(bucket, joinS3Key(binding.objectPrefix, file)),
|
|
2299
|
+
"--checksum-algorithm",
|
|
2300
|
+
"SHA256",
|
|
2301
|
+
],
|
|
2302
|
+
publicationFastPath: {
|
|
2303
|
+
qualificationRoot: binding.publicationFastPath.qualificationRoot,
|
|
2304
|
+
path: file,
|
|
2305
|
+
overwriteBoundary: "declared-mutable-file-only",
|
|
2306
|
+
},
|
|
2307
|
+
}));
|
|
2308
|
+
const mutableSync = binding.publicationFastPath
|
|
2309
|
+
? fastPathMutable
|
|
2310
|
+
: [
|
|
2311
|
+
{
|
|
2312
|
+
action: "sync-static-artifact",
|
|
2313
|
+
surface: binding.surface,
|
|
2314
|
+
command: "aws",
|
|
2315
|
+
args: syncStaticArtifactArgs({
|
|
2316
|
+
artifactRoot: surfaceArtifactRoot,
|
|
2317
|
+
bucket,
|
|
2318
|
+
objectPrefix: binding.objectPrefix,
|
|
2319
|
+
deleteExcludes: binding.mutableDeleteExcludes || [],
|
|
2320
|
+
cacheControl: binding.cacheControl?.default || "",
|
|
2321
|
+
}),
|
|
2322
|
+
routing: binding.routing,
|
|
2323
|
+
preservation: binding.mutableDeleteExcludes?.length > 0
|
|
2324
|
+
? {
|
|
2325
|
+
contract: "kungfu-buildchain-web-surface-immutable-delete-exclusion",
|
|
2326
|
+
mutableDeleteExcludes: binding.mutableDeleteExcludes,
|
|
2327
|
+
}
|
|
2328
|
+
: undefined,
|
|
2329
|
+
},
|
|
2330
|
+
...(binding.cacheControl?.mutable
|
|
2331
|
+
? [{
|
|
2332
|
+
action: "apply-mutable-cache-control",
|
|
2333
|
+
surface: binding.surface,
|
|
2334
|
+
command: "aws",
|
|
2335
|
+
args: mutableCacheControlArgs({
|
|
2336
|
+
artifactRoot: surfaceArtifactRoot,
|
|
2337
|
+
bucket,
|
|
2338
|
+
objectPrefix: binding.objectPrefix,
|
|
2339
|
+
cacheControl: binding.cacheControl.mutable,
|
|
2340
|
+
excludePatterns: binding.mutableDeleteExcludes || [],
|
|
2341
|
+
}),
|
|
2342
|
+
cacheControl: binding.cacheControl.mutable,
|
|
2343
|
+
patterns: ["*.html", "*.json", "*.xml"],
|
|
2344
|
+
excludes: binding.mutableDeleteExcludes || [],
|
|
2345
|
+
}]
|
|
2346
|
+
: []),
|
|
2347
|
+
];
|
|
2143
2348
|
const operations = [
|
|
2144
2349
|
...verifyImmutable("before-upload"),
|
|
2145
2350
|
...syncImmutable,
|
|
2146
2351
|
...verifyImmutable("after-upload"),
|
|
2147
|
-
|
|
2148
|
-
action: "sync-static-artifact",
|
|
2149
|
-
surface: binding.surface,
|
|
2150
|
-
command: "aws",
|
|
2151
|
-
args: syncStaticArtifactArgs({
|
|
2152
|
-
artifactRoot: surfaceArtifactRoot,
|
|
2153
|
-
bucket,
|
|
2154
|
-
objectPrefix: binding.objectPrefix,
|
|
2155
|
-
deleteExcludes: binding.mutableDeleteExcludes || [],
|
|
2156
|
-
cacheControl: binding.cacheControl?.default || "",
|
|
2157
|
-
}),
|
|
2158
|
-
routing: binding.routing,
|
|
2159
|
-
preservation: binding.mutableDeleteExcludes?.length > 0
|
|
2160
|
-
? {
|
|
2161
|
-
contract: "kungfu-buildchain-web-surface-immutable-delete-exclusion",
|
|
2162
|
-
mutableDeleteExcludes: binding.mutableDeleteExcludes,
|
|
2163
|
-
}
|
|
2164
|
-
: undefined,
|
|
2165
|
-
},
|
|
2166
|
-
...(binding.cacheControl?.mutable
|
|
2167
|
-
? [{
|
|
2168
|
-
action: "apply-mutable-cache-control",
|
|
2169
|
-
surface: binding.surface,
|
|
2170
|
-
command: "aws",
|
|
2171
|
-
args: mutableCacheControlArgs({
|
|
2172
|
-
artifactRoot: surfaceArtifactRoot,
|
|
2173
|
-
bucket,
|
|
2174
|
-
objectPrefix: binding.objectPrefix,
|
|
2175
|
-
cacheControl: binding.cacheControl.mutable,
|
|
2176
|
-
excludePatterns: binding.mutableDeleteExcludes || [],
|
|
2177
|
-
}),
|
|
2178
|
-
cacheControl: binding.cacheControl.mutable,
|
|
2179
|
-
patterns: ["*.html", "*.json", "*.xml"],
|
|
2180
|
-
excludes: binding.mutableDeleteExcludes || [],
|
|
2181
|
-
}]
|
|
2182
|
-
: []),
|
|
2352
|
+
...mutableSync,
|
|
2183
2353
|
...publishFriendlyInstallers,
|
|
2184
|
-
...
|
|
2354
|
+
...(binding.publicationFastPath
|
|
2355
|
+
? []
|
|
2356
|
+
: directoryIndexAliasOperations({ surfaceArtifactRoot, bucket, binding })),
|
|
2185
2357
|
{
|
|
2186
2358
|
action: "write-deployment-manifest",
|
|
2187
2359
|
surface: binding.surface,
|
|
@@ -2206,6 +2378,8 @@ function deployBindingOperations({ artifactRoot, deployConfig, manifest, binding
|
|
|
2206
2378
|
},
|
|
2207
2379
|
];
|
|
2208
2380
|
if (distribution) {
|
|
2381
|
+
const invalidationPaths = binding.publicationFastPath?.invalidationPaths
|
|
2382
|
+
|| [viewerWildcardPath(binding)];
|
|
2209
2383
|
operations.push({
|
|
2210
2384
|
action: "invalidate-cdn",
|
|
2211
2385
|
surface: binding.surface,
|
|
@@ -2216,7 +2390,7 @@ function deployBindingOperations({ artifactRoot, deployConfig, manifest, binding
|
|
|
2216
2390
|
"--distribution-id",
|
|
2217
2391
|
distribution,
|
|
2218
2392
|
"--paths",
|
|
2219
|
-
|
|
2393
|
+
...invalidationPaths,
|
|
2220
2394
|
cdnPath(binding.manifestKey),
|
|
2221
2395
|
],
|
|
2222
2396
|
});
|
|
@@ -2237,7 +2411,7 @@ function planAdapterSteps(adapter, deployConfig, manifest) {
|
|
|
2237
2411
|
{
|
|
2238
2412
|
action: "sync-immutable-artifact",
|
|
2239
2413
|
surface: binding.surface,
|
|
2240
|
-
roots: binding.immutablePublication.
|
|
2414
|
+
roots: binding.immutablePublication.uploadRoots,
|
|
2241
2415
|
overwrite: false,
|
|
2242
2416
|
},
|
|
2243
2417
|
{
|
|
@@ -2247,23 +2421,32 @@ function planAdapterSteps(adapter, deployConfig, manifest) {
|
|
|
2247
2421
|
},
|
|
2248
2422
|
]
|
|
2249
2423
|
: []),
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
target: binding.bucket,
|
|
2254
|
-
prefix: binding.objectPrefix,
|
|
2255
|
-
deleteExcludes: binding.mutableDeleteExcludes || [],
|
|
2256
|
-
cacheControl: binding.cacheControl || undefined,
|
|
2257
|
-
},
|
|
2258
|
-
...(binding.cacheControl?.mutable
|
|
2259
|
-
? [{
|
|
2260
|
-
action: "apply-mutable-cache-control",
|
|
2424
|
+
...(binding.publicationFastPath
|
|
2425
|
+
? binding.publicationFastPath.mutableFiles.map((file) => ({
|
|
2426
|
+
action: "sync-publication-fast-path",
|
|
2261
2427
|
surface: binding.surface,
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
}
|
|
2266
|
-
: [
|
|
2428
|
+
target: binding.bucket,
|
|
2429
|
+
path: file,
|
|
2430
|
+
qualificationRoot: binding.publicationFastPath.qualificationRoot,
|
|
2431
|
+
}))
|
|
2432
|
+
: [{
|
|
2433
|
+
action: "sync-static-artifact",
|
|
2434
|
+
surface: binding.surface,
|
|
2435
|
+
target: binding.bucket,
|
|
2436
|
+
prefix: binding.objectPrefix,
|
|
2437
|
+
deleteExcludes: binding.mutableDeleteExcludes || [],
|
|
2438
|
+
cacheControl: binding.cacheControl || undefined,
|
|
2439
|
+
},
|
|
2440
|
+
...(binding.cacheControl?.mutable
|
|
2441
|
+
? [{
|
|
2442
|
+
action: "apply-mutable-cache-control",
|
|
2443
|
+
surface: binding.surface,
|
|
2444
|
+
cacheControl: binding.cacheControl.mutable,
|
|
2445
|
+
patterns: ["*.html", "*.json", "*.xml"],
|
|
2446
|
+
excludes: binding.mutableDeleteExcludes || [],
|
|
2447
|
+
}]
|
|
2448
|
+
: []),
|
|
2449
|
+
]),
|
|
2267
2450
|
{
|
|
2268
2451
|
action: "write-deployment-manifest",
|
|
2269
2452
|
surface: binding.surface,
|
|
@@ -2274,13 +2457,16 @@ function planAdapterSteps(adapter, deployConfig, manifest) {
|
|
|
2274
2457
|
action: "invalidate-cdn",
|
|
2275
2458
|
surface: binding.surface,
|
|
2276
2459
|
distribution: binding.distributionId,
|
|
2460
|
+
paths: binding.publicationFastPath?.invalidationPaths,
|
|
2277
2461
|
},
|
|
2278
2462
|
]);
|
|
2279
2463
|
return [
|
|
2280
2464
|
...bindingSteps.filter((step) => step.action === "verify-immutable-artifact-before-upload"),
|
|
2281
2465
|
...bindingSteps.filter((step) => step.action === "sync-immutable-artifact"),
|
|
2282
2466
|
...bindingSteps.filter((step) => step.action === "verify-immutable-artifact-after-upload"),
|
|
2283
|
-
...cloudFrontDirectoryIndexRewriteOperations(
|
|
2467
|
+
...cloudFrontDirectoryIndexRewriteOperations(
|
|
2468
|
+
(manifest.surfaceBindings || []).filter((binding) => !binding.publicationFastPath),
|
|
2469
|
+
).map((operation) => ({
|
|
2284
2470
|
action: operation.action,
|
|
2285
2471
|
distribution: operation.routing.distributionId,
|
|
2286
2472
|
functionName: operation.routing.functionName,
|
|
@@ -37,9 +37,7 @@ export function resolveWebSurfaceProductionDecision({
|
|
|
37
37
|
reason: trusted ? "trusted-manual-dispatch" : "manual-actor-permission-insufficient",
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
|
-
const reviewedReleaseEvent =
|
|
41
|
-
(eventName === "push" && refName === "main") ||
|
|
42
|
-
(eventName === "pull_request" && eventAction === "closed");
|
|
40
|
+
const reviewedReleaseEvent = eventName === "push" && refName === "main";
|
|
43
41
|
if (
|
|
44
42
|
reviewedReleaseEvent &&
|
|
45
43
|
productionApply === true &&
|
|
@@ -57,6 +55,24 @@ export function resolveWebSurfaceProductionDecision({
|
|
|
57
55
|
reason: "reviewed-release-pr-merged",
|
|
58
56
|
});
|
|
59
57
|
}
|
|
58
|
+
if (
|
|
59
|
+
eventName === "pull_request" &&
|
|
60
|
+
eventAction === "closed" &&
|
|
61
|
+
productionApply === true &&
|
|
62
|
+
productionReleaseOnMain === true &&
|
|
63
|
+
releaseApproved === true
|
|
64
|
+
) {
|
|
65
|
+
return createWebSurfaceProductionDecision({
|
|
66
|
+
approved: false,
|
|
67
|
+
kind: "none",
|
|
68
|
+
repository,
|
|
69
|
+
sourceSha,
|
|
70
|
+
actor,
|
|
71
|
+
releasePr,
|
|
72
|
+
releaseSource,
|
|
73
|
+
reason: "release-pr-verified-awaiting-main-push",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
60
76
|
return createWebSurfaceProductionDecision({
|
|
61
77
|
approved: false,
|
|
62
78
|
kind: "none",
|