@kungfu-tech/buildchain 2.10.2 → 2.10.3
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/dist/site/agent-index.json +1 -0
- package/dist/site/artifact-schemas.json +1 -0
- package/dist/site/buildchain-contract.json +4 -4
- package/dist/site/buildchain-site.json +296 -16
- package/dist/site/capability-registry.json +302 -0
- package/dist/site/cli-registry.json +364 -52
- package/dist/site/kfd-claims.json +27 -3
- package/dist/site/manual-registry.json +210 -25
- package/dist/site/node-api-registry.json +162 -22
- package/dist/site/page-registry.json +273 -8
- package/dist/site/public-surface-audit.json +3 -3
- package/dist/site/release-provenance.json +1 -0
- package/dist/site/site-manifest.json +17 -8
- package/dist/site/workflow-registry.json +39 -0
- package/docs/MAP.md +19 -0
- package/docs/cli.md +5 -0
- package/docs/site-bundle-contract.md +9 -0
- package/docs/web-surface-deployments.md +18 -0
- package/package.json +2 -1
- package/packages/core/buildchain-kfd-claims.js +3 -0
- package/scripts/check-inventory.mjs +43 -2
- package/scripts/generate-site-bundle.mjs +352 -59
- package/scripts/web-surface-core.mjs +147 -0
- package/scripts/web-surface.mjs +16 -1
|
@@ -1309,6 +1309,133 @@ function htmlLikeResponse(response, url = "") {
|
|
|
1309
1309
|
return String(contentType).toLowerCase().includes("text/html") || /\/$|\.html(?:$|[?#])/.test(String(url || ""));
|
|
1310
1310
|
}
|
|
1311
1311
|
|
|
1312
|
+
const managedNetworkRequiredEvidence = [
|
|
1313
|
+
"sync-static-artifact",
|
|
1314
|
+
"write-deployment-manifest",
|
|
1315
|
+
];
|
|
1316
|
+
|
|
1317
|
+
function operationEvidenceStatus(operation, { plannedEvidence = false } = {}) {
|
|
1318
|
+
if (plannedEvidence && operation.status === undefined) {
|
|
1319
|
+
return true;
|
|
1320
|
+
}
|
|
1321
|
+
return operation.status === "applied" || operation.status === "planned";
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
function managedNetworkHealthEvidence({ target, result, plan }) {
|
|
1325
|
+
const operationSource = Array.isArray(result?.operations) && result.operations.length > 0
|
|
1326
|
+
? "apply-result"
|
|
1327
|
+
: "deploy-plan";
|
|
1328
|
+
const operations = operationSource === "apply-result"
|
|
1329
|
+
? result.operations
|
|
1330
|
+
: (Array.isArray(plan?.steps) ? plan.steps : []);
|
|
1331
|
+
const surfaceOperations = operations.filter((operation) => operation.surface === target.surface);
|
|
1332
|
+
const presentActions = new Set(
|
|
1333
|
+
surfaceOperations
|
|
1334
|
+
.filter((operation) => operationEvidenceStatus(operation, { plannedEvidence: operationSource === "deploy-plan" }))
|
|
1335
|
+
.map((operation) => operation.action),
|
|
1336
|
+
);
|
|
1337
|
+
const missingActions = managedNetworkRequiredEvidence.filter((action) => !presentActions.has(action));
|
|
1338
|
+
const missingFields = [];
|
|
1339
|
+
if (!target.manifestKey) missingFields.push("manifestKey");
|
|
1340
|
+
if (!target.bucket) missingFields.push("bucket");
|
|
1341
|
+
if (!target.objectPrefix) missingFields.push("objectPrefix");
|
|
1342
|
+
return {
|
|
1343
|
+
source: operationSource,
|
|
1344
|
+
actions: [...presentActions].sort(),
|
|
1345
|
+
requiredActions: managedNetworkRequiredEvidence,
|
|
1346
|
+
missingActions,
|
|
1347
|
+
missingFields,
|
|
1348
|
+
status: missingActions.length === 0 && missingFields.length === 0 ? "pass" : "fail",
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
function managedNetworkTargetObjectKey(target) {
|
|
1353
|
+
const requestPath = String(target.requestPath || "/");
|
|
1354
|
+
let relative = requestPath.replace(/^\/+/, "");
|
|
1355
|
+
if (!relative) {
|
|
1356
|
+
relative = "index.html";
|
|
1357
|
+
} else if (requestPath.endsWith("/")) {
|
|
1358
|
+
relative = `${relative.replace(/\/+$/, "")}/index.html`;
|
|
1359
|
+
}
|
|
1360
|
+
return joinS3Key(target.objectPrefix, relative);
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
function managedNetworkHeadObject({ target, kind, key, commandRunner }) {
|
|
1364
|
+
return runAdapterOperation({
|
|
1365
|
+
operation: {
|
|
1366
|
+
action: `health-head-${kind}`,
|
|
1367
|
+
surface: target.surface,
|
|
1368
|
+
command: "aws",
|
|
1369
|
+
args: ["s3api", "head-object", "--bucket", target.bucket, "--key", key],
|
|
1370
|
+
},
|
|
1371
|
+
dryRun: false,
|
|
1372
|
+
commandRunner,
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
function managedNetworkS3HealthCheck({ target, evidence, commandRunner }) {
|
|
1377
|
+
const objectKey = managedNetworkTargetObjectKey(target);
|
|
1378
|
+
const headResults = [
|
|
1379
|
+
managedNetworkHeadObject({ target, kind: "manifest", key: target.manifestKey, commandRunner }),
|
|
1380
|
+
managedNetworkHeadObject({ target, kind: "object", key: objectKey, commandRunner }),
|
|
1381
|
+
];
|
|
1382
|
+
const failed = headResults.filter((operation) => operation.status === "failed");
|
|
1383
|
+
return {
|
|
1384
|
+
surface: target.surface,
|
|
1385
|
+
kind: target.kind,
|
|
1386
|
+
requestPath: target.requestPath,
|
|
1387
|
+
url: target.url,
|
|
1388
|
+
accessControl: "managed-network",
|
|
1389
|
+
healthStrategy: "s3-object",
|
|
1390
|
+
status: evidence.status === "pass" && failed.length === 0 ? "pass" : "fail",
|
|
1391
|
+
httpStatus: null,
|
|
1392
|
+
finalUrl: "",
|
|
1393
|
+
noindexHeader: false,
|
|
1394
|
+
manifestKey: target.manifestKey || "",
|
|
1395
|
+
bucket: target.bucket || "",
|
|
1396
|
+
objectPrefix: target.objectPrefix || "",
|
|
1397
|
+
objectKey,
|
|
1398
|
+
evidence,
|
|
1399
|
+
s3Checks: headResults.map((operation) => ({
|
|
1400
|
+
action: operation.action,
|
|
1401
|
+
bucket: target.bucket,
|
|
1402
|
+
key: operation.args?.[operation.args.indexOf("--key") + 1] || "",
|
|
1403
|
+
status: operation.status,
|
|
1404
|
+
exitCode: operation.exitCode,
|
|
1405
|
+
stderr: operation.stderr || "",
|
|
1406
|
+
})),
|
|
1407
|
+
message: evidence.status === "pass" && failed.length === 0
|
|
1408
|
+
? "managed-network surface health verified from S3 manifest and object head checks; direct public fetch skipped"
|
|
1409
|
+
: `managed-network S3 object health failed: ${failed.map((operation) => `${operation.action}:${operation.stderr || operation.exitCode}`).join("; ") || "missing deployment evidence"}`,
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
function managedNetworkHealthCheck({ target, result, plan, commandRunner, verifyS3Objects = true }) {
|
|
1414
|
+
const evidence = managedNetworkHealthEvidence({ target, result, plan });
|
|
1415
|
+
if (verifyS3Objects && result?.status === "applied" && evidence.status === "pass") {
|
|
1416
|
+
return managedNetworkS3HealthCheck({ target, evidence, commandRunner });
|
|
1417
|
+
}
|
|
1418
|
+
return {
|
|
1419
|
+
surface: target.surface,
|
|
1420
|
+
kind: target.kind,
|
|
1421
|
+
requestPath: target.requestPath,
|
|
1422
|
+
url: target.url,
|
|
1423
|
+
accessControl: "managed-network",
|
|
1424
|
+
healthStrategy: "deployment-evidence",
|
|
1425
|
+
status: evidence.status,
|
|
1426
|
+
httpStatus: null,
|
|
1427
|
+
finalUrl: "",
|
|
1428
|
+
noindexHeader: false,
|
|
1429
|
+
manifestKey: target.manifestKey || "",
|
|
1430
|
+
bucket: target.bucket || "",
|
|
1431
|
+
objectPrefix: target.objectPrefix || "",
|
|
1432
|
+
evidence,
|
|
1433
|
+
message: evidence.status === "pass"
|
|
1434
|
+
? "managed-network surface health verified from deployment manifest and S3 object sync evidence; direct public fetch skipped"
|
|
1435
|
+
: `managed-network surface health requires manifest and S3 object evidence; missing actions=${evidence.missingActions.join(",") || "none"}, missing fields=${evidence.missingFields.join(",") || "none"}`,
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1312
1439
|
export async function checkWebSurfaceHealth({
|
|
1313
1440
|
result = null,
|
|
1314
1441
|
plan = null,
|
|
@@ -1316,6 +1443,9 @@ export async function checkWebSurfaceHealth({
|
|
|
1316
1443
|
fetchImpl = fetch,
|
|
1317
1444
|
checkedAt = new Date().toISOString(),
|
|
1318
1445
|
allowedStatuses = [200],
|
|
1446
|
+
allowedManagedNetworkRunner = false,
|
|
1447
|
+
managedNetworkS3ObjectVerification = true,
|
|
1448
|
+
commandRunner = defaultCommandRunner,
|
|
1319
1449
|
} = {}) {
|
|
1320
1450
|
const loadedConfig = loadBuildchainConfig(cwd);
|
|
1321
1451
|
const config = assertWebSurfaceConfig(loadedConfig);
|
|
@@ -1351,6 +1481,10 @@ export async function checkWebSurfaceHealth({
|
|
|
1351
1481
|
required: smoke.required !== false,
|
|
1352
1482
|
missing: Boolean(smoke.missing),
|
|
1353
1483
|
message: smoke.message || "",
|
|
1484
|
+
accessControl: binding.accessControl || config.channels?.[channel]?.accessControl || "",
|
|
1485
|
+
manifestKey: binding.manifestKey || "",
|
|
1486
|
+
bucket: binding.bucket || "",
|
|
1487
|
+
objectPrefix: binding.objectPrefix || "",
|
|
1354
1488
|
}));
|
|
1355
1489
|
})
|
|
1356
1490
|
: Object.entries(urls).map(([surface, url]) => ({
|
|
@@ -1359,6 +1493,7 @@ export async function checkWebSurfaceHealth({
|
|
|
1359
1493
|
requestPath: "/",
|
|
1360
1494
|
url,
|
|
1361
1495
|
required: true,
|
|
1496
|
+
accessControl: config.channels?.[channel]?.accessControl || "",
|
|
1362
1497
|
}));
|
|
1363
1498
|
|
|
1364
1499
|
for (const target of smokeTargets) {
|
|
@@ -1377,6 +1512,16 @@ export async function checkWebSurfaceHealth({
|
|
|
1377
1512
|
});
|
|
1378
1513
|
continue;
|
|
1379
1514
|
}
|
|
1515
|
+
if (target.accessControl === "managed-network" && !allowedManagedNetworkRunner) {
|
|
1516
|
+
checks.push(managedNetworkHealthCheck({
|
|
1517
|
+
target,
|
|
1518
|
+
result,
|
|
1519
|
+
plan,
|
|
1520
|
+
commandRunner,
|
|
1521
|
+
verifyS3Objects: managedNetworkS3ObjectVerification,
|
|
1522
|
+
}));
|
|
1523
|
+
continue;
|
|
1524
|
+
}
|
|
1380
1525
|
try {
|
|
1381
1526
|
const response = await fetchImpl(url, { redirect: "follow" });
|
|
1382
1527
|
const expectedNoindex = Boolean(config.channels?.[channel]?.noindex);
|
|
@@ -1393,6 +1538,8 @@ export async function checkWebSurfaceHealth({
|
|
|
1393
1538
|
kind: target.kind,
|
|
1394
1539
|
requestPath: target.requestPath,
|
|
1395
1540
|
url,
|
|
1541
|
+
accessControl: target.accessControl || "",
|
|
1542
|
+
healthStrategy: "http",
|
|
1396
1543
|
status: statusOk && noindexOk ? "pass" : "fail",
|
|
1397
1544
|
httpStatus: response.status,
|
|
1398
1545
|
finalUrl: response.url || url,
|
package/scripts/web-surface.mjs
CHANGED
|
@@ -231,7 +231,22 @@ export function webSurfaceCli() {
|
|
|
231
231
|
const resultFile = readArg("result", process.env.BUILDCHAIN_WEB_SURFACE_APPLY_RESULT || "");
|
|
232
232
|
const result = resultFile ? JSON.parse(fs.readFileSync(path.resolve(resultFile), "utf8")) : null;
|
|
233
233
|
const plan = readJsonFileArg("plan");
|
|
234
|
-
const
|
|
234
|
+
const envAllowedManagedNetworkRunner = process.env.BUILDCHAIN_WEB_SURFACE_HEALTH_ALLOWED_RUNNER === "true" ||
|
|
235
|
+
process.env.BUILDCHAIN_WEB_SURFACE_HEALTH_ALLOWED_RUNNER === "1";
|
|
236
|
+
const health = checkWebSurfaceHealth({
|
|
237
|
+
cwd,
|
|
238
|
+
result,
|
|
239
|
+
plan,
|
|
240
|
+
allowedManagedNetworkRunner: readBooleanArg(
|
|
241
|
+
"allowed-managed-network-runner",
|
|
242
|
+
envAllowedManagedNetworkRunner,
|
|
243
|
+
),
|
|
244
|
+
managedNetworkS3ObjectVerification: readBooleanArg(
|
|
245
|
+
"managed-network-s3-object-verification",
|
|
246
|
+
process.env.BUILDCHAIN_WEB_SURFACE_HEALTH_S3_OBJECTS !== "false" &&
|
|
247
|
+
process.env.BUILDCHAIN_WEB_SURFACE_HEALTH_S3_OBJECTS !== "0",
|
|
248
|
+
),
|
|
249
|
+
});
|
|
235
250
|
return Promise.resolve(health).then((resolved) => {
|
|
236
251
|
writeJson(resolved, output);
|
|
237
252
|
writeGitHubOutputs({
|