@kungfu-tech/buildchain 2.9.0 → 2.9.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.
@@ -11,6 +11,12 @@ import {
11
11
  import {
12
12
  createReadmeBadgeEndpointRegistry,
13
13
  } from "../packages/core/readme-badges.js";
14
+ import {
15
+ collectPublicSurfaceReverseAudit,
16
+ enumerateActionInputs,
17
+ enumerateCliCommandsFromBin,
18
+ enumerateWorkflowInputs,
19
+ } from "../packages/core/public-surface-audit.js";
14
20
  import { createSurfaceTimestampPolicy } from "../packages/core/surface-manifest.js";
15
21
 
16
22
  const SITE_BUNDLE_CONTRACT = "kungfu-buildchain-site-bundle";
@@ -276,29 +282,53 @@ function buildSiteBundle() {
276
282
  artifactDigestScope: "npm package dist/site JSON files",
277
283
  });
278
284
 
285
+ const cliPurposeById = new Map([
286
+ ["version", "Print the package or embedded binary version."],
287
+ ["help", "Print Buildchain CLI help."],
288
+ ["init", "Bootstrap a repository with Buildchain configuration and caller workflow files."],
289
+ ["validate", "Validate buildchain.toml and declared lifecycle surfaces."],
290
+ ["doctor", "Report local integration readiness."],
291
+ ["lifecycle", "Run configured lifecycle commands and write deterministic artifact manifests."],
292
+ ["release-dry-run", "Explain what a channel merge would publish before the PR is merged."],
293
+ ["release-line-open", "Plan or write the initial version-state commit for a new minor release line."],
294
+ ["release-transaction", "Inspect, recover, finalize, or abort durable release transactions."],
295
+ ["transaction-inspect", "Inspect durable release transaction state."],
296
+ ["collect-github-release", "Collect release assets into a release passport."],
297
+ ["verify-release-passport", "Fail closed unless a release passport and its evidence are complete."],
298
+ ["verify-artifact", "Verify artifact subjects against release passport evidence."],
299
+ ["verify-infra-contract-evidence-bundle", "Fail closed unless an infra-contract lifecycle evidence bundle is complete, hash-bound, and validation-consistent."],
300
+ ["verify-observability-log", "Verify Buildchain observability log events."],
301
+ ["explain-release", "Explain a release passport for humans or agents."],
302
+ ["explain-artifact", "Explain artifact evidence for humans or agents."],
303
+ ["inspect-release", "Inspect release passport evidence."],
304
+ ["inspect-artifact", "Inspect artifact evidence."],
305
+ ["release-propagation", "Plan channel-preserving downstream release PRs and write exact upstream release locks."],
306
+ ["logging", "Emit timestamped build events, summarize logs, and enforce required phases."],
307
+ ["mark", "Emit a single Buildchain log event."],
308
+ ["span", "Run a command inside a Buildchain log span."],
309
+ ["diagnostics-summary", "Summarize diagnostics artifacts into JSON and cross-platform lifecycle timing tables."],
310
+ ["build-facts", "Collect and verify Git source, version, module output, product artifact, and legacy Kungfu buildinfo facts."],
311
+ ["sample-process-tree", "Sample process-tree diagnostics for a wrapped command."],
312
+ ["npm-dry-run", "Verify npm publish shape before a release transaction."],
313
+ ["infra-contract", "Validate and publish provider-neutral infrastructure contract evidence."],
314
+ ["web-surface", "Plan, verify, and apply Buildchain web-surface deployments."],
315
+ ["badges-readme", "Generate or verify managed README badge blocks."],
316
+ ["badges-bundle", "Generate or verify the combined KFD and Release Passport badge bundle."],
317
+ ["homebrew-update-formula", "Generate Homebrew Formula metadata from upstream release passport evidence."],
318
+ ["homebrew-check", "Verify Homebrew tap metadata against upstream release passport evidence."],
319
+ ["publish-source", "Create, inspect, or verify publish-gate source-lock refs."],
320
+ ["build-contract", "Resolve Buildchain runtime contract metadata."],
321
+ ]);
279
322
  const cliRegistry = {
280
323
  schemaVersion: 1,
281
324
  contract: "kungfu-buildchain-cli-registry",
282
325
  binary: "buildchain",
283
326
  npmPackage: packageJson.name,
284
- commands: [
285
- { id: "version", usage: "buildchain version", purpose: "Print the package or embedded binary version." },
286
- { id: "init", usage: "buildchain init [--type package|native|web-surface|infra-contract|anchored-package]", purpose: "Bootstrap a repository with Buildchain configuration and caller workflow files." },
287
- { id: "validate", usage: "buildchain validate [--require-version-state]", purpose: "Validate buildchain.toml and declared lifecycle surfaces." },
288
- { id: "doctor", usage: "buildchain doctor [--json]", purpose: "Report local integration readiness." },
289
- { id: "lifecycle", usage: "buildchain lifecycle run <stage>", purpose: "Run configured lifecycle commands and write deterministic artifact manifests." },
290
- { id: "release-dry-run", usage: "buildchain release --dry-run --target-ref <ref>", purpose: "Explain what a channel merge would publish before the PR is merged." },
291
- { id: "collect-github-release", usage: "buildchain collect github-release --tag <tag>", purpose: "Collect release assets into a release passport." },
292
- { id: "verify-release-passport", usage: "buildchain verify release-passport <file-or-url>", purpose: "Fail closed unless a release passport and its evidence are complete." },
293
- { id: "release-propagation", usage: "buildchain release-propagation <plan|write-lock>", purpose: "Plan channel-preserving downstream release PRs and write exact upstream release locks." },
294
- { id: "verify-infra-contract-evidence-bundle", usage: "buildchain verify infra-contract-evidence-bundle <file>", purpose: "Fail closed unless an infra-contract lifecycle evidence bundle is complete, hash-bound, and validation-consistent." },
295
- { id: "logging", usage: "buildchain log|mark|span|verify observability-log", purpose: "Emit timestamped build events, summarize logs, and enforce required phases." },
296
- { id: "diagnostics-summary", usage: "buildchain diagnostics summary <diagnostics.json>...", purpose: "Summarize small diagnostics artifacts into JSON and a cross-platform lifecycle timing table." },
297
- { id: "build-facts", usage: "buildchain facts module|aggregate|verify", purpose: "Collect and verify Git source, version, module output, product artifact, and legacy Kungfu buildinfo facts." },
298
- { id: "npm-dry-run", usage: "buildchain npm dry-run --json", purpose: "Verify npm publish shape before a release transaction." },
299
- { id: "infra-contract", usage: "buildchain infra-contract --mode validate|ci|plan|contract|propagation-plan|propagation-apply|apply|evidence-bundle", purpose: "Validate and publish provider-neutral infrastructure contract evidence with a mutation-free CI evidence chain, provider command plans, configured provider command execution, saved-plan apply gates, dry-run-first propagation, and lifecycle evidence bundles." },
300
- { id: "homebrew", usage: "buildchain homebrew update-formula|check", purpose: "Generate and verify Homebrew tap Formula metadata as a distribution-index projection of upstream release passport evidence." },
301
- ],
327
+ commandSource: "bin/buildchain.mjs reverse enumeration",
328
+ commands: enumerateCliCommandsFromBin({ root }).map((entry) => ({
329
+ ...entry,
330
+ purpose: cliPurposeById.get(entry.id) || "Public Buildchain CLI surface. Add a specific purpose in scripts/generate-site-bundle.mjs when this command graduates.",
331
+ })),
302
332
  };
