@intentius/chant 0.13.1 → 0.15.0
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/audit/catalog.d.ts +45 -5
- package/dist/audit/catalog.d.ts.map +1 -1
- package/dist/audit/proof.d.ts +3 -0
- package/dist/audit/proof.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +4 -1
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/report.d.ts +3 -0
- package/dist/audit/report.d.ts.map +1 -1
- package/dist/audit/rules-doc.d.ts +8 -2
- package/dist/audit/rules-doc.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/lexicon-rolling-upgrade.d.ts +37 -0
- package/dist/cli/commands/lexicon-rolling-upgrade.d.ts.map +1 -0
- package/dist/cli/commands/lexicon-surface-diff.d.ts +48 -0
- package/dist/cli/commands/lexicon-surface-diff.d.ts.map +1 -0
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/pinned-upgrade.d.ts +41 -0
- package/dist/cli/commands/pinned-upgrade.d.ts.map +1 -0
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/components.d.ts +39 -0
- package/dist/cli/handlers/components.d.ts.map +1 -0
- package/dist/cli/handlers/dev.d.ts +3 -0
- package/dist/cli/handlers/dev.d.ts.map +1 -1
- package/dist/cli/handlers/graph.d.ts +4 -2
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts +49 -0
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +35 -0
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/fetch.d.ts +22 -0
- package/dist/codegen/fetch.d.ts.map +1 -1
- package/dist/codegen/lexicon-regen.d.ts +110 -0
- package/dist/codegen/lexicon-regen.d.ts.map +1 -0
- package/dist/codegen/pinned-upgrade.d.ts +153 -0
- package/dist/codegen/pinned-upgrade.d.ts.map +1 -0
- package/dist/codegen/rolling-upgrade.d.ts +138 -0
- package/dist/codegen/rolling-upgrade.d.ts.map +1 -0
- package/dist/codegen/surface-snapshot.d.ts +173 -0
- package/dist/codegen/surface-snapshot.d.ts.map +1 -0
- package/dist/codegen/version-bump.d.ts +21 -0
- package/dist/codegen/version-bump.d.ts.map +1 -0
- package/dist/components/auto-release.d.ts +93 -0
- package/dist/components/auto-release.d.ts.map +1 -0
- package/dist/components/builders.d.ts +37 -0
- package/dist/components/builders.d.ts.map +1 -0
- package/dist/components/capability-plugin-loader.d.ts +111 -0
- package/dist/components/capability-plugin-loader.d.ts.map +1 -0
- package/dist/components/capability-plugin-schema.d.ts +24 -0
- package/dist/components/capability-plugin-schema.d.ts.map +1 -0
- package/dist/components/capability-plugin.d.ts +85 -0
- package/dist/components/capability-plugin.d.ts.map +1 -0
- package/dist/components/capability.d.ts +89 -0
- package/dist/components/capability.d.ts.map +1 -0
- package/dist/components/cli-support.d.ts +204 -0
- package/dist/components/cli-support.d.ts.map +1 -0
- package/dist/components/component.d.ts +179 -0
- package/dist/components/component.d.ts.map +1 -0
- package/dist/components/config-defaults.d.ts +41 -0
- package/dist/components/config-defaults.d.ts.map +1 -0
- package/dist/components/discover.d.ts +60 -0
- package/dist/components/discover.d.ts.map +1 -0
- package/dist/components/driver-output.d.ts +20 -0
- package/dist/components/driver-output.d.ts.map +1 -0
- package/dist/components/driver.d.ts +209 -0
- package/dist/components/driver.d.ts.map +1 -0
- package/dist/components/index.d.ts +22 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/manifest-persistence.d.ts +79 -0
- package/dist/components/manifest-persistence.d.ts.map +1 -0
- package/dist/components/pilots/alb-ecs.pilot.d.ts +27 -0
- package/dist/components/pilots/alb-ecs.pilot.d.ts.map +1 -0
- package/dist/components/pilots/dynamodb.pilot.d.ts +22 -0
- package/dist/components/pilots/dynamodb.pilot.d.ts.map +1 -0
- package/dist/components/pilots/jar-emr.pilot.d.ts +37 -0
- package/dist/components/pilots/jar-emr.pilot.d.ts.map +1 -0
- package/dist/components/pilots/lambda.pilot.d.ts +35 -0
- package/dist/components/pilots/lambda.pilot.d.ts.map +1 -0
- package/dist/components/pilots/neo4j-fanout.pilot.d.ts +17 -0
- package/dist/components/pilots/neo4j-fanout.pilot.d.ts.map +1 -0
- package/dist/components/pilots/project.d.ts +13 -0
- package/dist/components/pilots/project.d.ts.map +1 -0
- package/dist/components/presets/ecs-fargate.d.ts +59 -0
- package/dist/components/presets/ecs-fargate.d.ts.map +1 -0
- package/dist/components/presets/index.d.ts +29 -0
- package/dist/components/presets/index.d.ts.map +1 -0
- package/dist/components/presets/lambda.d.ts +35 -0
- package/dist/components/presets/lambda.d.ts.map +1 -0
- package/dist/components/presets/single-host-compose.d.ts +43 -0
- package/dist/components/presets/single-host-compose.d.ts.map +1 -0
- package/dist/components/registry.d.ts +29 -0
- package/dist/components/registry.d.ts.map +1 -0
- package/dist/components/starter-plugin.d.ts +51 -0
- package/dist/components/starter-plugin.d.ts.map +1 -0
- package/dist/components/temporal-codegen-loader.d.ts +27 -0
- package/dist/components/temporal-codegen-loader.d.ts.map +1 -0
- package/dist/components/verbs/__tests__/mock-cloud-executor.d.ts +35 -0
- package/dist/components/verbs/__tests__/mock-cloud-executor.d.ts.map +1 -0
- package/dist/components/verbs/__tests__/mock-process-runner.d.ts +45 -0
- package/dist/components/verbs/__tests__/mock-process-runner.d.ts.map +1 -0
- package/dist/components/verbs/__tests__/mock-sbom-generator.d.ts +33 -0
- package/dist/components/verbs/__tests__/mock-sbom-generator.d.ts.map +1 -0
- package/dist/components/verbs/bom-writer.d.ts +155 -0
- package/dist/components/verbs/bom-writer.d.ts.map +1 -0
- package/dist/components/verbs/build-archive.d.ts +205 -0
- package/dist/components/verbs/build-archive.d.ts.map +1 -0
- package/dist/components/verbs/build.d.ts +150 -0
- package/dist/components/verbs/build.d.ts.map +1 -0
- package/dist/components/verbs/cloud-executor.d.ts +88 -0
- package/dist/components/verbs/cloud-executor.d.ts.map +1 -0
- package/dist/components/verbs/component-bom.d.ts +103 -0
- package/dist/components/verbs/component-bom.d.ts.map +1 -0
- package/dist/components/verbs/index.d.ts +49 -0
- package/dist/components/verbs/index.d.ts.map +1 -0
- package/dist/components/verbs/license-policy.d.ts +56 -0
- package/dist/components/verbs/license-policy.d.ts.map +1 -0
- package/dist/components/verbs/lockfile-sbom-generator.d.ts +81 -0
- package/dist/components/verbs/lockfile-sbom-generator.d.ts.map +1 -0
- package/dist/components/verbs/process-runner.d.ts +64 -0
- package/dist/components/verbs/process-runner.d.ts.map +1 -0
- package/dist/components/verbs/reproducibility.d.ts +80 -0
- package/dist/components/verbs/reproducibility.d.ts.map +1 -0
- package/dist/components/verbs/sbom-generator.d.ts +157 -0
- package/dist/components/verbs/sbom-generator.d.ts.map +1 -0
- package/dist/components/verbs/sbom.d.ts +79 -0
- package/dist/components/verbs/sbom.d.ts.map +1 -0
- package/dist/components/verbs/shell.d.ts +35 -0
- package/dist/components/verbs/shell.d.ts.map +1 -0
- package/dist/components/verbs/sign.d.ts +222 -0
- package/dist/components/verbs/sign.d.ts.map +1 -0
- package/dist/components/verbs/stub.d.ts +18 -0
- package/dist/components/verbs/stub.d.ts.map +1 -0
- package/dist/components/verbs/tool-sbom-generator.d.ts +51 -0
- package/dist/components/verbs/tool-sbom-generator.d.ts.map +1 -0
- package/dist/components/verbs/verify.d.ts +106 -0
- package/dist/components/verbs/verify.d.ts.map +1 -0
- package/dist/components/verbs/vex.d.ts +65 -0
- package/dist/components/verbs/vex.d.ts.map +1 -0
- package/dist/components/verbs/vuln-gate.d.ts +91 -0
- package/dist/components/verbs/vuln-gate.d.ts.map +1 -0
- package/dist/components/verbs/vuln-scan.d.ts +116 -0
- package/dist/components/verbs/vuln-scan.d.ts.map +1 -0
- package/dist/components/verbs/wait-verify.d.ts +85 -0
- package/dist/components/verbs/wait-verify.d.ts.map +1 -0
- package/dist/config.d.ts +205 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/lexicon.d.ts +78 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/build-ledger-store.d.ts +121 -0
- package/dist/lifecycle/build-ledger-store.d.ts.map +1 -0
- package/dist/lifecycle/build-ledger.d.ts +225 -0
- package/dist/lifecycle/build-ledger.d.ts.map +1 -0
- package/dist/lifecycle/git.d.ts +84 -0
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/index.d.ts +5 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/oras-referrer-lookup.d.ts +41 -0
- package/dist/lifecycle/oras-referrer-lookup.d.ts.map +1 -0
- package/dist/lifecycle/release-ledger.d.ts +122 -0
- package/dist/lifecycle/release-ledger.d.ts.map +1 -0
- package/dist/lifecycle/status.d.ts +150 -0
- package/dist/lifecycle/status.d.ts.map +1 -0
- package/dist/lint/component-checks.d.ts +95 -0
- package/dist/lint/component-checks.d.ts.map +1 -0
- package/dist/lint/engine.d.ts +23 -0
- package/dist/lint/engine.d.ts.map +1 -1
- package/dist/lint/presets/strict.json +8 -1
- package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.d.ts +10 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.d.ts +3 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.d.ts +3 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp001-publishes-never-applies.d.ts +20 -0
- package/dist/lint/rules/comp/comp001-publishes-never-applies.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp002-dangling-wiring-ref.d.ts +28 -0
- package/dist/lint/rules/comp/comp002-dangling-wiring-ref.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts +39 -0
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +37 -0
- package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp005-capability-kind-is-noun.d.ts +43 -0
- package/dist/lint/rules/comp/comp005-capability-kind-is-noun.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp006-shell-needs-reason.d.ts +22 -0
- package/dist/lint/rules/comp/comp006-shell-needs-reason.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp007-composition-sprawl.d.ts +33 -0
- package/dist/lint/rules/comp/comp007-composition-sprawl.d.ts.map +1 -0
- package/dist/lint/rules/comp/index.d.ts +18 -0
- package/dist/lint/rules/comp/index.d.ts.map +1 -0
- package/dist/lint/rules/comp/support.d.ts +71 -0
- package/dist/lint/rules/comp/support.d.ts.map +1 -0
- package/dist/lsp/lexicon-providers.d.ts.map +1 -1
- package/dist/op/activity-registry.d.ts +11 -6
- package/dist/op/activity-registry.d.ts.map +1 -1
- package/dist/op/activity-runtime.d.ts +24 -0
- package/dist/op/activity-runtime.d.ts.map +1 -0
- package/dist/op/builders.d.ts +56 -0
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/index.d.ts +2 -1
- package/dist/op/index.d.ts.map +1 -1
- package/dist/ownership.d.ts +20 -14
- package/dist/ownership.d.ts.map +1 -1
- package/package.json +14 -1
- package/src/audit/catalog.test.ts +74 -22
- package/src/audit/catalog.ts +74 -471
- package/src/audit/proof.ts +4 -2
- package/src/audit/report-html.test.ts +14 -7
- package/src/audit/report-model.ts +20 -9
- package/src/audit/report.test.ts +36 -13
- package/src/audit/report.ts +2 -0
- package/src/audit/rules-doc.test.ts +8 -6
- package/src/audit/rules-doc.ts +13 -5
- package/src/cli/commands/audit.ts +23 -18
- package/src/cli/commands/component-lint.test.ts +158 -0
- package/src/cli/commands/doctor.test.ts +116 -2
- package/src/cli/commands/doctor.ts +46 -0
- package/src/cli/commands/lexicon-rolling-upgrade.ts +198 -0
- package/src/cli/commands/lexicon-surface-diff.test.ts +104 -0
- package/src/cli/commands/lexicon-surface-diff.ts +185 -0
- package/src/cli/commands/lint.ts +142 -1
- package/src/cli/commands/pinned-upgrade.ts +204 -0
- package/src/cli/handlers/build.ts +58 -1
- package/src/cli/handlers/components.test.ts +504 -0
- package/src/cli/handlers/components.ts +404 -0
- package/src/cli/handlers/dev.ts +60 -1
- package/src/cli/handlers/graph.ts +47 -2
- package/src/cli/handlers/misc.ts +45 -1
- package/src/cli/handlers/run.test.ts +967 -3
- package/src/cli/handlers/run.ts +618 -11
- package/src/cli/main.test.ts +66 -0
- package/src/cli/main.ts +105 -2
- package/src/cli/registry.ts +35 -0
- package/src/codegen/fetch.test.ts +84 -3
- package/src/codegen/fetch.ts +76 -2
- package/src/codegen/lexicon-regen.test.ts +298 -0
- package/src/codegen/lexicon-regen.ts +545 -0
- package/src/codegen/pinned-upgrade.test.ts +399 -0
- package/src/codegen/pinned-upgrade.ts +423 -0
- package/src/codegen/rolling-upgrade.test.ts +333 -0
- package/src/codegen/rolling-upgrade.ts +320 -0
- package/src/codegen/surface-snapshot.test.ts +461 -0
- package/src/codegen/surface-snapshot.ts +546 -0
- package/src/codegen/version-bump.test.ts +47 -0
- package/src/codegen/version-bump.ts +39 -0
- package/src/components/README.md +130 -0
- package/src/components/SPRAWL-VALIDATION.md +181 -0
- package/src/components/__fixtures__/alb-ecs-service.json +51 -0
- package/src/components/__fixtures__/dynamodb-infra.json +32 -0
- package/src/components/__fixtures__/emr-job-consumer.json +26 -0
- package/src/components/__fixtures__/jar-lib-producer.json +16 -0
- package/src/components/__fixtures__/lambda-image-processor.json +28 -0
- package/src/components/__fixtures__/neo4j-fanout.json +34 -0
- package/src/components/__fixtures__/single-host-compose.json +29 -0
- package/src/components/auto-release.test.ts +217 -0
- package/src/components/auto-release.ts +216 -0
- package/src/components/builders.test.ts +45 -0
- package/src/components/builders.ts +71 -0
- package/src/components/capability-plugin-loader.ts +218 -0
- package/src/components/capability-plugin-schema.ts +56 -0
- package/src/components/capability-plugin.test.ts +235 -0
- package/src/components/capability-plugin.ts +108 -0
- package/src/components/capability.ts +117 -0
- package/src/components/cli-support.test.ts +579 -0
- package/src/components/cli-support.ts +481 -0
- package/src/components/component-schema.test.ts +265 -0
- package/src/components/component.schema.json +235 -0
- package/src/components/component.test.ts +251 -0
- package/src/components/component.ts +255 -0
- package/src/components/config-defaults.test.ts +263 -0
- package/src/components/config-defaults.ts +145 -0
- package/src/components/discover.test.ts +352 -0
- package/src/components/discover.ts +167 -0
- package/src/components/driver-output.ts +71 -0
- package/src/components/driver.test.ts +638 -0
- package/src/components/driver.ts +690 -0
- package/src/components/index.ts +113 -0
- package/src/components/manifest-persistence.test.ts +152 -0
- package/src/components/manifest-persistence.ts +164 -0
- package/src/components/pilots/README.md +33 -0
- package/src/components/pilots/alb-ecs.pilot.ts +63 -0
- package/src/components/pilots/dynamodb.pilot.ts +59 -0
- package/src/components/pilots/jar-emr.pilot.ts +69 -0
- package/src/components/pilots/lambda.pilot.ts +55 -0
- package/src/components/pilots/neo4j-fanout.pilot.ts +92 -0
- package/src/components/pilots/pilots-lint-clean.test.ts +78 -0
- package/src/components/pilots/pilots.test.ts +126 -0
- package/src/components/pilots/project.ts +13 -0
- package/src/components/presets/comp007-reduction.test.ts +122 -0
- package/src/components/presets/ecs-fargate.test.ts +84 -0
- package/src/components/presets/ecs-fargate.ts +103 -0
- package/src/components/presets/index.ts +32 -0
- package/src/components/presets/lambda.test.ts +35 -0
- package/src/components/presets/lambda.ts +53 -0
- package/src/components/presets/single-host-compose.test.ts +65 -0
- package/src/components/presets/single-host-compose.ts +64 -0
- package/src/components/registry.test.ts +112 -0
- package/src/components/registry.ts +37 -0
- package/src/components/starter-plugin.ts +105 -0
- package/src/components/temporal-codegen-loader.ts +41 -0
- package/src/components/verbs/__fixtures__/package-lock.json +45 -0
- package/src/components/verbs/__fixtures__/pom.xml +37 -0
- package/src/components/verbs/__fixtures__/schemas/cyclonedx-1.5.schema.json +3800 -0
- package/src/components/verbs/__fixtures__/schemas/cyclonedx-spdx-license.schema.json +621 -0
- package/src/components/verbs/__fixtures__/schemas/jsf-0.82.schema.json +240 -0
- package/src/components/verbs/__fixtures__/schemas/spdx-2.3.schema.json +740 -0
- package/src/components/verbs/__tests__/mock-cloud-executor.ts +102 -0
- package/src/components/verbs/__tests__/mock-process-runner.ts +79 -0
- package/src/components/verbs/__tests__/mock-sbom-generator.ts +85 -0
- package/src/components/verbs/bom-writer.test.ts +331 -0
- package/src/components/verbs/bom-writer.ts +386 -0
- package/src/components/verbs/build-archive.test.ts +251 -0
- package/src/components/verbs/build-archive.ts +296 -0
- package/src/components/verbs/build.test.ts +222 -0
- package/src/components/verbs/build.ts +252 -0
- package/src/components/verbs/cloud-executor.ts +181 -0
- package/src/components/verbs/component-bom.test.ts +219 -0
- package/src/components/verbs/component-bom.ts +189 -0
- package/src/components/verbs/index.ts +49 -0
- package/src/components/verbs/license-policy.ts +115 -0
- package/src/components/verbs/lockfile-sbom-generator.test.ts +195 -0
- package/src/components/verbs/lockfile-sbom-generator.ts +236 -0
- package/src/components/verbs/process-runner.test.ts +79 -0
- package/src/components/verbs/process-runner.ts +113 -0
- package/src/components/verbs/reproducibility.test.ts +24 -0
- package/src/components/verbs/reproducibility.ts +100 -0
- package/src/components/verbs/sbom-generator.test.ts +96 -0
- package/src/components/verbs/sbom-generator.ts +209 -0
- package/src/components/verbs/sbom.test.ts +184 -0
- package/src/components/verbs/sbom.ts +152 -0
- package/src/components/verbs/shell.test.ts +36 -0
- package/src/components/verbs/shell.ts +51 -0
- package/src/components/verbs/sign.test.ts +210 -0
- package/src/components/verbs/sign.ts +350 -0
- package/src/components/verbs/stub.ts +34 -0
- package/src/components/verbs/tool-sbom-generator.test.ts +194 -0
- package/src/components/verbs/tool-sbom-generator.ts +192 -0
- package/src/components/verbs/verify.test.ts +159 -0
- package/src/components/verbs/verify.ts +179 -0
- package/src/components/verbs/vex.ts +182 -0
- package/src/components/verbs/vuln-gate.test.ts +201 -0
- package/src/components/verbs/vuln-gate.ts +170 -0
- package/src/components/verbs/vuln-scan.test.ts +145 -0
- package/src/components/verbs/vuln-scan.ts +266 -0
- package/src/components/verbs/wait-verify.test.ts +111 -0
- package/src/components/verbs/wait-verify.ts +171 -0
- package/src/config.test.ts +74 -1
- package/src/config.ts +219 -0
- package/src/index.ts +32 -0
- package/src/lexicon.ts +88 -0
- package/src/lifecycle/build-ledger-store.test.ts +217 -0
- package/src/lifecycle/build-ledger-store.ts +165 -0
- package/src/lifecycle/build-ledger.test.ts +349 -0
- package/src/lifecycle/build-ledger.ts +305 -0
- package/src/lifecycle/git.test.ts +118 -0
- package/src/lifecycle/git.ts +149 -14
- package/src/lifecycle/index.ts +5 -0
- package/src/lifecycle/oras-referrer-lookup.test.ts +116 -0
- package/src/lifecycle/oras-referrer-lookup.ts +110 -0
- package/src/lifecycle/release-ledger.test.ts +209 -0
- package/src/lifecycle/release-ledger.ts +185 -0
- package/src/lifecycle/status.test.ts +330 -0
- package/src/lifecycle/status.ts +278 -0
- package/src/lint/component-checks.ts +148 -0
- package/src/lint/config.test.ts +21 -0
- package/src/lint/engine.ts +11 -2
- package/src/lint/presets/relaxed.json +8 -1
- package/src/lint/presets/strict.json +8 -1
- package/src/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.ts +9 -0
- package/src/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.ts +9 -0
- package/src/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.ts +11 -0
- package/src/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.ts +13 -0
- package/src/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.ts +11 -0
- package/src/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.ts +27 -0
- package/src/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.ts +10 -0
- package/src/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.ts +23 -0
- package/src/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.ts +10 -0
- package/src/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.ts +19 -0
- package/src/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.ts +10 -0
- package/src/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.ts +20 -0
- package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +23 -0
- package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +10 -0
- package/src/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.ts +14 -0
- package/src/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.ts +18 -0
- package/src/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.ts +10 -0
- package/src/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.ts +14 -0
- package/src/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.ts +13 -0
- package/src/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.ts +13 -0
- package/src/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.ts +12 -0
- package/src/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.ts +18 -0
- package/src/lint/rules/comp/comp.test.ts +378 -0
- package/src/lint/rules/comp/comp001-publishes-never-applies.ts +78 -0
- package/src/lint/rules/comp/comp002-dangling-wiring-ref.ts +94 -0
- package/src/lint/rules/comp/comp003-mutating-no-rollback.ts +115 -0
- package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +66 -0
- package/src/lint/rules/comp/comp005-capability-kind-is-noun.ts +113 -0
- package/src/lint/rules/comp/comp006-shell-needs-reason.ts +55 -0
- package/src/lint/rules/comp/comp007-composition-sprawl.ts +71 -0
- package/src/lint/rules/comp/index.ts +37 -0
- package/src/lint/rules/comp/support.ts +173 -0
- package/src/lsp/lexicon-providers.test.ts +55 -0
- package/src/lsp/lexicon-providers.ts +13 -3
- package/src/op/activity-registry.test.ts +38 -0
- package/src/op/activity-registry.ts +31 -12
- package/src/op/activity-runtime.ts +73 -0
- package/src/op/builders.ts +92 -0
- package/src/op/index.ts +3 -1
- package/src/ownership.test.ts +24 -26
- package/src/ownership.ts +26 -38
package/src/lexicon.ts
CHANGED
|
@@ -7,6 +7,8 @@ import type { TypeScriptGenerator } from "./import/generator";
|
|
|
7
7
|
import type { ArtifactIntegrity } from "./lexicon-integrity";
|
|
8
8
|
import type { CompletionContext, CompletionItem, HoverContext, HoverInfo, CodeActionContext, CodeAction } from "./lsp/types";
|
|
9
9
|
import type { McpToolContribution, McpResourceContribution } from "./mcp/types";
|
|
10
|
+
import type { DriverComponent } from "./components/driver";
|
|
11
|
+
import type { RuleMeta } from "./audit/catalog";
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Manifest for a packaged lexicon — metadata embedded in the tarball.
|
|
@@ -161,6 +163,68 @@ export interface InitTemplateSet {
|
|
|
161
163
|
* Required lifecycle methods enforce consistency: every lexicon must support
|
|
162
164
|
* generate, validate, coverage, and package operations.
|
|
163
165
|
*/
|
|
166
|
+
/**
|
|
167
|
+
* Where a lexicon pins an upstream schema/spec version, and where to look for a
|
|
168
|
+
* newer one — the lexicon's own declaration for the self-upgrade tooling
|
|
169
|
+
* (`chant dev pinned-upgrade`), so core never hard-codes per-lexicon upstream
|
|
170
|
+
* repos or version-constant locations. See ./codegen/pinned-upgrade.ts.
|
|
171
|
+
*/
|
|
172
|
+
export interface UpstreamPin {
|
|
173
|
+
/** Source file (relative to the lexicon package root) holding the pinned version constant. */
|
|
174
|
+
readonly file: string;
|
|
175
|
+
/** Regex whose first capture group is the current pinned version in `file`. */
|
|
176
|
+
readonly pattern: RegExp;
|
|
177
|
+
/** Rebuild a line that matched `pattern`, substituting the new version. */
|
|
178
|
+
replace(newVersion: string, line: string): string;
|
|
179
|
+
/** Where to query for the latest stable upstream tag. */
|
|
180
|
+
readonly upstream: {
|
|
181
|
+
readonly owner: string;
|
|
182
|
+
readonly repo: string;
|
|
183
|
+
/** `releases` = published releases; `tags` = every git tag. */
|
|
184
|
+
readonly kind: "releases" | "tags";
|
|
185
|
+
/** Only consider tags ending with this suffix (e.g. "-ee" for GitLab). */
|
|
186
|
+
readonly tagSuffix?: string;
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** One CI job in a generated pipeline (generate mode) — a thin trigger for one component in one wave. */
|
|
191
|
+
export interface ComponentPipelineJob {
|
|
192
|
+
/** CI job name (safe as a YAML key). */
|
|
193
|
+
jobName: string;
|
|
194
|
+
/** The component this job triggers. */
|
|
195
|
+
component: string;
|
|
196
|
+
/** The stage/wave this job runs in. */
|
|
197
|
+
stage: string;
|
|
198
|
+
/** Direct dependency job names this job waits on (mirrors the component's `dependsOn`). */
|
|
199
|
+
needs: string[];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Generic knobs for generate mode, applied once across every generated job (never per component). */
|
|
203
|
+
export interface ComponentPipelineOptions {
|
|
204
|
+
/** Target environment threaded into the trigger command. */
|
|
205
|
+
env?: string;
|
|
206
|
+
/** The trigger command per component, argv with `{name}` substituted. */
|
|
207
|
+
runCommand?: string[];
|
|
208
|
+
/** Extra script lines appended to every job. */
|
|
209
|
+
extraScript?: string[];
|
|
210
|
+
/** Script lines run before the trigger command in every job. */
|
|
211
|
+
beforeScript?: string[];
|
|
212
|
+
/** Container image every job runs in. */
|
|
213
|
+
image?: string;
|
|
214
|
+
/** Top-level CI `variables:` block. */
|
|
215
|
+
variables?: Record<string, string>;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** The synthesized CI pipeline for a component graph (generate mode). */
|
|
219
|
+
export interface ComponentPipelineResult {
|
|
220
|
+
/** The synthesized CI YAML (e.g. `.gitlab-ci.yml` content). */
|
|
221
|
+
yaml: string;
|
|
222
|
+
/** Wave-ordered stage names (matches the driver's graph waves 1:1). */
|
|
223
|
+
stages: string[];
|
|
224
|
+
/** Every generated job, in emit order. */
|
|
225
|
+
jobs: ComponentPipelineJob[];
|
|
226
|
+
}
|
|
227
|
+
|
|
164
228
|
export interface LexiconPlugin {
|
|
165
229
|
// ── Required ──────────────────────────────────────────────
|
|
166
230
|
/** Human-readable name (e.g. "aws", "gcp") */
|
|
@@ -191,6 +255,15 @@ export interface LexiconPlugin {
|
|
|
191
255
|
/** Return post-synthesis checks for build validation */
|
|
192
256
|
postSynthChecks?(): PostSynthCheck[];
|
|
193
257
|
|
|
258
|
+
/**
|
|
259
|
+
* Audit catalog metadata (title/tier/fix/authority/category) for this
|
|
260
|
+
* lexicon's `postSynthChecks`, keyed by check id — the per-provider half of
|
|
261
|
+
* `chant audit`'s rule catalog (#687, epic #350). Core aggregates these over
|
|
262
|
+
* its static catalog via `resolveAuditCatalog` (./audit/catalog.ts). Omit
|
|
263
|
+
* for lexicons whose rule metadata still lives in core's static catalog.
|
|
264
|
+
*/
|
|
265
|
+
auditCatalog?(): Record<string, RuleMeta>;
|
|
266
|
+
|
|
194
267
|
/** Return intrinsic function definitions */
|
|
195
268
|
intrinsics?(): IntrinsicDef[];
|
|
196
269
|
|
|
@@ -219,6 +292,21 @@ export interface LexiconPlugin {
|
|
|
219
292
|
/** Optional initialization hook */
|
|
220
293
|
init?(): void | Promise<void>;
|
|
221
294
|
|
|
295
|
+
/** How this lexicon pins its upstream schema version + where to check for a newer one (self-upgrade tooling — see ./codegen/pinned-upgrade.ts). Omit for lexicons with no pinned upstream. */
|
|
296
|
+
readonly upstreamPin?: UpstreamPin;
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Generate a CI pipeline from the discovered component graph — the
|
|
300
|
+
* `chant build --components --generate <name>` seam. Only CI-provider
|
|
301
|
+
* lexicons (gitlab, github, forgejo) implement this; core owns the generic
|
|
302
|
+
* driver (discovery + graph waves) and dispatches here. Omit for lexicons
|
|
303
|
+
* that are not CI providers.
|
|
304
|
+
*/
|
|
305
|
+
generateComponentPipeline?(
|
|
306
|
+
components: DriverComponent[],
|
|
307
|
+
options?: ComponentPipelineOptions,
|
|
308
|
+
): ComponentPipelineResult;
|
|
309
|
+
|
|
222
310
|
// LSP
|
|
223
311
|
/** Provide completions for LSP */
|
|
224
312
|
completionProvider?(ctx: CompletionContext): CompletionItem[];
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
import { withTestDir } from "@intentius/chant-test-utils";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { writeFileSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import {
|
|
7
|
+
persistBuildManifest,
|
|
8
|
+
readBuildManifest,
|
|
9
|
+
listBuildManifestDigests,
|
|
10
|
+
readAllBuildManifests,
|
|
11
|
+
findBuildManifestByArtifactDigest,
|
|
12
|
+
} from "./build-ledger-store";
|
|
13
|
+
import {
|
|
14
|
+
createBuildArchiveManifest,
|
|
15
|
+
addArchiveEntry,
|
|
16
|
+
type BuildArchiveManifest,
|
|
17
|
+
} from "../components/verbs/build-archive";
|
|
18
|
+
|
|
19
|
+
function git(args: string[], cwd: string): { stdout: string; exitCode: number } {
|
|
20
|
+
const r = spawnSync("git", args, { cwd, encoding: "utf-8" });
|
|
21
|
+
return { stdout: r.stdout ?? "", exitCode: r.status ?? -1 };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function initRepo(dir: string): Promise<void> {
|
|
25
|
+
git(["init", "-q", "-b", "main"], dir);
|
|
26
|
+
git(["config", "user.email", "test@chant.dev"], dir);
|
|
27
|
+
git(["config", "user.name", "Test"], dir);
|
|
28
|
+
writeFileSync(join(dir, "README.md"), "fixture\n");
|
|
29
|
+
git(["add", "README.md"], dir);
|
|
30
|
+
git(["commit", "-q", "-m", "init"], dir);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function makeManifest(component: string, imageDigest: string): BuildArchiveManifest {
|
|
34
|
+
let manifest = createBuildArchiveManifest(component, { now: () => new Date("2026-01-01T00:00:00.000Z") });
|
|
35
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: imageDigest });
|
|
36
|
+
manifest = addArchiveEntry(manifest, {
|
|
37
|
+
kind: "sbom",
|
|
38
|
+
path: "image.tar.sbom.json",
|
|
39
|
+
digest: "sha256:sbomdoc",
|
|
40
|
+
mediaType: "application/spdx+json",
|
|
41
|
+
subjectDigest: imageDigest,
|
|
42
|
+
packageCount: 12,
|
|
43
|
+
generator: "syft",
|
|
44
|
+
});
|
|
45
|
+
return manifest;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
describe("lifecycle/build-ledger-store", () => {
|
|
49
|
+
describe("persistBuildManifest / readBuildManifest", () => {
|
|
50
|
+
test("round-trips a manifest by its own manifestDigest", async () => {
|
|
51
|
+
await withTestDir(async (dir) => {
|
|
52
|
+
await initRepo(dir);
|
|
53
|
+
const manifest = makeManifest("search-service", "sha256:image1");
|
|
54
|
+
|
|
55
|
+
const { commit } = await persistBuildManifest(manifest, { cwd: dir });
|
|
56
|
+
expect(commit).toMatch(/^[0-9a-f]{40}$/);
|
|
57
|
+
|
|
58
|
+
const readBack = await readBuildManifest(manifest.manifestDigest, { cwd: dir });
|
|
59
|
+
expect(readBack).not.toBeNull();
|
|
60
|
+
expect(readBack).toEqual(manifest);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("preserves BOM entries and reproducibility/provenance across the round-trip", async () => {
|
|
65
|
+
await withTestDir(async (dir) => {
|
|
66
|
+
await initRepo(dir);
|
|
67
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
68
|
+
manifest = addArchiveEntry(manifest, {
|
|
69
|
+
kind: "image",
|
|
70
|
+
path: "image.tar",
|
|
71
|
+
digest: "sha256:image1",
|
|
72
|
+
provenance: { sourceRef: "deadbeef", artifactDigest: "sha256:image1" },
|
|
73
|
+
});
|
|
74
|
+
manifest = addArchiveEntry(manifest, {
|
|
75
|
+
kind: "sbom",
|
|
76
|
+
path: "image.tar.sbom.json",
|
|
77
|
+
digest: "sha256:sbomdoc",
|
|
78
|
+
mediaType: "application/spdx+json",
|
|
79
|
+
subjectDigest: "sha256:image1",
|
|
80
|
+
bomKind: "software",
|
|
81
|
+
packageCount: 42,
|
|
82
|
+
generator: "syft",
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
await persistBuildManifest(manifest, { cwd: dir });
|
|
86
|
+
const readBack = await readBuildManifest(manifest.manifestDigest, { cwd: dir });
|
|
87
|
+
|
|
88
|
+
expect(readBack?.contents.find((e) => e.kind === "image")?.reproducibility).toEqual({ basis: "best-effort" });
|
|
89
|
+
expect(readBack?.contents.find((e) => e.kind === "image")?.provenance).toEqual({
|
|
90
|
+
sourceRef: "deadbeef",
|
|
91
|
+
artifactDigest: "sha256:image1",
|
|
92
|
+
});
|
|
93
|
+
const sbomEntry = readBack?.contents.find((e) => e.kind === "sbom");
|
|
94
|
+
expect(sbomEntry?.packageCount).toBe(42);
|
|
95
|
+
expect(sbomEntry?.generator).toBe("syft");
|
|
96
|
+
expect(sbomEntry?.bomKind).toBe("software");
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("readBuildManifest returns null for an unknown digest", async () => {
|
|
101
|
+
await withTestDir(async (dir) => {
|
|
102
|
+
await initRepo(dir);
|
|
103
|
+
expect(await readBuildManifest("sha256:doesnotexist", { cwd: dir })).toBeNull();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("persisting the same manifest twice overwrites rather than duplicating", async () => {
|
|
108
|
+
await withTestDir(async (dir) => {
|
|
109
|
+
await initRepo(dir);
|
|
110
|
+
const manifest = makeManifest("svc", "sha256:image1");
|
|
111
|
+
await persistBuildManifest(manifest, { cwd: dir });
|
|
112
|
+
await persistBuildManifest(manifest, { cwd: dir });
|
|
113
|
+
|
|
114
|
+
const digests = await listBuildManifestDigests({ cwd: dir });
|
|
115
|
+
expect(digests).toHaveLength(1);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("release ledger and build manifest store coexist on the same orphan branch", async () => {
|
|
120
|
+
const { appendReleaseRecord, readReleaseLedger } = await import("./release-ledger");
|
|
121
|
+
await withTestDir(async (dir) => {
|
|
122
|
+
await initRepo(dir);
|
|
123
|
+
const manifest = makeManifest("svc", "sha256:image1");
|
|
124
|
+
await persistBuildManifest(manifest, { cwd: dir });
|
|
125
|
+
await appendReleaseRecord(
|
|
126
|
+
{
|
|
127
|
+
component: "svc",
|
|
128
|
+
env: "prod",
|
|
129
|
+
digest: "sha256:image1",
|
|
130
|
+
gitSha: "deadbeef",
|
|
131
|
+
runId: "run-1",
|
|
132
|
+
timestamp: "2026-01-01T00:00:00.000Z",
|
|
133
|
+
actor: "ci-bot",
|
|
134
|
+
},
|
|
135
|
+
{ cwd: dir },
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
expect(await readBuildManifest(manifest.manifestDigest, { cwd: dir })).not.toBeNull();
|
|
139
|
+
const { records } = await readReleaseLedger("prod", { cwd: dir });
|
|
140
|
+
expect(records).toHaveLength(1);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe("listBuildManifestDigests / readAllBuildManifests", () => {
|
|
146
|
+
test("returns empty when no manifest has ever been persisted", async () => {
|
|
147
|
+
await withTestDir(async (dir) => {
|
|
148
|
+
await initRepo(dir);
|
|
149
|
+
expect(await listBuildManifestDigests({ cwd: dir })).toEqual([]);
|
|
150
|
+
expect(await readAllBuildManifests({ cwd: dir })).toEqual([]);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("lists every persisted manifest's own digest", async () => {
|
|
155
|
+
await withTestDir(async (dir) => {
|
|
156
|
+
await initRepo(dir);
|
|
157
|
+
const a = makeManifest("svc-a", "sha256:a1");
|
|
158
|
+
const b = makeManifest("svc-b", "sha256:b1");
|
|
159
|
+
await persistBuildManifest(a, { cwd: dir });
|
|
160
|
+
await persistBuildManifest(b, { cwd: dir });
|
|
161
|
+
|
|
162
|
+
const digests = await listBuildManifestDigests({ cwd: dir });
|
|
163
|
+
expect(digests.sort()).toEqual([a.manifestDigest, b.manifestDigest].sort());
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("readAllBuildManifests reads every persisted manifest back", async () => {
|
|
168
|
+
await withTestDir(async (dir) => {
|
|
169
|
+
await initRepo(dir);
|
|
170
|
+
const a = makeManifest("svc-a", "sha256:a1");
|
|
171
|
+
const b = makeManifest("svc-b", "sha256:b1");
|
|
172
|
+
await persistBuildManifest(a, { cwd: dir });
|
|
173
|
+
await persistBuildManifest(b, { cwd: dir });
|
|
174
|
+
|
|
175
|
+
const manifests = await readAllBuildManifests({ cwd: dir });
|
|
176
|
+
expect(manifests.map((m) => m.component).sort()).toEqual(["svc-a", "svc-b"]);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
describe("findBuildManifestByArtifactDigest", () => {
|
|
182
|
+
test("finds the manifest carrying a matching image-entry digest", async () => {
|
|
183
|
+
await withTestDir(async (dir) => {
|
|
184
|
+
await initRepo(dir);
|
|
185
|
+
const manifest = makeManifest("search-service", "sha256:image1");
|
|
186
|
+
await persistBuildManifest(manifest, { cwd: dir });
|
|
187
|
+
|
|
188
|
+
const found = await findBuildManifestByArtifactDigest("sha256:image1", { cwd: dir });
|
|
189
|
+
expect(found?.manifestDigest).toBe(manifest.manifestDigest);
|
|
190
|
+
expect(found?.component).toBe("search-service");
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("returns undefined when no persisted manifest carries the digest", async () => {
|
|
195
|
+
await withTestDir(async (dir) => {
|
|
196
|
+
await initRepo(dir);
|
|
197
|
+
const manifest = makeManifest("svc", "sha256:image1");
|
|
198
|
+
await persistBuildManifest(manifest, { cwd: dir });
|
|
199
|
+
|
|
200
|
+
expect(await findBuildManifestByArtifactDigest("sha256:unknown", { cwd: dir })).toBeUndefined();
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("distinguishes between multiple persisted manifests by digest", async () => {
|
|
205
|
+
await withTestDir(async (dir) => {
|
|
206
|
+
await initRepo(dir);
|
|
207
|
+
const a = makeManifest("svc-a", "sha256:a1");
|
|
208
|
+
const b = makeManifest("svc-b", "sha256:b1");
|
|
209
|
+
await persistBuildManifest(a, { cwd: dir });
|
|
210
|
+
await persistBuildManifest(b, { cwd: dir });
|
|
211
|
+
|
|
212
|
+
expect((await findBuildManifestByArtifactDigest("sha256:a1", { cwd: dir }))?.component).toBe("svc-a");
|
|
213
|
+
expect((await findBuildManifestByArtifactDigest("sha256:b1", { cwd: dir }))?.component).toBe("svc-b");
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
});
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build-archive manifest store (#609, epic #551 follow-up to #564/#568/#606/
|
|
3
|
+
* #613/#614): persists a `BuildArchiveManifest`
|
|
4
|
+
* (../components/verbs/build-archive.ts) to a durable, content-addressed
|
|
5
|
+
* location so it survives past the in-process build/run that assembled it.
|
|
6
|
+
*
|
|
7
|
+
* **The gap this closes.** `docker-build` (../components/verbs/build.ts) and
|
|
8
|
+
* friends assemble a real `BuildArchiveManifest` in memory — complete with
|
|
9
|
+
* `sbom`/config-BOM entries (#606/#613) and per-artifact
|
|
10
|
+
* `reproducibility`/`provenance` (#614) — but nothing ever wrote it anywhere
|
|
11
|
+
* durable. `build-ledger.ts`'s `buildLedgerEntries`/`componentBomSummary` are
|
|
12
|
+
* unit-tested against a manifest handed to them directly, but
|
|
13
|
+
* `chant components status` (../cli/handlers/components.ts) had no manifest
|
|
14
|
+
* to hand them at all: `componentBom` and `build.reproducibility` were always
|
|
15
|
+
* `null` in that CLI path. This module is the missing durable layer — write
|
|
16
|
+
* once at build time, read back by digest at status time.
|
|
17
|
+
*
|
|
18
|
+
* **Storage choice: the `chant/lifecycle` orphan branch, a sibling namespace
|
|
19
|
+
* to the release ledger.** Reuses the exact git-plumbing
|
|
20
|
+
* `writeBlobToPath`/`readBlobFromPath`/`listFilesInDir` (./git.ts) that
|
|
21
|
+
* `writeSnapshot`/`appendReleaseRecordLine` already use — same hash-object →
|
|
22
|
+
* mktree → commit-tree → update-ref pipeline, same `--force-with-lease` push
|
|
23
|
+
* discipline (`pushLifecycle`), same "no checkout, no working tree changes"
|
|
24
|
+
* property. Rejected alternatives:
|
|
25
|
+
* - **A new on-disk directory in the working tree** — would need its own
|
|
26
|
+
* persistence/sync story (commit it? gitignore it? sync across CI
|
|
27
|
+
* runners?) that the orphan branch already solves for the release ledger;
|
|
28
|
+
* introducing a second mechanism for a manifest that is conceptually the
|
|
29
|
+
* same kind of durable, git-native record would be needless divergence.
|
|
30
|
+
* - **A path under `<env>/`** — a build archive is *not* env-scoped. The
|
|
31
|
+
* whole point of promote-by-digest (epic #551 "4. Build archive +
|
|
32
|
+
* deferred publish") is that one build's manifest is referenced from
|
|
33
|
+
* every environment it gets promoted to; writing it under a single env
|
|
34
|
+
* directory would either duplicate it per env (defeating "byte-identical
|
|
35
|
+
* across envs") or arbitrarily pick one env as "the" owner. Manifests
|
|
36
|
+
* instead live under a fixed top-level `_builds/` directory on the same
|
|
37
|
+
* orphan branch, keyed by `manifestDigest` — a peer of the per-env
|
|
38
|
+
* directories `writeSnapshot`/the release ledger already create there,
|
|
39
|
+
* never nested inside one.
|
|
40
|
+
*
|
|
41
|
+
* **Content-addressed, keyed by `manifestDigest`.** One file per manifest,
|
|
42
|
+
* `_builds/<manifestDigest-with-":"->"_">.json`, so a rebuild of
|
|
43
|
+
* byte-identical inputs (same `manifestDigest`, per
|
|
44
|
+
* ../components/verbs/build-archive.ts's module doc) overwrites its own
|
|
45
|
+
* entry with identical content rather than accumulating duplicates —
|
|
46
|
+
* `writeBlobToPath` already replaces-by-path, so this falls out for free.
|
|
47
|
+
* `findBuildManifestByArtifactDigest` additionally resolves "the manifest
|
|
48
|
+
* that produced artifact digest X" (the join key `chant components status`
|
|
49
|
+
* actually has on hand — a `ReleaseRecord.digest` is an *artifact* digest,
|
|
50
|
+
* not a manifest digest) by scanning every stored manifest's `contents` for
|
|
51
|
+
* a matching entry. This is a linear scan over however many manifests have
|
|
52
|
+
* been persisted; fine for the ledger's expected scale (one manifest per
|
|
53
|
+
* build), and avoids maintaining a second reverse-index file that could get
|
|
54
|
+
* out of sync with the manifests themselves.
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
import { sortedJsonReplacer } from "../utils";
|
|
58
|
+
import { writeBlobToPath, readBlobFromPath, listFilesInDir } from "./git";
|
|
59
|
+
import type { BuildArchiveManifest } from "../components/verbs/build-archive";
|
|
60
|
+
|
|
61
|
+
/** Fixed top-level directory on the `chant/lifecycle` orphan branch that holds every persisted build manifest — a peer of the per-env directories, never nested inside one (see module doc for why). */
|
|
62
|
+
const BUILDS_DIR = "_builds";
|
|
63
|
+
|
|
64
|
+
/** Turn a `sha256:...`-style digest into a filesystem/git-tree-safe filename stem (`:` is not usable in a git tree entry name the way this plumbing constructs it). */
|
|
65
|
+
function digestToFilenameStem(digest: string): string {
|
|
66
|
+
return digest.replace(/:/g, "_");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function manifestFilename(manifestDigest: string): string {
|
|
70
|
+
return `${digestToFilenameStem(manifestDigest)}.json`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Persist a `BuildArchiveManifest` to the orphan branch, keyed by its own
|
|
75
|
+
* `manifestDigest`. Does not push to the remote — call `pushLifecycle`
|
|
76
|
+
* (./git.ts) afterward, the same two-step (`write` then `push`) shape
|
|
77
|
+
* `appendReleaseRecord`/`takeSnapshot` use, so a caller persisting several
|
|
78
|
+
* manifests (or a manifest plus a release record) in one run can batch the
|
|
79
|
+
* push into one network round-trip.
|
|
80
|
+
*
|
|
81
|
+
* Idempotent: persisting the same manifest twice (identical `manifestDigest`)
|
|
82
|
+
* overwrites the same path with identical bytes — safe to call more than
|
|
83
|
+
* once for the same build.
|
|
84
|
+
*/
|
|
85
|
+
export async function persistBuildManifest(
|
|
86
|
+
manifest: BuildArchiveManifest,
|
|
87
|
+
opts?: { cwd?: string },
|
|
88
|
+
): Promise<{ commit: string }> {
|
|
89
|
+
const json = JSON.stringify(manifest, sortedJsonReplacer);
|
|
90
|
+
const commit = await writeBlobToPath(BUILDS_DIR, manifestFilename(manifest.manifestDigest), json, "Build manifest", opts);
|
|
91
|
+
return { commit };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Read a persisted `BuildArchiveManifest` back by its own `manifestDigest`.
|
|
96
|
+
* Returns `null` when no manifest was ever persisted under that digest
|
|
97
|
+
* (never throws — a status query for a digest with no recorded build is a
|
|
98
|
+
* normal, expected state, matching `readSnapshot`'s null-on-missing
|
|
99
|
+
* convention).
|
|
100
|
+
*/
|
|
101
|
+
export async function readBuildManifest(
|
|
102
|
+
manifestDigest: string,
|
|
103
|
+
opts?: { cwd?: string },
|
|
104
|
+
): Promise<BuildArchiveManifest | null> {
|
|
105
|
+
const content = await readBlobFromPath(BUILDS_DIR, manifestFilename(manifestDigest), opts);
|
|
106
|
+
if (!content) return null;
|
|
107
|
+
try {
|
|
108
|
+
return JSON.parse(content) as BuildArchiveManifest;
|
|
109
|
+
} catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** List the `manifestDigest` of every build manifest persisted on the orphan branch. */
|
|
115
|
+
export async function listBuildManifestDigests(opts?: { cwd?: string }): Promise<string[]> {
|
|
116
|
+
const files = await listFilesInDir(BUILDS_DIR, opts);
|
|
117
|
+
return files
|
|
118
|
+
.filter((f) => f.endsWith(".json"))
|
|
119
|
+
.map((f) => f.replace(/\.json$/, "").replace(/_/g, ":"));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Read every persisted build manifest. Best-effort: a manifest file that
|
|
124
|
+
* fails to parse is skipped rather than failing the whole read (mirrors
|
|
125
|
+
* `readReleaseLedger`'s "skip malformed lines" degrade-gracefully
|
|
126
|
+
* convention) — the caller only gets the count implicitly via array length,
|
|
127
|
+
* since a corrupted manifest blob is a much rarer/more surprising event than
|
|
128
|
+
* a hand-edited ledger line and not expected to need the same "surfaced
|
|
129
|
+
* count" treatment.
|
|
130
|
+
*/
|
|
131
|
+
export async function readAllBuildManifests(opts?: { cwd?: string }): Promise<BuildArchiveManifest[]> {
|
|
132
|
+
const digests = await listBuildManifestDigests(opts);
|
|
133
|
+
const manifests: BuildArchiveManifest[] = [];
|
|
134
|
+
for (const digest of digests) {
|
|
135
|
+
const manifest = await readBuildManifest(digest, opts);
|
|
136
|
+
if (manifest) manifests.push(manifest);
|
|
137
|
+
}
|
|
138
|
+
return manifests;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Find the persisted manifest that carries an entry (any kind — image,
|
|
143
|
+
* template, asset, or sbom) whose `digest` equals `artifactDigest`. This is
|
|
144
|
+
* the lookup `chant components status` actually needs: a `ReleaseRecord`
|
|
145
|
+
* (../lifecycle/release-ledger.ts) records the *artifact* digest that was
|
|
146
|
+
* promoted/deployed, not the manifest's own aggregate `manifestDigest` — so
|
|
147
|
+
* resolving "the build behind this deployed digest" means searching
|
|
148
|
+
* manifest contents, not a direct keyed lookup. Returns `undefined` when no
|
|
149
|
+
* persisted manifest contains a matching entry (no build was ever persisted
|
|
150
|
+
* for that artifact digest — e.g. it predates #609, or was recorded via the
|
|
151
|
+
* standalone `chant components release` command with no corresponding
|
|
152
|
+
* `chant build`/`run` in this checkout).
|
|
153
|
+
*
|
|
154
|
+
* A `ReleaseRecord.manifestDigest`, when present (an optional field a caller
|
|
155
|
+
* may have supplied at record time), lets a caller skip this scan entirely
|
|
156
|
+
* via `readBuildManifest` directly — this function is the fallback for the
|
|
157
|
+
* common case where only the artifact digest is known.
|
|
158
|
+
*/
|
|
159
|
+
export async function findBuildManifestByArtifactDigest(
|
|
160
|
+
artifactDigest: string,
|
|
161
|
+
opts?: { cwd?: string },
|
|
162
|
+
): Promise<BuildArchiveManifest | undefined> {
|
|
163
|
+
const manifests = await readAllBuildManifests(opts);
|
|
164
|
+
return manifests.find((m) => m.contents.some((entry) => entry.digest === artifactDigest));
|
|
165
|
+
}
|