@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
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COMP* composition lint rule tests (#562, epic #551).
|
|
3
|
+
*
|
|
4
|
+
* Each rule gets one passing and one failing fixture directory under
|
|
5
|
+
* ../__fixtures__/comp/<ruleId>/{pass,fail}/ — real `*.component.ts` files
|
|
6
|
+
* discovered the same way `chant lint` discovers them (via
|
|
7
|
+
* ../../component-checks.ts's `runComponentChecks`, itself
|
|
8
|
+
* `../../../components/discover.ts`'s `discoverComponents` plus every
|
|
9
|
+
* `ComponentCheck`). Each fixture directory is its own tiny "project" so
|
|
10
|
+
* cross-component rules (COMP002, COMP007) only see the components meant for
|
|
11
|
+
* that specific case.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { describe, expect, it } from "vitest";
|
|
16
|
+
import { runComponentChecks } from "../../component-checks";
|
|
17
|
+
import type { RollbackPolicy } from "../../../components/capability";
|
|
18
|
+
import { loadComponentChecks } from "./index";
|
|
19
|
+
|
|
20
|
+
const FIXTURES_DIR = join(import.meta.dirname, "..", "__fixtures__", "comp");
|
|
21
|
+
const checks = loadComponentChecks();
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Rollback dispositions the COMP003 fixtures reference, standing in for what
|
|
25
|
+
* `chant lint` derives from the project's registry (`cfn-deploy` has a native
|
|
26
|
+
* `rollback`; `run-migration` declares `needs-opt-out` in the aws lexicon).
|
|
27
|
+
*/
|
|
28
|
+
const FIXTURE_ROLLBACK_POLICIES = new Map<string, RollbackPolicy>([
|
|
29
|
+
["cfn-deploy", "native"],
|
|
30
|
+
["run-migration", "needs-opt-out"],
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
async function lintFixture(
|
|
34
|
+
ruleId: string,
|
|
35
|
+
kind: "pass" | "fail",
|
|
36
|
+
registryContext?: { knownKinds?: ReadonlySet<string>; rollbackPolicies?: ReadonlyMap<string, RollbackPolicy> },
|
|
37
|
+
) {
|
|
38
|
+
const dir = join(FIXTURES_DIR, ruleId.toLowerCase(), kind);
|
|
39
|
+
return runComponentChecks(dir, checks, registryContext);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe("COMP000: component discovery errors", () => {
|
|
43
|
+
it("surfaces a duplicate component name as a COMP000 diagnostic instead of silently dropping it", async () => {
|
|
44
|
+
const dir = join(FIXTURES_DIR, "comp000-discovery-error");
|
|
45
|
+
const diagnostics = await runComponentChecks(dir, checks);
|
|
46
|
+
const hits = diagnostics.filter((d) => d.checkId === "COMP000");
|
|
47
|
+
expect(hits.length).toBeGreaterThan(0);
|
|
48
|
+
expect(hits[0].severity).toBe("error");
|
|
49
|
+
expect(hits[0].message).toContain("Duplicate component name");
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe("loadComponentChecks()", () => {
|
|
54
|
+
it("returns all seven COMP* checks, sorted by id, each error or warning severity", () => {
|
|
55
|
+
expect(checks.map((c) => c.id)).toEqual([
|
|
56
|
+
"COMP001",
|
|
57
|
+
"COMP002",
|
|
58
|
+
"COMP003",
|
|
59
|
+
"COMP004",
|
|
60
|
+
"COMP005",
|
|
61
|
+
"COMP006",
|
|
62
|
+
"COMP007",
|
|
63
|
+
]);
|
|
64
|
+
for (const check of checks) {
|
|
65
|
+
expect(["error", "warning"]).toContain(check.severity);
|
|
66
|
+
expect(typeof check.description).toBe("string");
|
|
67
|
+
expect(check.description.length).toBeGreaterThan(0);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("COMP001: publishes-but-never-applies", () => {
|
|
73
|
+
it("flags a publish step whose output nothing ever consumes", async () => {
|
|
74
|
+
const diagnostics = await lintFixture("comp001", "fail");
|
|
75
|
+
const hits = diagnostics.filter((d) => d.checkId === "COMP001");
|
|
76
|
+
expect(hits).toHaveLength(1);
|
|
77
|
+
expect(hits[0].component).toBe("orphan-lib");
|
|
78
|
+
expect(hits[0].message).toContain("never consumed");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("does not flag a publish step consumed by a downstream component", async () => {
|
|
82
|
+
const diagnostics = await lintFixture("comp001", "pass");
|
|
83
|
+
expect(diagnostics.filter((d) => d.checkId === "COMP001")).toHaveLength(0);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe("COMP002: dangling-wiring-ref", () => {
|
|
88
|
+
it("flags a @Phase.field reference to a nonexistent phase and a @component.publish.* reference to an undiscovered component", async () => {
|
|
89
|
+
const diagnostics = await lintFixture("comp002", "fail");
|
|
90
|
+
const hits = diagnostics.filter((d) => d.checkId === "COMP002");
|
|
91
|
+
expect(hits.length).toBeGreaterThanOrEqual(2);
|
|
92
|
+
expect(hits.some((d) => d.message.includes('"@Build.digest"'))).toBe(true);
|
|
93
|
+
expect(hits.some((d) => d.message.includes('"@missing-lib.publish.uri"'))).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("does not flag references that resolve to a real phase/dependency", async () => {
|
|
97
|
+
const diagnostics = await lintFixture("comp002", "pass");
|
|
98
|
+
expect(diagnostics.filter((d) => d.checkId === "COMP002")).toHaveLength(0);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("flags a cross-component reference to a component missing from dependsOn", async () => {
|
|
102
|
+
// Reuses the pass fixture's components but confirms the dependsOn check
|
|
103
|
+
// specifically by asserting the pass fixture's searchService (which DOES
|
|
104
|
+
// list jar-lib in dependsOn) produces no COMP002 hit for that reference.
|
|
105
|
+
const diagnostics = await lintFixture("comp002", "pass");
|
|
106
|
+
const jarRefHits = diagnostics.filter(
|
|
107
|
+
(d) => d.checkId === "COMP002" && d.message.includes("jar-lib"),
|
|
108
|
+
);
|
|
109
|
+
expect(jarRefHits).toHaveLength(0);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe("COMP003: mutating-no-rollback", () => {
|
|
114
|
+
it("flags a mutating step (run-migration) with no native rollback and no opt-out", async () => {
|
|
115
|
+
const diagnostics = await lintFixture("comp003", "fail", { rollbackPolicies: FIXTURE_ROLLBACK_POLICIES });
|
|
116
|
+
const hits = diagnostics.filter((d) => d.checkId === "COMP003");
|
|
117
|
+
expect(hits).toHaveLength(1);
|
|
118
|
+
expect(hits[0].component).toBe("migrator");
|
|
119
|
+
expect(hits[0].message).toContain("run-migration");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("does not flag a step with a native rollback (cfn-deploy) or an explicit noRollback opt-out", async () => {
|
|
123
|
+
const diagnostics = await lintFixture("comp003", "pass", { rollbackPolicies: FIXTURE_ROLLBACK_POLICIES });
|
|
124
|
+
expect(diagnostics.filter((d) => d.checkId === "COMP003")).toHaveLength(0);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("flags a needs-opt-out step nested inside a fan-out phase, not just top-level phases", () => {
|
|
128
|
+
// Regression test: the rule used to iterate component.deploy directly and
|
|
129
|
+
// never recursed into nested Phase entries (a fan-out unit, e.g. the
|
|
130
|
+
// Neo4j pilot's per-instance phases), so a mutating step buried inside one
|
|
131
|
+
// was silently skipped. It must now use the same walkComponent traversal
|
|
132
|
+
// every other COMP rule uses.
|
|
133
|
+
const [comp003] = checks.filter((c) => c.id === "COMP003");
|
|
134
|
+
const ctx = {
|
|
135
|
+
rollbackPolicies: FIXTURE_ROLLBACK_POLICIES,
|
|
136
|
+
components: new Map([
|
|
137
|
+
[
|
|
138
|
+
"fanout-cluster",
|
|
139
|
+
{
|
|
140
|
+
component: {
|
|
141
|
+
name: "fanout-cluster",
|
|
142
|
+
dependsOn: [],
|
|
143
|
+
deploy: [
|
|
144
|
+
{
|
|
145
|
+
phase: "Rolling",
|
|
146
|
+
steps: [
|
|
147
|
+
{
|
|
148
|
+
phase: "Node 1",
|
|
149
|
+
steps: [{ kind: "run-migration", tool: "flyway", target: "node-1" }],
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
filePath: "fanout-cluster.component.ts",
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
]),
|
|
159
|
+
};
|
|
160
|
+
const diagnostics = comp003.check(ctx as never);
|
|
161
|
+
expect(diagnostics).toHaveLength(1);
|
|
162
|
+
expect(diagnostics[0].component).toBe("fanout-cluster");
|
|
163
|
+
expect(diagnostics[0].message).toContain("run-migration");
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("a compensation sibling only counts within the same nested phase, not a same-named sibling phase elsewhere in the component", () => {
|
|
167
|
+
// Two different "Node" phases both contain a run-migration step; only the
|
|
168
|
+
// second one has a rollback-previous sibling. The first must still be
|
|
169
|
+
// flagged — object identity, not phase-name string, decides "same phase".
|
|
170
|
+
const [comp003] = checks.filter((c) => c.id === "COMP003");
|
|
171
|
+
const ctx = {
|
|
172
|
+
rollbackPolicies: FIXTURE_ROLLBACK_POLICIES,
|
|
173
|
+
components: new Map([
|
|
174
|
+
[
|
|
175
|
+
"fanout-cluster",
|
|
176
|
+
{
|
|
177
|
+
component: {
|
|
178
|
+
name: "fanout-cluster",
|
|
179
|
+
dependsOn: [],
|
|
180
|
+
deploy: [
|
|
181
|
+
{ phase: "Node A", steps: [{ kind: "run-migration", tool: "flyway", target: "a" }] },
|
|
182
|
+
{
|
|
183
|
+
phase: "Node B",
|
|
184
|
+
steps: [
|
|
185
|
+
{ kind: "run-migration", tool: "flyway", target: "b" },
|
|
186
|
+
{ kind: "rollback-previous", resource: "b" },
|
|
187
|
+
],
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
},
|
|
191
|
+
filePath: "fanout-cluster.component.ts",
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
]),
|
|
195
|
+
};
|
|
196
|
+
const diagnostics = comp003.check(ctx as never);
|
|
197
|
+
expect(diagnostics).toHaveLength(1);
|
|
198
|
+
expect(diagnostics[0].message).toContain('phase "Node A"');
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
describe("COMP004: gate-needs-temporal", () => {
|
|
203
|
+
it("flags a gate step with no disable directive", async () => {
|
|
204
|
+
const diagnostics = await lintFixture("comp004", "fail");
|
|
205
|
+
const hits = diagnostics.filter((d) => d.checkId === "COMP004");
|
|
206
|
+
expect(hits).toHaveLength(1);
|
|
207
|
+
expect(hits[0].component).toBe("neo4j-cluster");
|
|
208
|
+
expect(hits[0].message).toContain("gate");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("does not flag a component with no gate steps", async () => {
|
|
212
|
+
const diagnostics = await lintFixture("comp004", "pass");
|
|
213
|
+
const hits = diagnostics.filter((d) => d.checkId === "COMP004" && d.component !== "neo4j-cluster");
|
|
214
|
+
expect(hits).toHaveLength(0);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("still raises the raw diagnostic for a gate with a file-level disable directive — runComponentChecks does not itself apply disable-directive filtering (that is the chant lint CLI layer's job, see ../../../cli/commands/component-lint.test.ts)", async () => {
|
|
218
|
+
const diagnostics = await lintFixture("comp004", "pass");
|
|
219
|
+
const hits = diagnostics.filter((d) => d.checkId === "COMP004" && d.component === "neo4j-cluster");
|
|
220
|
+
expect(hits).toHaveLength(1);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe("COMP005: capability-kind-is-noun", () => {
|
|
225
|
+
it("flags a step kind named after the component itself", async () => {
|
|
226
|
+
const diagnostics = await lintFixture("comp005", "fail");
|
|
227
|
+
const hits = diagnostics.filter((d) => d.checkId === "COMP005");
|
|
228
|
+
expect(hits).toHaveLength(1);
|
|
229
|
+
expect(hits[0].message).toContain("deploy-search-service");
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("does not flag verb-named capability kinds", async () => {
|
|
233
|
+
const diagnostics = await lintFixture("comp005", "pass");
|
|
234
|
+
expect(diagnostics.filter((d) => d.checkId === "COMP005")).toHaveLength(0);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("regression: never flags a registered verb, even when a component's name is a substring of it (e.g. a component named \"stack\")", () => {
|
|
238
|
+
// A component literally named "stack", "job", "image", or "host" must not
|
|
239
|
+
// falsely flag wait-for-stack / emr-start-job-run / publish-image /
|
|
240
|
+
// load-image-on-host by dash-segment substring collision. `ctx.knownKinds`
|
|
241
|
+
// is what `chant lint` passes for an aws-active project — the registry's
|
|
242
|
+
// resolved kinds, which include these lexicon leaves.
|
|
243
|
+
const [comp005] = checks.filter((c) => c.id === "COMP005");
|
|
244
|
+
const knownKinds = new Set([
|
|
245
|
+
"docker-build",
|
|
246
|
+
"wait-for-stack",
|
|
247
|
+
"emr-start-job-run",
|
|
248
|
+
"publish-image",
|
|
249
|
+
"load-image-on-host",
|
|
250
|
+
]);
|
|
251
|
+
const ctx = {
|
|
252
|
+
knownKinds,
|
|
253
|
+
components: new Map([
|
|
254
|
+
[
|
|
255
|
+
"stack",
|
|
256
|
+
{
|
|
257
|
+
component: {
|
|
258
|
+
name: "stack",
|
|
259
|
+
dependsOn: [],
|
|
260
|
+
deploy: [{ phase: "Verify", steps: [{ kind: "wait-for-stack", stack: "stack" }] }],
|
|
261
|
+
},
|
|
262
|
+
filePath: "stack.component.ts",
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
[
|
|
266
|
+
"job",
|
|
267
|
+
{
|
|
268
|
+
component: {
|
|
269
|
+
name: "job",
|
|
270
|
+
dependsOn: [],
|
|
271
|
+
deploy: [{ phase: "Submit", steps: [{ kind: "emr-start-job-run", jar: "x" }] }],
|
|
272
|
+
},
|
|
273
|
+
filePath: "job.component.ts",
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
[
|
|
277
|
+
"image",
|
|
278
|
+
{
|
|
279
|
+
component: {
|
|
280
|
+
name: "image",
|
|
281
|
+
dependsOn: [],
|
|
282
|
+
build: { kind: "docker-build" },
|
|
283
|
+
deploy: [{ phase: "Publish", steps: [{ kind: "publish-image", from: "a", to: "b" }] }],
|
|
284
|
+
},
|
|
285
|
+
filePath: "image.component.ts",
|
|
286
|
+
},
|
|
287
|
+
],
|
|
288
|
+
[
|
|
289
|
+
"host",
|
|
290
|
+
{
|
|
291
|
+
component: {
|
|
292
|
+
name: "host",
|
|
293
|
+
dependsOn: [],
|
|
294
|
+
deploy: [{ phase: "Publish", steps: [{ kind: "load-image-on-host", from: "a", host: "b" }] }],
|
|
295
|
+
},
|
|
296
|
+
filePath: "host.component.ts",
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
]),
|
|
300
|
+
};
|
|
301
|
+
expect(comp005.check(ctx as never)).toEqual([]);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it("falls back to the core starter set when no registry (knownKinds) was resolved — a core verb whose name overlaps a component is still not flagged", () => {
|
|
305
|
+
const [comp005] = checks.filter((c) => c.id === "COMP005");
|
|
306
|
+
const ctx = {
|
|
307
|
+
// no knownKinds -> fall back to core starter (includes wait-cluster-healthy)
|
|
308
|
+
components: new Map([
|
|
309
|
+
[
|
|
310
|
+
"cluster",
|
|
311
|
+
{
|
|
312
|
+
component: {
|
|
313
|
+
name: "cluster",
|
|
314
|
+
dependsOn: [],
|
|
315
|
+
deploy: [{ phase: "Verify", steps: [{ kind: "wait-cluster-healthy", size: 1 }] }],
|
|
316
|
+
},
|
|
317
|
+
filePath: "cluster.component.ts",
|
|
318
|
+
},
|
|
319
|
+
],
|
|
320
|
+
]),
|
|
321
|
+
};
|
|
322
|
+
expect(comp005.check(ctx as never)).toEqual([]);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it("without a resolved registry, an active-lexicon verb it can't know about IS flagged when it collides with a component name (registry-driven, not hard-coded)", () => {
|
|
326
|
+
const [comp005] = checks.filter((c) => c.id === "COMP005");
|
|
327
|
+
const ctx = {
|
|
328
|
+
// no knownKinds -> `wait-for-stack` is not a core verb, so for a component
|
|
329
|
+
// literally named "stack" it reads as a noun-collision. A real `chant lint`
|
|
330
|
+
// run passes knownKinds and avoids this; this documents the fallback edge.
|
|
331
|
+
components: new Map([
|
|
332
|
+
[
|
|
333
|
+
"stack",
|
|
334
|
+
{
|
|
335
|
+
component: {
|
|
336
|
+
name: "stack",
|
|
337
|
+
dependsOn: [],
|
|
338
|
+
deploy: [{ phase: "Verify", steps: [{ kind: "wait-for-stack", stack: "stack" }] }],
|
|
339
|
+
},
|
|
340
|
+
filePath: "stack.component.ts",
|
|
341
|
+
},
|
|
342
|
+
],
|
|
343
|
+
]),
|
|
344
|
+
};
|
|
345
|
+
const diagnostics = comp005.check(ctx as never);
|
|
346
|
+
expect(diagnostics).toHaveLength(1);
|
|
347
|
+
expect(diagnostics[0].checkId).toBe("COMP005");
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
describe("COMP006: shell-needs-reason", () => {
|
|
352
|
+
it('flags a "shell" step with no reason', async () => {
|
|
353
|
+
const diagnostics = await lintFixture("comp006", "fail");
|
|
354
|
+
const hits = diagnostics.filter((d) => d.checkId === "COMP006");
|
|
355
|
+
expect(hits).toHaveLength(1);
|
|
356
|
+
expect(hits[0].component).toBe("legacy-tool");
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('does not flag a "shell" step with a declared reason', async () => {
|
|
360
|
+
const diagnostics = await lintFixture("comp006", "pass");
|
|
361
|
+
expect(diagnostics.filter((d) => d.checkId === "COMP006")).toHaveLength(0);
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
describe("COMP007: composition-sprawl", () => {
|
|
366
|
+
it("flags two components with an identical phase/step-kind composition shape", async () => {
|
|
367
|
+
const diagnostics = await lintFixture("comp007", "fail");
|
|
368
|
+
const hits = diagnostics.filter((d) => d.checkId === "COMP007");
|
|
369
|
+
expect(hits).toHaveLength(2);
|
|
370
|
+
expect(hits.map((d) => d.component).sort()).toEqual(["inventory-table", "orders-table"]);
|
|
371
|
+
expect(hits[0].severity).toBe("warning");
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it("does not flag structurally distinct components", async () => {
|
|
375
|
+
const diagnostics = await lintFixture("comp007", "pass");
|
|
376
|
+
expect(diagnostics.filter((d) => d.checkId === "COMP007")).toHaveLength(0);
|
|
377
|
+
});
|
|
378
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COMP001: publishes-but-never-applies
|
|
3
|
+
*
|
|
4
|
+
* Flags a component that publishes an image/artifact (a `publish-image`,
|
|
5
|
+
* `publish-artifact`, or `load-image-on-host` step) that nothing ever
|
|
6
|
+
* consumes: neither an apply/submit step later in the same component (via a
|
|
7
|
+
* `@Phase.field` prior-step reference into the publish phase) nor any other
|
|
8
|
+
* component (via `@<this-component>.publish.uri|digest|key`). A publish step
|
|
9
|
+
* with no consumer either belongs to a producer/library component another
|
|
10
|
+
* component should depend on, or is dead composition — see epic #551 §7
|
|
11
|
+
* ("What chant adds") and §"Failure modes" (renamed/orphaned sprawl).
|
|
12
|
+
*
|
|
13
|
+
* Triggers on: a `service`-shaped component whose `publish-image` output is
|
|
14
|
+
* never referenced by its own Apply phase or by any sibling component.
|
|
15
|
+
* OK: the publish output is referenced via `@Publish.digest` (same
|
|
16
|
+
* component) or `@<name>.publish.uri` (a consumer component).
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { ComponentCheck, ComponentCheckContext, ComponentCheckDiagnostic } from "../../component-checks";
|
|
20
|
+
import { collectWiringRefs, walkComponent } from "./support";
|
|
21
|
+
|
|
22
|
+
const PUBLISH_KIND = /^(publish-image|publish-artifact|load-image-on-host)$/;
|
|
23
|
+
|
|
24
|
+
export const comp001PublishesNeverAppliesRule: ComponentCheck = {
|
|
25
|
+
id: "COMP001",
|
|
26
|
+
severity: "error",
|
|
27
|
+
category: "correctness",
|
|
28
|
+
description: "A publish step's output is never consumed by an apply/submit step, in this component or any other",
|
|
29
|
+
check(ctx: ComponentCheckContext): ComponentCheckDiagnostic[] {
|
|
30
|
+
const diagnostics: ComponentCheckDiagnostic[] = [];
|
|
31
|
+
|
|
32
|
+
// Every `@<name>.publish.*` reference anywhere across every discovered
|
|
33
|
+
// component — a publish step in component X is "consumed" if some
|
|
34
|
+
// component (including X itself, via `@Phase.field`) references it.
|
|
35
|
+
const referencedComponentPublishes = new Set<string>();
|
|
36
|
+
for (const { component } of ctx.components.values()) {
|
|
37
|
+
const { steps } = walkComponent(component);
|
|
38
|
+
for (const { step } of steps) {
|
|
39
|
+
for (const ref of collectWiringRefs(step)) {
|
|
40
|
+
if (ref.kind === "component-artifact") referencedComponentPublishes.add(ref.componentName);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for (const [name, { component, filePath }] of ctx.components) {
|
|
46
|
+
const { steps } = walkComponent(component);
|
|
47
|
+
const publishSteps = steps.filter(({ step }) => PUBLISH_KIND.test(step.kind));
|
|
48
|
+
if (publishSteps.length === 0) continue;
|
|
49
|
+
|
|
50
|
+
// Consumed if: (a) some other/same component references
|
|
51
|
+
// `@<name>.publish.*`, or (b) a same-component prior-step reference
|
|
52
|
+
// points at the publish step's own phase (e.g. `@Publish.digest` used
|
|
53
|
+
// by a later Apply step in the same component).
|
|
54
|
+
if (referencedComponentPublishes.has(name)) continue;
|
|
55
|
+
|
|
56
|
+
const publishPhaseNames = new Set(publishSteps.map(({ phaseName }) => phaseName));
|
|
57
|
+
const consumedByPriorStepRef = steps.some(({ step }) =>
|
|
58
|
+
collectWiringRefs(step).some((ref) => ref.kind === "prior-step" && publishPhaseNames.has(ref.phaseName)),
|
|
59
|
+
);
|
|
60
|
+
if (consumedByPriorStepRef) continue;
|
|
61
|
+
|
|
62
|
+
diagnostics.push({
|
|
63
|
+
checkId: "COMP001",
|
|
64
|
+
severity: "error",
|
|
65
|
+
component: name,
|
|
66
|
+
file: filePath,
|
|
67
|
+
message:
|
|
68
|
+
`Component "${name}" publishes an artifact (${[...publishPhaseNames].join(", ")} phase) that is never ` +
|
|
69
|
+
`consumed — no apply/submit step in this component references it via "@${[...publishPhaseNames][0]}.<field>", ` +
|
|
70
|
+
`and no other component references "@${name}.publish.<field>". If this is a producer/library ` +
|
|
71
|
+
`component, a consumer should depend on it and reference its publish output; otherwise this publish step ` +
|
|
72
|
+
`is dead composition.`,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return diagnostics;
|
|
77
|
+
},
|
|
78
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COMP002: dangling-wiring-ref
|
|
3
|
+
*
|
|
4
|
+
* Flags a wiring reference (`@Phase.field` or `@<component>.publish.*`,
|
|
5
|
+
* carried in a step's `imageRef`/`jar`/`revision`/`inputs`/any other
|
|
6
|
+
* capability-specific field — see ../../../components/component.ts's
|
|
7
|
+
* `Wiring`) that points at a phase or component that does not exist:
|
|
8
|
+
*
|
|
9
|
+
* - `@Phase.field` naming a phase not declared anywhere in this component's
|
|
10
|
+
* own `deploy` (top-level phase names only — a nested fan-out phase's
|
|
11
|
+
* name is addressed by its own name, never a parent's, matching how
|
|
12
|
+
* ../../../components/driver.ts's `resolveWiring` keys `phaseOutputs` by
|
|
13
|
+
* the phase the step ran directly under).
|
|
14
|
+
* - `@<component>.publish.<field>` naming a component that either is not
|
|
15
|
+
* discovered at all, or is discovered but not listed in this component's
|
|
16
|
+
* `dependsOn` — the graph only resolves an artifact reference for a
|
|
17
|
+
* declared dependency (composition-and-wiring.mdx#cross-component-outputs).
|
|
18
|
+
*
|
|
19
|
+
* Triggers on: `imageRef: "@Build.digest"` when no phase named "Build"
|
|
20
|
+
* exists, or `jar: "@some-lib.publish.uri"` when "some-lib" is undiscovered
|
|
21
|
+
* or missing from `dependsOn`.
|
|
22
|
+
* OK: `imageRef: "@Publish.digest"` when a "Publish" phase exists in the same
|
|
23
|
+
* component, or `jar: "@jar-lib.publish.uri"` when "jar-lib" is discovered
|
|
24
|
+
* and listed in `dependsOn`.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import type { ComponentCheck, ComponentCheckContext, ComponentCheckDiagnostic } from "../../component-checks";
|
|
28
|
+
import { collectWiringRefs, topLevelPhaseNames, walkComponent } from "./support";
|
|
29
|
+
|
|
30
|
+
export const comp002DanglingWiringRefRule: ComponentCheck = {
|
|
31
|
+
id: "COMP002",
|
|
32
|
+
severity: "error",
|
|
33
|
+
category: "correctness",
|
|
34
|
+
description: "A wiring reference (@Phase.field or @component.publish.*) points at a phase or component that does not exist",
|
|
35
|
+
check(ctx: ComponentCheckContext): ComponentCheckDiagnostic[] {
|
|
36
|
+
const diagnostics: ComponentCheckDiagnostic[] = [];
|
|
37
|
+
|
|
38
|
+
for (const [name, { component, filePath }] of ctx.components) {
|
|
39
|
+
const phaseNames = topLevelPhaseNames(component);
|
|
40
|
+
const dependsOn = new Set(component.dependsOn ?? []);
|
|
41
|
+
const { steps } = walkComponent(component);
|
|
42
|
+
|
|
43
|
+
for (const { step, phaseName } of steps) {
|
|
44
|
+
for (const ref of collectWiringRefs(step)) {
|
|
45
|
+
if (ref.kind === "prior-step") {
|
|
46
|
+
if (phaseNames.has(ref.phaseName)) continue;
|
|
47
|
+
diagnostics.push({
|
|
48
|
+
checkId: "COMP002",
|
|
49
|
+
severity: "error",
|
|
50
|
+
component: name,
|
|
51
|
+
file: filePath,
|
|
52
|
+
message:
|
|
53
|
+
`Component "${name}": step "${step.kind}" (in phase "${phaseName}") references "@${ref.phaseName}.${ref.field}", ` +
|
|
54
|
+
`but no phase named "${ref.phaseName}" exists in this component's "deploy". Known phases: ` +
|
|
55
|
+
`${[...phaseNames].join(", ") || "(none)"}.`,
|
|
56
|
+
});
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ref.kind === "component-artifact"
|
|
61
|
+
const target = ctx.components.get(ref.componentName);
|
|
62
|
+
if (!target) {
|
|
63
|
+
diagnostics.push({
|
|
64
|
+
checkId: "COMP002",
|
|
65
|
+
severity: "error",
|
|
66
|
+
component: name,
|
|
67
|
+
file: filePath,
|
|
68
|
+
message:
|
|
69
|
+
`Component "${name}": step "${step.kind}" (in phase "${phaseName}") references ` +
|
|
70
|
+
`"@${ref.componentName}.publish.${ref.field}", but no component named "${ref.componentName}" was discovered.`,
|
|
71
|
+
});
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!dependsOn.has(ref.componentName)) {
|
|
76
|
+
diagnostics.push({
|
|
77
|
+
checkId: "COMP002",
|
|
78
|
+
severity: "error",
|
|
79
|
+
component: name,
|
|
80
|
+
file: filePath,
|
|
81
|
+
message:
|
|
82
|
+
`Component "${name}": step "${step.kind}" (in phase "${phaseName}") references ` +
|
|
83
|
+
`"@${ref.componentName}.publish.${ref.field}", but "${ref.componentName}" is not listed in this ` +
|
|
84
|
+
`component's "dependsOn" — the graph only resolves an artifact reference for a declared dependency. ` +
|
|
85
|
+
`Add "${ref.componentName}" to dependsOn.`,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return diagnostics;
|
|
93
|
+
},
|
|
94
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COMP003: mutating-no-rollback
|
|
3
|
+
*
|
|
4
|
+
* Flags a mutating capability step whose family has no known native/starter
|
|
5
|
+
* rollback and no explicit opt-out — the "mutating capability with no
|
|
6
|
+
* rollback and no explicit opt-out reason" guardrail from epic #551 §7 and
|
|
7
|
+
* §"Failure modes".
|
|
8
|
+
*
|
|
9
|
+
* Each capability declares its rollback disposition via `rollbackPolicy`
|
|
10
|
+
* (../../../components/capability.ts): `"native"` (a paired `rollback`, e.g.
|
|
11
|
+
* `cfn-deploy`/`ecs-update-service`/`lambda-deploy`/`code-deploy`),
|
|
12
|
+
* `"none-by-design"` (nothing to compensate — the build/publish and wait/verify
|
|
13
|
+
* families, whose outputs are immutable/content-addressed or read-only), or
|
|
14
|
+
* `"needs-opt-out"` (a mutating verb with no native rollback and no safe undo,
|
|
15
|
+
* e.g. `s3-sync`/`run-migration`). This rule flags only `"needs-opt-out"` steps
|
|
16
|
+
* lacking an explicit opt-out. It reads the disposition from the project's
|
|
17
|
+
* capability registry (`ctx.rollbackPolicies`, built by `chant lint` from the
|
|
18
|
+
* active lexicons + core starter — the same seam COMP005 uses for
|
|
19
|
+
* `ctx.knownKinds`), never a hard-coded verb list, so the aws leaves'
|
|
20
|
+
* dispositions come from the aws lexicon that owns them.
|
|
21
|
+
*
|
|
22
|
+
* The opt-out: since the capability interface and schema are out of scope for
|
|
23
|
+
* this issue (`Step` already has `additionalProperties: true` — see
|
|
24
|
+
* component.schema.json), a component may attach a `noRollback: "<reason>"`
|
|
25
|
+
* string property directly on the step to declare the compensation gap is
|
|
26
|
+
* intentional and understood, or supply its own explicit compensation via a
|
|
27
|
+
* component-level `rollback` field (as the ALB/ECS pilot does with
|
|
28
|
+
* `rollback-previous`) or a sibling `rollback-previous`/`snapshot-before` step
|
|
29
|
+
* in the same phase.
|
|
30
|
+
*
|
|
31
|
+
* Triggers on: a bare `{ kind: "run-migration", ... }` step with no
|
|
32
|
+
* `noRollback` reason, no component-level `rollback`, and no sibling
|
|
33
|
+
* `rollback-previous`/`snapshot-before` step.
|
|
34
|
+
* OK: `{ kind: "run-migration", ..., noRollback: "forward-only migration, ..." }`,
|
|
35
|
+
* or a component `rollback` field, or a sibling compensation step.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import type { ComponentCheck, ComponentCheckContext, ComponentCheckDiagnostic } from "../../component-checks";
|
|
39
|
+
import { walkComponent } from "./support";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A step is flagged only when its verb's rollback disposition is
|
|
43
|
+
* `"needs-opt-out"` — a mutating capability with no native rollback and no safe
|
|
44
|
+
* undo. That disposition is the capability's own declaration (`rollbackPolicy`,
|
|
45
|
+
* ../../../components/capability.ts), surfaced to this rule via
|
|
46
|
+
* `ctx.rollbackPolicies` (built from the project's registry by `chant lint`,
|
|
47
|
+
* exactly like `ctx.knownKinds` for COMP005). Verbs with a native rollback
|
|
48
|
+
* (`cfn-deploy`, …) or nothing to compensate (build/publish/wait) are
|
|
49
|
+
* `"native"`/`"none-by-design"` and never flagged. When no registry was
|
|
50
|
+
* resolved (a direct unit test), an unknown verb defaults to
|
|
51
|
+
* `"none-by-design"` — the rule stays silent rather than guessing.
|
|
52
|
+
*/
|
|
53
|
+
function policyFor(ctx: ComponentCheckContext, kind: string): string {
|
|
54
|
+
return ctx.rollbackPolicies?.get(kind) ?? "none-by-design";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** A sibling step kind that itself is (or supplies) compensation — its presence in the same phase is treated as the component handling rollback explicitly, not silence. */
|
|
58
|
+
const COMPENSATION_SIBLING_KINDS = new Set(["rollback-previous", "snapshot-before"]);
|
|
59
|
+
|
|
60
|
+
export const comp003MutatingNoRollbackRule: ComponentCheck = {
|
|
61
|
+
id: "COMP003",
|
|
62
|
+
severity: "error",
|
|
63
|
+
category: "correctness",
|
|
64
|
+
description: "A mutating capability step has no known rollback and no explicit opt-out (noRollback reason, component rollback, or a compensation sibling step)",
|
|
65
|
+
check(ctx: ComponentCheckContext): ComponentCheckDiagnostic[] {
|
|
66
|
+
const diagnostics: ComponentCheckDiagnostic[] = [];
|
|
67
|
+
|
|
68
|
+
for (const [name, { component, filePath }] of ctx.components) {
|
|
69
|
+
const hasComponentRollback = Array.isArray(component.rollback) && component.rollback.length > 0;
|
|
70
|
+
|
|
71
|
+
// Walk every step (including inside nested fan-out phases and
|
|
72
|
+
// onFailure compensation phases — see support.ts's walkComponent) so a
|
|
73
|
+
// mutating step nested inside a fan-out unit (e.g. a Neo4j-style
|
|
74
|
+
// per-instance phase) is inspected the same as a top-level one.
|
|
75
|
+
const { steps } = walkComponent(component);
|
|
76
|
+
|
|
77
|
+
// Group steps by the exact Phase object they live under (identity, not
|
|
78
|
+
// name — two different nested phases can share a display name) so the
|
|
79
|
+
// "sibling in the same phase" compensation check only looks at true
|
|
80
|
+
// siblings, never steps from an unrelated phase of the same name.
|
|
81
|
+
const stepsByPhase = new Map<object, typeof steps>();
|
|
82
|
+
for (const walked of steps) {
|
|
83
|
+
const group = stepsByPhase.get(walked.phase) ?? [];
|
|
84
|
+
group.push(walked);
|
|
85
|
+
stepsByPhase.set(walked.phase, group);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (const walked of steps) {
|
|
89
|
+
const { step, phaseName, phase } = walked;
|
|
90
|
+
if (policyFor(ctx, step.kind) !== "needs-opt-out") continue;
|
|
91
|
+
|
|
92
|
+
const noRollback = (step as { noRollback?: unknown }).noRollback;
|
|
93
|
+
const hasStepOptOut = typeof noRollback === "string" && noRollback.trim().length > 0;
|
|
94
|
+
if (hasStepOptOut || hasComponentRollback) continue;
|
|
95
|
+
|
|
96
|
+
const siblings = stepsByPhase.get(phase) ?? [];
|
|
97
|
+
const hasCompensationSibling = siblings.some((s) => COMPENSATION_SIBLING_KINDS.has(s.step.kind));
|
|
98
|
+
if (hasCompensationSibling) continue;
|
|
99
|
+
|
|
100
|
+
diagnostics.push({
|
|
101
|
+
checkId: "COMP003",
|
|
102
|
+
severity: "error",
|
|
103
|
+
component: name,
|
|
104
|
+
file: filePath,
|
|
105
|
+
message:
|
|
106
|
+
`Component "${name}": step "${step.kind}" (phase "${phaseName}") is a mutating capability with no ` +
|
|
107
|
+
`known native rollback and no explicit opt-out. Add a "noRollback: \\"<reason>\\"" property to the step, ` +
|
|
108
|
+
`a component-level "rollback" phase, or a sibling "rollback-previous"/"snapshot-before" step in the same phase.`,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return diagnostics;
|
|
114
|
+
},
|
|
115
|
+
};
|