303
333
 
304
334
  const manualRegistry = {
@@ -374,21 +404,49 @@ function buildSiteBundle() {
374
404
  const workflowRegistry = {
375
405
  schemaVersion: 1,
376
406
  contract: "kungfu-buildchain-workflow-registry",
377
- workflows: [
378
- { id: "build", path: ".github/workflows/.build.yml", surface: "reusable-build", status: "active" },
379
- { id: "web-surface", path: ".github/workflows/.web-surface.yml", surface: "site-app-deployment", status: "active" },
380
- { id: "buildchain-ref-promotion", path: ".github/workflows/buildchain-ref-promotion.yml", surface: "release-governance", status: "active" },
381
- { id: "release-propagation", path: ".github/workflows/release-propagation.yml", surface: "release-propagation", status: "preview" },
382
- { id: "dev-pr-auto-merge", path: ".github/workflows/dev-pr-auto-merge.yml", surface: "dev-governance", status: "active" },
383
- { id: "binary-distribution", path: ".github/workflows/binary-distribution.yml", surface: "release-passport", status: "active" },
384
- ],
385
- actions: [
386
- { id: "validate-config", path: "actions/validate-config", status: "active" },
387
- { id: "run-lifecycle", path: "actions/run-lifecycle", status: "active" },
388
- { id: "promote-buildchain-ref", path: "actions/promote-buildchain-ref", status: "active" },
389
- { id: "report-buildchain-issue", path: "actions/report-buildchain-issue", status: "active" },
390
- ],
407
+ workflowSource: ".github/workflows reverse input enumeration",
408
+ workflows: enumerateWorkflowInputs({ root }).map((entry) => {
409
+ const surfaceById = new Map([
410
+ ["build", "reusable-build"],
411
+ ["web-surface", "site-app-deployment"],
412
+ ["buildchain-ref-promotion", "release-governance"],
413
+ ["release-line-bootstrap", "release-governance"],
414
+ ["release-candidate-promote", "release-governance"],
415
+ ["release-propagation", "release-propagation"],
416
+ ["dev-pr-auto-merge", "dev-governance"],
417
+ ["binary-distribution", "release-passport"],
418
+ ["buildchain-patrol", "repository-patrol"],
419
+ ["buildchain-patrol-daily", "repository-patrol"],
420
+ ["buildchain-patrol-weekly", "repository-patrol"],
421
+ ["buildchain-patrol-monthly", "repository-patrol"],
422
+ ["patrol-daily", "repository-patrol"],
423
+ ["patrol-weekly", "repository-patrol"],
424
+ ["patrol-monthly", "repository-patrol"],
425
+ ]);
426
+ const statusById = new Map([
427
+ ["release-propagation", "preview"],
428
+ ["candidate-lab", "repository-internal"],
429
+ ["build-surface-fixture", "repository-internal"],
430
+ ["self-hosted-runner-smoke", "compatibility-fixture"],
431
+ ]);
432
+ return {
433
+ ...entry,
434
+ surface: surfaceById.get(entry.id) || (entry.path.includes("/.") ? "reusable-workflow" : "repository-workflow"),
435
+ status: statusById.get(entry.id) || "active",
436
+ };
437
+ }),
438
+ actionSource: "actions/*/action.yml reverse input enumeration",
439
+ actions: enumerateActionInputs({ root }).map((entry) => ({
440
+ ...entry,
441
+ status: "active",
442
+ })),
391
443
  };
444
+ const publicSurfaceAudit = collectPublicSurfaceReverseAudit({
445
+ root,
446
+ cliRegistry,
447
+ workflowRegistry,
448
+ pageRegistry,
449
+ });
392
450
 
393
451
  const releaseModel = {
394
452
  schemaVersion: 1,
@@ -460,6 +518,7 @@ function buildSiteBundle() {
460
518
  "manual-registry.json",
461
519
  "node-api-registry.json",
462
520
  "workflow-registry.json",
521
+ "public-surface-audit.json",
463
522
  "release-model.json",
464
523
  "artifact-schemas.json",
465
524
  "buildchain-contract.json",
@@ -538,6 +597,7 @@ function buildSiteBundle() {
538
597
  "manual-registry.json",
539
598
  "node-api-registry.json",
540
599
  "workflow-registry.json",
600
+ "public-surface-audit.json",
541
601
  "release-model.json",
542
602
  "artifact-schemas.json",
543
603
  "badge-endpoint-registry.json",
@@ -560,6 +620,7 @@ function buildSiteBundle() {
560
620
  "manual-registry.json",
561
621
  "node-api-registry.json",
562
622
  "workflow-registry.json",
623
+ "public-surface-audit.json",
563
624
  "artifact-schemas.json",
564
625
  "buildchain-contract.json",
565
626
  "kfd-claims.json",
@@ -690,6 +751,7 @@ function buildSiteBundle() {
690
751
  "release model facts",
691
752
  "workflow and action registries",
692
753
  "CLI command registry",
754
+ "public surface reverse audit",
693
755
  "manual and Node API registries",
694
756
  "KFD claim registry",
695
757
  "release-passport evidence vocabulary",
@@ -715,6 +777,7 @@ function buildSiteBundle() {
715
777
  "manual-registry.json": manualRegistry,
716
778
  "node-api-registry.json": nodeApiRegistry,
717
779
  "workflow-registry.json": workflowRegistry,
780
+ "public-surface-audit.json": publicSurfaceAudit,
718
781
  "release-model.json": releaseModel,
719
782
  "artifact-schemas.json": artifactSchemas,
720
783
  "badge-endpoint-registry.json": badgeEndpointRegistry,
@@ -324,6 +324,70 @@ function directoryIndexAliasOperations({ surfaceArtifactRoot, bucket, binding })
324
324
  });
325
325
  }
326
326
 
327
+ function cloudfrontDefaultRootObjectOperation({ distributionId, directoryIndex = "index.html" }) {
328
+ const normalizedDistribution = String(distributionId || "").trim();
329
+ const normalizedDirectoryIndex = normalizeS3Key(directoryIndex || "index.html");
330
+ if (!normalizedDistribution || !normalizedDirectoryIndex || normalizedDirectoryIndex.includes("/")) {
331
+ return null;
332
+ }
333
+ return {
334
+ action: "ensure-cloudfront-default-root-object",
335
+ surface: "__distribution__",
336
+ command: "bash",
337
+ args: [
338
+ "-euo",
339
+ "pipefail",
340
+ "-c",
341
+ [
342
+ 'distribution_id="$1"',
343
+ 'root_object="$2"',
344
+ 'tmp_dir="$(mktemp -d)"',
345
+ 'trap \'rm -rf "$tmp_dir"\' EXIT',
346
+ 'aws cloudfront get-distribution-config --id "$distribution_id" > "$tmp_dir/distribution.json"',
347
+ 'etag="$(jq -r \'.ETag\' "$tmp_dir/distribution.json")"',
348
+ 'current="$(jq -r \'.DistributionConfig.DefaultRootObject // ""\' "$tmp_dir/distribution.json")"',
349
+ 'if [ "$current" = "$root_object" ]; then',
350
+ ' echo "cloudfront default root object already $root_object for $distribution_id"',
351
+ ' exit 0',
352
+ 'fi',
353
+ 'jq --arg root "$root_object" \'.DistributionConfig | .DefaultRootObject = $root\' "$tmp_dir/distribution.json" > "$tmp_dir/distribution-config.json"',
354
+ 'aws cloudfront update-distribution --id "$distribution_id" --if-match "$etag" --distribution-config "file://$tmp_dir/distribution-config.json" >/dev/null',
355
+ 'aws cloudfront wait distribution-deployed --id "$distribution_id"',
356
+ 'echo "cloudfront default root object set to $root_object for $distribution_id"',
357
+ ].join("\n"),
358
+ "buildchain-cloudfront-default-root-object",
359
+ normalizedDistribution,
360
+ normalizedDirectoryIndex,
361
+ ],
362
+ routing: {
363
+ contract: "kungfu-buildchain-web-surface-cloudfront-default-root-object",
364
+ distributionId: normalizedDistribution,
365
+ defaultRootObject: normalizedDirectoryIndex,
366
+ reason: "Surface host root requests must resolve to index.html before host/path prefix rewrite reaches the S3 origin.",
367
+ },
368
+ };
369
+ }
370
+
371
+ function cloudfrontDefaultRootObjectOperations({ bindings }) {
372
+ const seen = new Set();
373
+ return (bindings || []).flatMap((binding) => {
374
+ if (binding.directoryIndexResolution === false || !binding.distributionId) {
375
+ return [];
376
+ }
377
+ const directoryIndex = binding.directoryIndex || "index.html";
378
+ const key = `${binding.distributionId}:${directoryIndex}`;
379
+ if (seen.has(key)) {
380
+ return [];
381
+ }
382
+ seen.add(key);
383
+ const operation = cloudfrontDefaultRootObjectOperation({
384
+ distributionId: binding.distributionId,
385
+ directoryIndex,
386
+ });
387
+ return operation ? [operation] : [];
388
+ });
389
+ }
390
+
327
391
  function defaultCommandRunner({ command, args, stdin = "" }) {
328
392
  const result = spawnSync(command, args, {
329
393
  encoding: "utf8",
@@ -941,12 +1005,15 @@ export function applyWebSurfaceDeploy({
941
1005
  });
942
1006
  }
943
1007
  }
944
- const operations = bindings.flatMap((binding) => deployBindingOperations({
945
- artifactRoot,
946
- deployConfig,
947
- manifest: resolvedPlan.manifest,
948
- binding,
949
- }));
1008
+ const operations = [
1009
+ ...cloudfrontDefaultRootObjectOperations({ bindings }),
1010
+ ...bindings.flatMap((binding) => deployBindingOperations({
1011
+ artifactRoot,
1012
+ deployConfig,
1013
+ manifest: resolvedPlan.manifest,
1014
+ binding,
1015
+ })),
1016
+ ];
950
1017
  const primaryBinding = bindings[0] || {};
951
1018
  const objectPrefix = primaryBinding.objectPrefix || objectPrefixFor(deployConfig, resolvedPlan.manifest.alias || resolvedPlan.manifest.channel);
952
1019
  const manifestKey = primaryBinding.manifestKey || deployManifestKey(deployConfig, resolvedPlan.manifest);
@@ -1428,6 +1495,12 @@ function deployBindingOperations({ artifactRoot, deployConfig, manifest, binding
1428
1495
  function planAdapterSteps(adapter, deployConfig, manifest) {
1429
1496
  if (adapter === "aws-s3-cloudfront") {
1430
1497
  return [
1498
+ ...cloudfrontDefaultRootObjectOperations({ bindings: manifest.surfaceBindings }).map((operation) => ({
1499
+ action: operation.action,
1500
+ surface: operation.surface,
1501
+ distribution: operation.routing.distributionId,
1502
+ defaultRootObject: operation.routing.defaultRootObject,
1503
+ })),
1431
1504
  ...manifest.surfaceBindings.flatMap((binding) => [
1432
1505
  {
1433
1506
  action: "sync-static-artifact",