@empire-builder-kit/nx 1.0.0-rc.4 → 1.0.0-rc.40
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/README.md +18 -0
- package/executors.json +21 -1
- package/generators.json +10 -0
- package/package.json +31 -7
- package/src/aws/child-environment.d.ts +10 -0
- package/src/aws/child-environment.js +77 -0
- package/src/aws/identity.d.ts +13 -0
- package/src/aws/identity.js +41 -0
- package/src/aws-authority/index.d.ts +2 -0
- package/src/aws-authority/index.js +6 -0
- package/src/aws-authority/lambda-policy-ordering.d.ts +38 -0
- package/src/aws-authority/lambda-policy-ordering.js +56 -0
- package/src/aws-authority/stage-authority.d.ts +248 -0
- package/src/aws-authority/stage-authority.js +3558 -0
- package/src/blueprints/external-sandbox.js +21 -9
- package/src/blueprints/manifest.d.ts +1 -1
- package/src/blueprints/resolver.js +28 -15
- package/src/cli/bootstrap.d.ts +1 -1
- package/src/cli/deploy-runtime.d.ts +6 -0
- package/src/cli/deploy-runtime.js +58 -0
- package/src/cli/dev-command.d.ts +16 -4
- package/src/cli/dev-command.js +63 -0
- package/src/cli/main.d.ts +9 -1
- package/src/cli/main.js +139 -20
- package/src/cli/output.d.ts +1 -1
- package/src/cli/output.js +1 -1
- package/src/developer-agent/context.js +41 -9
- package/src/developer-agent/generator-planning.js +20 -6
- package/src/developer-agent/git-inspection.d.ts +6 -2
- package/src/developer-agent/git-inspection.js +78 -6
- package/src/developer-agent/model.d.ts +5 -3
- package/src/developer-agent/model.js +4 -4
- package/src/developer-agent/operation-registry.d.ts +5 -0
- package/src/developer-agent/operation-registry.js +286 -40
- package/src/developer-agent/planning.js +16 -13
- package/src/developer-agent/preview-changes.d.ts +2 -0
- package/src/developer-agent/preview-changes.js +14 -2
- package/src/developer-agent/preview-source.js +3 -0
- package/src/developer-agent/validation.js +25 -9
- package/src/developer-agent/workspace-inspection.js +60 -5
- package/src/executors/blueprint-trust/blueprint-trust.js +6 -0
- package/src/executors/deployment-plan/deployment-plan.d.ts +2 -0
- package/src/executors/deployment-plan/deployment-plan.js +57 -2
- package/src/executors/dev-doctor/dev-doctor.js +13 -6
- package/src/executors/dev-task-cleanup/dev-task-cleanup.d.ts +23 -0
- package/src/executors/dev-task-cleanup/dev-task-cleanup.js +145 -0
- package/src/executors/dev-task-cleanup/schema.json +9 -0
- package/src/executors/dev-task-cleanup/task-stack-absence.d.ts +27 -0
- package/src/executors/dev-task-cleanup/task-stack-absence.js +281 -0
- package/src/executors/dev-task-cleanup/task-stack-checkpoint.d.ts +12 -0
- package/src/executors/dev-task-cleanup/task-stack-checkpoint.js +92 -0
- package/src/executors/fleet/fleet.js +1 -1
- package/src/executors/foundation-preflight/foundation-preflight.d.ts +1 -0
- package/src/executors/foundation-preflight/foundation-preflight.js +6 -2
- package/src/executors/foundation-preflight/schema.json +5 -0
- package/src/executors/foundation-profile-preflight/foundation-profile-preflight.d.ts +13 -2
- package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js +95 -31
- package/src/executors/foundation-profile-preflight/production-database-safeguard.d.ts +65 -0
- package/src/executors/foundation-profile-preflight/production-database-safeguard.js +223 -0
- package/src/executors/foundation-profile-preflight/schema.json +15 -4
- package/src/executors/foundation-remove-preflight/foundation-remove-preflight.d.ts +11 -1
- package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js +70 -20
- package/src/executors/foundation-remove-preflight/schema.json +6 -1
- package/src/executors/github-bootstrap/github-bootstrap.d.ts +1 -0
- package/src/executors/github-bootstrap/github-bootstrap.js +3 -0
- package/src/executors/github-bootstrap/schema.json +7 -1
- package/src/executors/infrastructure-plan/infrastructure-plan.d.ts +29 -136
- package/src/executors/infrastructure-plan/infrastructure-plan.js +230 -3282
- package/src/executors/infrastructure-plan/schema.json +12 -52
- package/src/executors/local-deploy/federated-credential-transport.d.ts +48 -0
- package/src/executors/local-deploy/federated-credential-transport.js +252 -0
- package/src/executors/local-deploy/local-approval.d.ts +18 -0
- package/src/executors/local-deploy/local-approval.js +39 -0
- package/src/executors/local-deploy/local-deploy.d.ts +112 -0
- package/src/executors/local-deploy/local-deploy.js +1404 -0
- package/src/executors/local-deploy/schema.json +70 -0
- package/src/executors/refresh/refresh.js +2 -2
- package/src/executors/remove/remove.js +2 -2
- package/src/executors/router-waf-rehearsal/router-waf-rehearsal.d.ts +42 -0
- package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js +1229 -0
- package/src/executors/router-waf-rehearsal/schema.json +29 -0
- package/src/executors/secret-initialize/schema.json +42 -0
- package/src/executors/secret-initialize/secret-initialize.d.ts +34 -0
- package/src/executors/secret-initialize/secret-initialize.js +297 -0
- package/src/executors/secret-metadata/schema.json +5 -0
- package/src/executors/secret-metadata/secret-metadata.d.ts +16 -2
- package/src/executors/secret-metadata/secret-metadata.js +129 -30
- package/src/executors/sst-lifecycle/generated-secret-retirement.d.ts +53 -0
- package/src/executors/sst-lifecycle/generated-secret-retirement.js +192 -0
- package/src/executors/sst-lifecycle/governed-native-operation.d.ts +48 -0
- package/src/executors/sst-lifecycle/governed-native-operation.js +162 -0
- package/src/executors/sst-lifecycle/logging-preflight.d.ts +5 -0
- package/src/executors/sst-lifecycle/logging-preflight.js +23 -13
- package/src/executors/sst-lifecycle/run-sst-command.d.ts +40 -6
- package/src/executors/sst-lifecycle/run-sst-command.js +779 -70
- package/src/executors/sst-lifecycle/schema.json +8 -1
- package/src/executors/sst-lifecycle/secret-preflight.d.ts +2 -0
- package/src/executors/sst-lifecycle/secret-preflight.js +13 -7
- package/src/executors/sst-lifecycle/sst-environment-source.d.ts +33 -0
- package/src/executors/sst-lifecycle/sst-environment-source.js +267 -0
- package/src/executors/sst-lifecycle/task-unlock.d.ts +15 -0
- package/src/executors/sst-lifecycle/task-unlock.js +126 -0
- package/src/executors/unlock/unlock.js +2 -2
- package/src/foundation/capability-plan.d.ts +43 -0
- package/src/foundation/capability-plan.js +168 -0
- package/src/foundation/discovery.d.ts +14 -11
- package/src/foundation/discovery.js +32 -6
- package/src/foundation/index.d.ts +4 -0
- package/src/foundation/index.js +11 -0
- package/src/foundation/local-database-contract.d.ts +20 -0
- package/src/foundation/local-database-contract.js +262 -13
- package/src/foundation/local-private-smoke-access.d.ts +20 -0
- package/src/foundation/local-private-smoke-access.js +197 -0
- package/src/foundation/logging-preflight.js +1 -1
- package/src/foundation/logging.d.ts +3 -1
- package/src/foundation/logging.js +1 -1
- package/src/foundation/partial-state.d.ts +10 -0
- package/src/foundation/partial-state.js +21 -0
- package/src/foundation/preflight.js +37 -18
- package/src/foundation/private-smoke-access.d.ts +25 -0
- package/src/foundation/private-smoke-access.js +135 -0
- package/src/foundation/private-smoke-admission.d.ts +45 -0
- package/src/foundation/private-smoke-admission.js +180 -0
- package/src/foundation/private-smoke-control-plane.d.ts +18 -0
- package/src/foundation/private-smoke-control-plane.js +184 -0
- package/src/foundation/provision-database-bindings.d.ts +5 -0
- package/src/foundation/provision-database-bindings.js +41 -8
- package/src/foundation/provision-logical-database.js +15 -8
- package/src/foundation/requirements.d.ts +3 -3
- package/src/foundation/requirements.js +24 -12
- package/src/foundation/router-waf-rate-rehearsal.d.ts +97 -0
- package/src/foundation/router-waf-rate-rehearsal.js +210 -0
- package/src/foundation/router-waf.d.ts +149 -0
- package/src/foundation/router-waf.js +809 -0
- package/src/foundation/stages.d.ts +1 -0
- package/src/foundation/stages.js +11 -0
- package/src/generators/blueprint/blueprint.d.ts +5 -0
- package/src/generators/blueprint/blueprint.js +102 -0
- package/src/generators/blueprint/files.d.ts +6 -0
- package/src/generators/blueprint/files.js +217 -0
- package/src/generators/blueprint/schema.d.ts +4 -0
- package/src/generators/blueprint/schema.json +18 -0
- package/src/generators/formatting.d.ts +8 -0
- package/src/generators/formatting.js +141 -0
- package/src/generators/function/function.js +38 -17
- package/src/generators/logging-registry.d.ts +2 -0
- package/src/generators/logging-registry.js +22 -0
- package/src/generators/post-generation-install.js +2 -2
- package/src/generators/preset/dependency-overrides.d.ts +8 -0
- package/src/generators/preset/dependency-overrides.js +55 -0
- package/src/generators/preset/generator.js +43 -3
- package/src/generators/preset/schema.d.ts +10 -0
- package/src/generators/preset/schema.json +47 -0
- package/src/generators/preset/tool-versions.d.ts +2 -0
- package/src/generators/preset/tool-versions.js +3 -1
- package/src/generators/preset/workspace-files-adopter-skills.d.ts +7 -0
- package/src/generators/preset/workspace-files-adopter-skills.js +303 -0
- package/src/generators/preset/workspace-files-ci-acceptance.d.ts +1 -0
- package/src/generators/preset/workspace-files-ci-acceptance.js +791 -0
- package/src/generators/preset/workspace-files-ci-install.d.ts +2 -0
- package/src/generators/preset/workspace-files-ci-install.js +196 -0
- package/src/generators/preset/workspace-files-developer-agent.d.ts +2 -0
- package/src/generators/preset/workspace-files-developer-agent.js +90 -28
- package/src/generators/preset/workspace-files-ecr-inspector.d.ts +18 -0
- package/src/generators/preset/workspace-files-ecr-inspector.js +298 -0
- package/src/generators/preset/workspace-files-foundation-workload-roles.js +5 -2
- package/src/generators/preset/workspace-files-performance-cost.d.ts +2 -2
- package/src/generators/preset/workspace-files-performance-cost.js +8 -8
- package/src/generators/preset/workspace-files-performance-load.d.ts +1 -1
- package/src/generators/preset/workspace-files-performance-load.js +39 -25
- package/src/generators/preset/workspace-files-stage-authority.d.ts +6 -0
- package/src/generators/preset/workspace-files-stage-authority.js +518 -0
- package/src/generators/preset/workspace-files-thin-deploy.d.ts +55 -0
- package/src/generators/preset/workspace-files-thin-deploy.js +2473 -0
- package/src/generators/preset/workspace-files-thin-docs.d.ts +7 -0
- package/src/generators/preset/workspace-files-thin-docs.js +352 -0
- package/src/generators/preset/workspace-files-thin-release.d.ts +14 -0
- package/src/generators/preset/workspace-files-thin-release.js +26 -0
- package/src/generators/preset/workspace-files-workload-role-baseline.js +254 -42
- package/src/generators/preset/workspace-files.d.ts +18 -2
- package/src/generators/preset/workspace-files.js +1504 -608
- package/src/generators/repository-policy/repository-policy.js +235 -5
- package/src/generators/repository-policy/schema.d.ts +10 -0
- package/src/generators/repository-policy/schema.json +80 -4
- package/src/generators/repository-policy/tree-policy.d.ts +3 -0
- package/src/generators/repository-policy/tree-policy.js +25 -0
- package/src/generators/slice/built-in-plan-api-contract-files.js +42 -7
- package/src/generators/slice/built-in-plan-api-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-api-files.js +130 -414
- package/src/generators/slice/built-in-plan-async-work-files.js +166 -1969
- package/src/generators/slice/built-in-plan-authorization-files.js +92 -329
- package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js +60 -2
- package/src/generators/slice/built-in-plan-authorization-storage.d.ts +3 -0
- package/src/generators/slice/built-in-plan-authorization-storage.js +119 -0
- package/src/generators/slice/built-in-plan-background-work-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-background-work-files.js +99 -0
- package/src/generators/slice/built-in-plan-data-deletion-files.js +2 -830
- package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js +5 -31
- package/src/generators/slice/built-in-plan-data-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-data-files.js +163 -1196
- package/src/generators/slice/built-in-plan-data-lifecycle-files.js +53 -28
- package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js +37 -2
- package/src/generators/slice/built-in-plan-data-retention-job-files.js +7 -2443
- package/src/generators/slice/built-in-plan-data-retention-launcher-files.js +50 -8
- package/src/generators/slice/built-in-plan-data-rights-files.js +6 -4482
- package/src/generators/slice/built-in-plan-database-lifecycle-files.js +44 -25
- package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js +20 -1
- package/src/generators/slice/built-in-plan-foundation-files.d.ts +2 -1
- package/src/generators/slice/built-in-plan-foundation-files.js +39 -18
- package/src/generators/slice/built-in-plan-frontend-files.js +7 -3
- package/src/generators/slice/built-in-plan-infrastructure-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-infrastructure-files.js +933 -729
- package/src/generators/slice/built-in-plan-infrastructure-safety-files.js +67 -11
- package/src/generators/slice/built-in-plan-key-value-cleanup.d.ts +4 -0
- package/src/generators/slice/built-in-plan-key-value-cleanup.js +100 -0
- package/src/generators/slice/built-in-plan-key-value-consumer.d.ts +4 -0
- package/src/generators/slice/built-in-plan-key-value-consumer.js +151 -0
- package/src/generators/slice/built-in-plan-key-value-deletion.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-deletion.js +377 -0
- package/src/generators/slice/built-in-plan-key-value-delivery.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-delivery.js +137 -0
- package/src/generators/slice/built-in-plan-key-value-export.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-export.js +595 -0
- package/src/generators/slice/built-in-plan-key-value-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-files.js +31 -0
- package/src/generators/slice/built-in-plan-key-value-holds.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-holds.js +189 -0
- package/src/generators/slice/built-in-plan-key-value-infrastructure.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-infrastructure.js +102 -0
- package/src/generators/slice/built-in-plan-key-value-performance.d.ts +5 -0
- package/src/generators/slice/built-in-plan-key-value-performance.js +530 -0
- package/src/generators/slice/built-in-plan-key-value-provider.d.ts +4 -0
- package/src/generators/slice/built-in-plan-key-value-provider.js +366 -0
- package/src/generators/slice/built-in-plan-key-value-records.d.ts +3 -0
- package/src/generators/slice/built-in-plan-key-value-records.js +232 -0
- package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.js +103 -0
- package/src/generators/slice/built-in-plan-key-value-retention-job-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention-job-files.js +160 -0
- package/src/generators/slice/built-in-plan-key-value-retention-maintenance.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention-maintenance.js +329 -0
- package/src/generators/slice/built-in-plan-key-value-retention-worker.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention-worker.js +477 -0
- package/src/generators/slice/built-in-plan-key-value-retention.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention.js +184 -0
- package/src/generators/slice/built-in-plan-metadata-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-metadata-files.js +157 -28
- package/src/generators/slice/built-in-plan-native-runtime-authority-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-native-runtime-authority-files.js +324 -0
- package/src/generators/slice/built-in-plan-operations-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-operations-files.js +585 -97
- package/src/generators/slice/built-in-plan-performance-files.d.ts +2 -1
- package/src/generators/slice/built-in-plan-performance-files.js +220 -1074
- package/src/generators/slice/built-in-plan-project-files.js +141 -135
- package/src/generators/slice/built-in-plan-release-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-release-files.js +310 -70
- package/src/generators/slice/built-in-plan-reliability-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-reliability-files.js +279 -43
- package/src/generators/slice/built-in-plan-secret-runtime-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-secret-runtime-files.js +87 -76
- package/src/generators/slice/built-in-plan-shared.d.ts +4 -2
- package/src/generators/slice/built-in-plan-shared.js +41 -19
- package/src/generators/slice/built-in-plan-sql-events.d.ts +2 -0
- package/src/generators/slice/built-in-plan-sql-events.js +276 -0
- package/src/generators/slice/built-in-plan-testing-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-testing-files.js +304 -172
- package/src/generators/slice/built-in-plan-testing-persistence-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-testing-persistence-files.js +178 -0
- package/src/generators/slice/built-in-plan-thin-release-files.d.ts +11 -0
- package/src/generators/slice/built-in-plan-thin-release-files.js +162 -0
- package/src/generators/slice/built-in-plan-verification-files.js +76 -2
- package/src/generators/slice/built-in-plan-web-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-web-files.js +223 -16
- package/src/generators/slice/built-in-plan-workload-role-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-workload-role-files.js +143 -279
- package/src/generators/slice/built-in-plan.d.ts +11 -1
- package/src/generators/slice/built-in-plan.js +80 -29
- package/src/generators/slice/data-policy-review-seed.d.ts +2 -0
- package/src/generators/slice/data-policy-review-seed.js +22 -0
- package/src/generators/slice/deployed-smoke-readiness.d.ts +2 -0
- package/src/generators/slice/deployed-smoke-readiness.js +214 -0
- package/src/generators/slice/local-source-identity.d.ts +2 -0
- package/src/generators/slice/local-source-identity.js +31 -0
- package/src/generators/slice/persistence-contract.d.ts +14 -0
- package/src/generators/slice/persistence-contract.js +32 -0
- package/src/generators/slice/slice.js +61 -50
- package/src/generators/sql/schema.d.ts +7 -0
- package/src/generators/sql/schema.json +38 -0
- package/src/generators/sql/sql.d.ts +6 -0
- package/src/generators/sql/sql.js +53 -0
- package/src/generators/upgrade/sql-seeded-reconciliation.d.ts +2 -0
- package/src/generators/upgrade/sql-seeded-reconciliation.js +170 -0
- package/src/generators/upgrade/upgrade.d.ts +15 -1
- package/src/generators/upgrade/upgrade.js +435 -44
- package/src/generators/workload/performance-registration.d.ts +1 -0
- package/src/generators/workload/performance-registration.js +9 -11
- package/src/generators/workload/service-performance-files.d.ts +1 -0
- package/src/generators/workload/service-performance-files.js +27 -7
- package/src/generators/workload/workload.d.ts +1 -1
- package/src/generators/workload/workload.js +105 -29
- package/src/git-policy/action-pins.d.ts +5 -0
- package/src/git-policy/action-pins.js +5 -0
- package/src/git-policy/github-bootstrap.d.ts +39 -8
- package/src/git-policy/github-bootstrap.js +329 -175
- package/src/git-policy/routing.d.ts +7 -1
- package/src/git-policy/routing.js +62 -1
- package/src/index.d.ts +1 -0
- package/src/index.js +1 -0
- package/src/infrastructure-change/local-capability.d.ts +73 -0
- package/src/infrastructure-change/local-capability.js +114 -0
- package/src/infrastructure-change/model.d.ts +25 -4
- package/src/infrastructure-change/model.js +1 -0
- package/src/infrastructure-change/normalize.d.ts +4 -0
- package/src/infrastructure-change/normalize.js +47 -7
- package/src/infrastructure-change/plan.d.ts +30 -3
- package/src/infrastructure-change/plan.js +323 -13
- package/src/infrastructure-change/risk.d.ts +3 -1
- package/src/infrastructure-change/risk.js +96 -16
- package/src/local-dev/browser-verification.d.ts +37 -0
- package/src/local-dev/browser-verification.js +281 -0
- package/src/local-dev/contracts.d.ts +10 -1
- package/src/local-dev/contracts.js +114 -13
- package/src/local-dev/dev-command-core.js +24 -5
- package/src/local-dev/docker-credential-health.d.ts +14 -0
- package/src/local-dev/docker-credential-health.js +94 -0
- package/src/local-dev/environment-inspector.d.ts +7 -0
- package/src/local-dev/environment-inspector.js +42 -15
- package/src/local-dev/environment-state.js +2 -2
- package/src/local-dev/errors.d.ts +1 -1
- package/src/local-dev/errors.js +2 -0
- package/src/local-dev/fleet-client.js +2 -2
- package/src/local-dev/fleet-configuration.js +57 -25
- package/src/local-dev/fleet-inventory.js +46 -18
- package/src/local-dev/fleet-ipc.d.ts +7 -0
- package/src/local-dev/fleet-ipc.js +14 -2
- package/src/local-dev/fleet-launcher.d.ts +6 -0
- package/src/local-dev/fleet-launcher.js +14 -2
- package/src/local-dev/fleet-logs.js +50 -22
- package/src/local-dev/fleet-process.d.ts +20 -1
- package/src/local-dev/fleet-process.js +197 -41
- package/src/local-dev/fleet-runner-evidence.d.ts +5 -0
- package/src/local-dev/fleet-runner-evidence.js +56 -0
- package/src/local-dev/fleet-runtime.js +268 -229
- package/src/local-dev/fleet-session.d.ts +2 -0
- package/src/local-dev/fleet-session.js +28 -11
- package/src/local-dev/fleet-slice-runner-daemon.js +4 -0
- package/src/local-dev/fleet-state.d.ts +5 -2
- package/src/local-dev/fleet-state.js +25 -4
- package/src/local-dev/fleet-supervisor.d.ts +3 -0
- package/src/local-dev/fleet-supervisor.js +66 -11
- package/src/local-dev/fleet-web-start.d.ts +12 -0
- package/src/local-dev/fleet-web-start.js +48 -0
- package/src/local-dev/identity.js +3 -14
- package/src/local-dev/index.d.ts +1 -0
- package/src/local-dev/index.js +1 -0
- package/src/local-dev/native-completion.d.ts +9 -0
- package/src/local-dev/native-completion.js +130 -0
- package/src/local-dev/nx-workspace.d.ts +4 -2
- package/src/local-dev/nx-workspace.js +11 -6
- package/src/local-dev/personal-stage.d.ts +4 -5
- package/src/local-dev/personal-stage.js +6 -54
- package/src/local-dev/port-claims.d.ts +13 -0
- package/src/local-dev/port-claims.js +46 -0
- package/src/local-dev/ports.d.ts +14 -2
- package/src/local-dev/ports.js +78 -3
- package/src/local-dev/private-state.d.ts +11 -0
- package/src/local-dev/private-state.js +146 -40
- package/src/local-dev/process-group.d.ts +4 -0
- package/src/local-dev/process-group.js +29 -0
- package/src/local-dev/process-identity.d.ts +8 -0
- package/src/local-dev/process-identity.js +31 -1
- package/src/local-dev/proxy.js +11 -12
- package/src/local-dev/runtime-paths.js +1 -1
- package/src/local-dev/slice-claim.js +1 -1
- package/src/local-dev/slice-runner.d.ts +15 -6
- package/src/local-dev/slice-runner.js +189 -54
- package/src/local-dev/task-attachment.d.ts +17 -0
- package/src/local-dev/task-attachment.js +73 -0
- package/src/local-dev/task-authority.d.ts +10 -0
- package/src/local-dev/task-authority.js +27 -0
- package/src/local-dev/task-identity.d.ts +33 -0
- package/src/local-dev/task-identity.js +418 -0
- package/src/local-dev/task-native-writer.d.ts +18 -0
- package/src/local-dev/task-native-writer.js +329 -0
- package/src/local-dev/tls.js +8 -2
- package/src/local-dev/web-launch.d.ts +9 -0
- package/src/local-dev/web-launch.js +130 -0
- package/src/ownership/composition.d.ts +2 -1
- package/src/ownership/composition.js +1 -0
- package/src/performance/cost-collection.d.ts +1 -1
- package/src/performance/cost-collection.js +4 -2
- package/src/performance/cost-controls.d.ts +8 -4
- package/src/performance/cost-controls.js +39 -28
- package/src/performance/evidence.d.ts +1 -1
- package/src/performance/evidence.js +5 -3
- package/src/performance/load-safety.js +5 -3
- package/src/performance/model.d.ts +4 -4
- package/src/performance/model.js +4 -5
- package/src/performance/scenario.d.ts +22 -0
- package/src/performance/scenario.js +100 -92
- package/src/release/index.d.ts +0 -2
- package/src/release/index.js +0 -2
- package/src/release/source-authority.d.ts +5 -5
- package/src/release/source-authority.js +141 -124
- package/src/repository-policy/aurora-preflight.d.ts +1 -1
- package/src/repository-policy/aurora-preflight.js +4 -1
- package/src/repository-policy/capacity-migration.d.ts +3 -0
- package/src/repository-policy/capacity-migration.js +21 -0
- package/src/repository-policy/deployment-plan.d.ts +4 -1
- package/src/repository-policy/deployment-plan.js +9 -0
- package/src/repository-policy/foundation-profile.js +10 -1
- package/src/repository-policy/policy.d.ts +57 -3
- package/src/repository-policy/policy.js +497 -11
- package/src/schemas/blueprint-v1.json +5 -4
- package/src/schemas/foundation-requirement-v1.json +5 -4
- package/src/schemas/performance-policy-v1.json +3 -3
- package/src/schemas/repository-policy-v1.json +130 -6
- package/src/schemas/testing-evidence-result-v1.json +1 -1
- package/src/secrets/adapters.d.ts +7 -2
- package/src/secrets/adapters.js +34 -3
- package/src/secrets/aws-control-plane.d.ts +10 -1
- package/src/secrets/aws-control-plane.js +67 -0
- package/src/secrets/generated-aws-control-plane.d.ts +23 -0
- package/src/secrets/generated-aws-control-plane.js +259 -0
- package/src/secrets/metadata.js +38 -10
- package/src/secrets/model.d.ts +8 -1
- package/src/secrets/model.js +24 -3
- package/src/secrets/operator.d.ts +91 -3
- package/src/secrets/operator.js +463 -13
- package/src/secrets/registry.d.ts +2 -1
- package/src/secrets/registry.js +18 -1
- package/src/secrets/verification.js +9 -1
- package/src/secrets/workload-access.js +10 -2
- package/src/sst-environment-source-format.d.ts +6 -0
- package/src/sst-environment-source-format.js +199 -0
- package/src/supply-chain/action-pins.js +61 -7
- package/src/supply-chain/blueprint-trust-git.js +85 -24
- package/src/supply-chain/blueprint-verification-authority.js +55 -3
- package/src/supply-chain/build-output-bounds.d.ts +2 -0
- package/src/supply-chain/build-output-bounds.js +12 -0
- package/src/supply-chain/build-output.d.ts +7 -1
- package/src/supply-chain/build-output.js +177 -24
- package/src/supply-chain/ecr-scanning.d.ts +5 -3
- package/src/supply-chain/ecr-scanning.js +11 -7
- package/src/supply-chain/ecs-deployed-image.d.ts +5 -3
- package/src/supply-chain/ecs-deployed-image.js +6 -4
- package/src/supply-chain/generated-blueprint-trust-command.js +18 -1
- package/src/supply-chain/generated-command.js +1 -0
- package/src/supply-chain/generated-container-release-command.js +1285 -124
- package/src/supply-chain/generated-ecr-collector-command.d.ts +0 -1
- package/src/supply-chain/generated-ecr-collector-command.js +200 -93
- package/src/supply-chain/generated-files.js +25 -14
- package/src/supply-chain/generated-scanner-command.js +2 -1
- package/src/supply-chain/index.d.ts +1 -1
- package/src/supply-chain/index.js +2 -1
- package/src/supply-chain/sbom.d.ts +1 -1
- package/src/supply-chain/sbom.js +30 -5
- package/src/supply-chain/scanner.js +3 -0
- package/src/testing-evidence/fixture-scope.d.ts +2 -1
- package/src/testing-evidence/fixture-scope.js +4 -2
- package/src/testing-evidence/result.d.ts +2 -1
- package/src/testing-evidence/result.js +18 -4
- package/src/workload-roles/account-baseline.d.ts +11 -0
- package/src/workload-roles/account-baseline.js +253 -16
- package/src/workload-roles/authority.d.ts +3 -1
- package/src/workload-roles/authority.js +48 -2
- package/src/workload-roles/aws-s3-state.js +27 -0
- package/src/workload-roles/boundary.d.ts +2 -1
- package/src/workload-roles/boundary.js +9 -2
- package/src/workload-roles/handler.js +52 -24
- package/src/workload-roles/index.d.ts +10 -17
- package/src/workload-roles/index.js +5 -18
- package/src/workload-roles/injection.js +1 -1
- package/src/workload-roles/provisioner.js +12 -4
- package/src/workload-roles/registration.d.ts +1 -0
- package/src/workload-roles/registration.js +5 -0
- package/src/workload-roles/stage-boundary.js +1 -1
- package/src/workload-roles/standard-slice.js +3 -3
- package/src/workspace-deployment/affected-lifecycle.d.ts +53 -0
- package/src/workspace-deployment/affected-lifecycle.js +84 -0
- package/src/workspace-deployment/identity.d.ts +3 -0
- package/src/workspace-deployment/identity.js +22 -0
- package/src/workspace-deployment/index.d.ts +4 -0
- package/src/workspace-deployment/index.js +8 -0
- package/src/workspace-deployment/migration-evidence.d.ts +29 -0
- package/src/workspace-deployment/migration-evidence.js +83 -0
- package/src/workspace-deployment/migration.d.ts +27 -0
- package/src/workspace-deployment/migration.js +109 -0
- package/src/workspace-deployment/ownership.d.ts +81 -0
- package/src/workspace-deployment/ownership.js +381 -0
- package/src/api-contract/baseline-update.js.map +0 -1
- package/src/api-contract/compatibility.js.map +0 -1
- package/src/api-contract/contract-graph.js.map +0 -1
- package/src/api-contract/evidence.js.map +0 -1
- package/src/api-contract/index.js.map +0 -1
- package/src/api-contract/model.fixtures.d.ts +0 -2
- package/src/api-contract/model.fixtures.js +0 -81
- package/src/api-contract/model.fixtures.js.map +0 -1
- package/src/api-contract/model.js.map +0 -1
- package/src/api-contract/rollout.js.map +0 -1
- package/src/authorization/compatibility.js.map +0 -1
- package/src/authorization/index.js.map +0 -1
- package/src/blueprints/execution.js.map +0 -1
- package/src/blueprints/external-sandbox.js.map +0 -1
- package/src/blueprints/index.js.map +0 -1
- package/src/blueprints/inputs.js.map +0 -1
- package/src/blueprints/manifest.js.map +0 -1
- package/src/blueprints/resolver.js.map +0 -1
- package/src/blueprints/transitions.js.map +0 -1
- package/src/cli/agent-command.js.map +0 -1
- package/src/cli/agent-runtime.js.map +0 -1
- package/src/cli/blueprint-authority.js.map +0 -1
- package/src/cli/blueprint-command.js.map +0 -1
- package/src/cli/bootstrap.js.map +0 -1
- package/src/cli/dev-command.js.map +0 -1
- package/src/cli/main.js.map +0 -1
- package/src/cli/output.js.map +0 -1
- package/src/cli/personal-secret-runtime.js.map +0 -1
- package/src/cli/secret-terminal.js.map +0 -1
- package/src/container-images.js.map +0 -1
- package/src/data-lifecycle/index.js.map +0 -1
- package/src/data-lifecycle/model.fixtures.d.ts +0 -2
- package/src/data-lifecycle/model.fixtures.js +0 -244
- package/src/data-lifecycle/model.fixtures.js.map +0 -1
- package/src/data-lifecycle/model.js.map +0 -1
- package/src/data-lifecycle/sensitive-artifacts.js.map +0 -1
- package/src/data-lifecycle/validation.js.map +0 -1
- package/src/database-retirement/contract.js.map +0 -1
- package/src/database-retirement/index.js.map +0 -1
- package/src/deterministic-order.js.map +0 -1
- package/src/developer-agent/context.js.map +0 -1
- package/src/developer-agent/generator-planning.js.map +0 -1
- package/src/developer-agent/generator-preview.js.map +0 -1
- package/src/developer-agent/git-inspection.js.map +0 -1
- package/src/developer-agent/handoff.js.map +0 -1
- package/src/developer-agent/index.js.map +0 -1
- package/src/developer-agent/model.js.map +0 -1
- package/src/developer-agent/operation-registry.js.map +0 -1
- package/src/developer-agent/ordering.js.map +0 -1
- package/src/developer-agent/planning.js.map +0 -1
- package/src/developer-agent/preview-changes.js.map +0 -1
- package/src/developer-agent/preview-source.js.map +0 -1
- package/src/developer-agent/validation.js.map +0 -1
- package/src/developer-agent/workspace-inspection.js.map +0 -1
- package/src/developer-agent/workspace-path.js.map +0 -1
- package/src/executors/blueprint-trust/blueprint-trust.js.map +0 -1
- package/src/executors/blueprint-verification/blueprint-verification.js.map +0 -1
- package/src/executors/deploy/deploy.js.map +0 -1
- package/src/executors/deployment-plan/deployment-plan.js.map +0 -1
- package/src/executors/dev/dev.js.map +0 -1
- package/src/executors/dev-doctor/dev-doctor.js.map +0 -1
- package/src/executors/dev-setup/dev-setup.js.map +0 -1
- package/src/executors/developer-context/developer-context.js.map +0 -1
- package/src/executors/fleet/fleet.js.map +0 -1
- package/src/executors/foundation-preflight/foundation-preflight.js.map +0 -1
- package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js.map +0 -1
- package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js.map +0 -1
- package/src/executors/github-bootstrap/github-bootstrap.js.map +0 -1
- package/src/executors/infrastructure-plan/infrastructure-plan.js.map +0 -1
- package/src/executors/refresh/refresh.js.map +0 -1
- package/src/executors/remove/remove.js.map +0 -1
- package/src/executors/secret-guardrails/secret-guardrails.js.map +0 -1
- package/src/executors/secret-metadata/secret-metadata.js.map +0 -1
- package/src/executors/sst-lifecycle/logging-preflight.js.map +0 -1
- package/src/executors/sst-lifecycle/run-sst-command.js.map +0 -1
- package/src/executors/sst-lifecycle/secret-preflight.js.map +0 -1
- package/src/executors/unlock/unlock.js.map +0 -1
- package/src/executors/work-markers/work-markers.js.map +0 -1
- package/src/foundation/discovery.js.map +0 -1
- package/src/foundation/index.js.map +0 -1
- package/src/foundation/local-database-contract.js.map +0 -1
- package/src/foundation/logging-preflight.js.map +0 -1
- package/src/foundation/logging.js.map +0 -1
- package/src/foundation/preflight.js.map +0 -1
- package/src/foundation/provision-database-bindings.js.map +0 -1
- package/src/foundation/provision-logical-database.js.map +0 -1
- package/src/foundation/requirements.js.map +0 -1
- package/src/foundation/router-origin-proof.js.map +0 -1
- package/src/foundation/stages.js.map +0 -1
- package/src/frontend-security/index.js.map +0 -1
- package/src/frontend-security/security-headers.js.map +0 -1
- package/src/generators/feature-registry.js.map +0 -1
- package/src/generators/function/function.js.map +0 -1
- package/src/generators/job/job.js.map +0 -1
- package/src/generators/logging-registry.js.map +0 -1
- package/src/generators/post-generation-install.js.map +0 -1
- package/src/generators/preset/generator.js.map +0 -1
- package/src/generators/preset/tool-versions.js.map +0 -1
- package/src/generators/preset/workspace-files-developer-agent.js.map +0 -1
- package/src/generators/preset/workspace-files-foundation-workload-roles.js.map +0 -1
- package/src/generators/preset/workspace-files-performance-cost.js.map +0 -1
- package/src/generators/preset/workspace-files-performance-load.js.map +0 -1
- package/src/generators/preset/workspace-files-workload-role-baseline.js.map +0 -1
- package/src/generators/preset/workspace-files.js.map +0 -1
- package/src/generators/repository-policy/repository-policy.js.map +0 -1
- package/src/generators/service/service.js.map +0 -1
- package/src/generators/slice/built-in-plan-api-contract-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-api-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-async-work-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-authorization-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-deletion-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-lifecycle-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-retention-job-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-retention-launcher-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-rights-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-database-lifecycle-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-foundation-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-frontend-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-infrastructure-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-infrastructure-safety-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-metadata-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-operations-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-performance-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-project-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-release-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-reliability-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-secret-runtime-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-shared.js.map +0 -1
- package/src/generators/slice/built-in-plan-testing-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-verification-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-web-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-workload-role-files.js.map +0 -1
- package/src/generators/slice/built-in-plan.js.map +0 -1
- package/src/generators/slice/slice.js.map +0 -1
- package/src/generators/upgrade/upgrade.js.map +0 -1
- package/src/generators/workload/frozen-container.js.map +0 -1
- package/src/generators/workload/performance-registration.js.map +0 -1
- package/src/generators/workload/reliability-registration.js.map +0 -1
- package/src/generators/workload/service-performance-files.js.map +0 -1
- package/src/generators/workload/workload.js.map +0 -1
- package/src/git-policy/action-pins.js.map +0 -1
- package/src/git-policy/github-bootstrap.js.map +0 -1
- package/src/git-policy/index.js.map +0 -1
- package/src/git-policy/routing.js.map +0 -1
- package/src/index.js.map +0 -1
- package/src/infrastructure-change/convergence.js.map +0 -1
- package/src/infrastructure-change/drift.js.map +0 -1
- package/src/infrastructure-change/index.js.map +0 -1
- package/src/infrastructure-change/model.js.map +0 -1
- package/src/infrastructure-change/normalize.js.map +0 -1
- package/src/infrastructure-change/plan.js.map +0 -1
- package/src/infrastructure-change/risk.js.map +0 -1
- package/src/infrastructure-change/summary.js.map +0 -1
- package/src/local-dev/context-discovery.js.map +0 -1
- package/src/local-dev/contract-validation.js.map +0 -1
- package/src/local-dev/contracts.js.map +0 -1
- package/src/local-dev/control-contract.js.map +0 -1
- package/src/local-dev/dev-command-core.js.map +0 -1
- package/src/local-dev/effects.js.map +0 -1
- package/src/local-dev/environment-inspector.js.map +0 -1
- package/src/local-dev/environment-state.js.map +0 -1
- package/src/local-dev/errors.js.map +0 -1
- package/src/local-dev/fleet-client.js.map +0 -1
- package/src/local-dev/fleet-configuration.js.map +0 -1
- package/src/local-dev/fleet-inventory.js.map +0 -1
- package/src/local-dev/fleet-ipc.js.map +0 -1
- package/src/local-dev/fleet-launcher.js.map +0 -1
- package/src/local-dev/fleet-lock.js.map +0 -1
- package/src/local-dev/fleet-logs.js.map +0 -1
- package/src/local-dev/fleet-process.js.map +0 -1
- package/src/local-dev/fleet-runtime.js.map +0 -1
- package/src/local-dev/fleet-session.js.map +0 -1
- package/src/local-dev/fleet-slice-runner-daemon.js.map +0 -1
- package/src/local-dev/fleet-startup.js.map +0 -1
- package/src/local-dev/fleet-state.js.map +0 -1
- package/src/local-dev/fleet-supervisor-daemon.js.map +0 -1
- package/src/local-dev/fleet-supervisor.js.map +0 -1
- package/src/local-dev/gateway-daemon.js.map +0 -1
- package/src/local-dev/gateway.js.map +0 -1
- package/src/local-dev/identity.js.map +0 -1
- package/src/local-dev/index.js.map +0 -1
- package/src/local-dev/local-context.js.map +0 -1
- package/src/local-dev/nx-workspace.js.map +0 -1
- package/src/local-dev/personal-secret-ledger.js.map +0 -1
- package/src/local-dev/personal-stage.js.map +0 -1
- package/src/local-dev/portless.js.map +0 -1
- package/src/local-dev/ports.js.map +0 -1
- package/src/local-dev/private-state.js.map +0 -1
- package/src/local-dev/process-identity.js.map +0 -1
- package/src/local-dev/proxy.js.map +0 -1
- package/src/local-dev/runtime-paths.js.map +0 -1
- package/src/local-dev/secret-slice-claim.js.map +0 -1
- package/src/local-dev/slice-claim.js.map +0 -1
- package/src/local-dev/slice-runner.js.map +0 -1
- package/src/local-dev/tls.js.map +0 -1
- package/src/ownership/composition.js.map +0 -1
- package/src/ownership/foundation-record.js.map +0 -1
- package/src/ownership/hash.js.map +0 -1
- package/src/ownership/index.js.map +0 -1
- package/src/ownership/json-ownership.js.map +0 -1
- package/src/ownership/record.js.map +0 -1
- package/src/ownership/workspace-record.js.map +0 -1
- package/src/performance/cost-collection.js.map +0 -1
- package/src/performance/cost-controls.js.map +0 -1
- package/src/performance/evidence.js.map +0 -1
- package/src/performance/index.js.map +0 -1
- package/src/performance/load-safety.js.map +0 -1
- package/src/performance/model.js.map +0 -1
- package/src/performance/pr-artifact-budget.js.map +0 -1
- package/src/performance/runner.js.map +0 -1
- package/src/performance/scenario-endpoint.js.map +0 -1
- package/src/performance/scenario.js.map +0 -1
- package/src/performance/test-fixtures.d.ts +0 -7
- package/src/performance/test-fixtures.js +0 -169
- package/src/performance/test-fixtures.js.map +0 -1
- package/src/performance/unit-cost.js.map +0 -1
- package/src/release/index.js.map +0 -1
- package/src/release/lifecycle.js.map +0 -1
- package/src/release/manifest.js.map +0 -1
- package/src/release/rollout.js.map +0 -1
- package/src/release/source-authority.js.map +0 -1
- package/src/repository-policy/aurora-preflight.js.map +0 -1
- package/src/repository-policy/deployment-plan.js.map +0 -1
- package/src/repository-policy/foundation-profile.js.map +0 -1
- package/src/repository-policy/index.js.map +0 -1
- package/src/repository-policy/policy.js.map +0 -1
- package/src/secrets/adapters.js.map +0 -1
- package/src/secrets/aws-control-plane.js.map +0 -1
- package/src/secrets/declaration-verification.js.map +0 -1
- package/src/secrets/guardrails.js.map +0 -1
- package/src/secrets/index.js.map +0 -1
- package/src/secrets/metadata.js.map +0 -1
- package/src/secrets/model.js.map +0 -1
- package/src/secrets/operator.js.map +0 -1
- package/src/secrets/permissions.js.map +0 -1
- package/src/secrets/personal-command.js.map +0 -1
- package/src/secrets/personal-ledger.js.map +0 -1
- package/src/secrets/registry.js.map +0 -1
- package/src/secrets/verification.js.map +0 -1
- package/src/secrets/workload-access.js.map +0 -1
- package/src/supply-chain/action-pins.js.map +0 -1
- package/src/supply-chain/blueprint-trust-git.js.map +0 -1
- package/src/supply-chain/blueprint-trust-process.js.map +0 -1
- package/src/supply-chain/blueprint-trust.js.map +0 -1
- package/src/supply-chain/blueprint-verification-authority.js.map +0 -1
- package/src/supply-chain/blueprint-verification-execution.js.map +0 -1
- package/src/supply-chain/build-output.js.map +0 -1
- package/src/supply-chain/common.js.map +0 -1
- package/src/supply-chain/container-artifact-contract.js.map +0 -1
- package/src/supply-chain/container-release.js.map +0 -1
- package/src/supply-chain/ecr-applicability.js.map +0 -1
- package/src/supply-chain/ecr-scanning.js.map +0 -1
- package/src/supply-chain/ecs-deployed-image.js.map +0 -1
- package/src/supply-chain/errors.js.map +0 -1
- package/src/supply-chain/generated-blueprint-trust-command.js.map +0 -1
- package/src/supply-chain/generated-command.js.map +0 -1
- package/src/supply-chain/generated-container-release-command.js.map +0 -1
- package/src/supply-chain/generated-ecr-collector-command.js.map +0 -1
- package/src/supply-chain/generated-files.js.map +0 -1
- package/src/supply-chain/generated-scanner-command.js.map +0 -1
- package/src/supply-chain/index.js.map +0 -1
- package/src/supply-chain/inventory.js.map +0 -1
- package/src/supply-chain/license-expression.js.map +0 -1
- package/src/supply-chain/pnpm-toolchain-authority.js.map +0 -1
- package/src/supply-chain/policy.js.map +0 -1
- package/src/supply-chain/publication.js.map +0 -1
- package/src/supply-chain/sbom.js.map +0 -1
- package/src/supply-chain/scanner.js.map +0 -1
- package/src/supply-chain/vendored-source.js.map +0 -1
- package/src/testing-evidence/fixture-scope.js.map +0 -1
- package/src/testing-evidence/index.js.map +0 -1
- package/src/testing-evidence/quarantine.js.map +0 -1
- package/src/testing-evidence/result.js.map +0 -1
- package/src/testing-evidence/runner-adapter.js.map +0 -1
- package/src/testing-evidence/sensitive-artifacts.js.map +0 -1
- package/src/validation.js.map +0 -1
- package/src/work-markers/index.js.map +0 -1
- package/src/work-markers/work-markers.js.map +0 -1
- package/src/workload-roles/account-baseline.js.map +0 -1
- package/src/workload-roles/authority.js.map +0 -1
- package/src/workload-roles/aws-adapter.js.map +0 -1
- package/src/workload-roles/aws-internal-handler.js.map +0 -1
- package/src/workload-roles/aws-s3-state.js.map +0 -1
- package/src/workload-roles/aws-sdk-control-plane.js.map +0 -1
- package/src/workload-roles/boundary.js.map +0 -1
- package/src/workload-roles/convergence.js.map +0 -1
- package/src/workload-roles/effective-policy.js.map +0 -1
- package/src/workload-roles/handler.js.map +0 -1
- package/src/workload-roles/history.js.map +0 -1
- package/src/workload-roles/index.js.map +0 -1
- package/src/workload-roles/injection.js.map +0 -1
- package/src/workload-roles/manifest.js.map +0 -1
- package/src/workload-roles/provisioner.js.map +0 -1
- package/src/workload-roles/registration.js.map +0 -1
- package/src/workload-roles/stage-boundary.js.map +0 -1
- package/src/workload-roles/standard-slice.js.map +0 -1
|
@@ -7,8 +7,10 @@ exports.renderWorkspaceFiles = renderWorkspaceFiles;
|
|
|
7
7
|
exports.renderWorkspaceRootFilePlan = renderWorkspaceRootFilePlan;
|
|
8
8
|
exports.renderFoundationFilePlan = renderFoundationFilePlan;
|
|
9
9
|
const sensitive_patterns_1 = require("@empire-builder-kit/runtime/sensitive-patterns");
|
|
10
|
+
const dependency_overrides_js_1 = require("./dependency-overrides.js");
|
|
10
11
|
const index_js_1 = require("../../ownership/index.js");
|
|
11
12
|
const index_js_2 = require("../../frontend-security/index.js");
|
|
13
|
+
const capability_plan_js_1 = require("../../foundation/capability-plan.js");
|
|
12
14
|
const index_js_3 = require("../../git-policy/index.js");
|
|
13
15
|
const index_js_4 = require("../../repository-policy/index.js");
|
|
14
16
|
const index_js_5 = require("../../release/index.js");
|
|
@@ -16,10 +18,14 @@ const index_js_6 = require("../../supply-chain/index.js");
|
|
|
16
18
|
const deterministic_order_js_1 = require("../../deterministic-order.js");
|
|
17
19
|
const tool_versions_js_1 = require("./tool-versions.js");
|
|
18
20
|
const workspace_files_developer_agent_js_1 = require("./workspace-files-developer-agent.js");
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
21
|
+
const workspace_files_ci_acceptance_js_1 = require("./workspace-files-ci-acceptance.js");
|
|
22
|
+
const workspace_files_ci_install_js_1 = require("./workspace-files-ci-install.js");
|
|
23
|
+
const workspace_files_adopter_skills_js_1 = require("./workspace-files-adopter-skills.js");
|
|
24
|
+
const workspace_files_ecr_inspector_js_1 = require("./workspace-files-ecr-inspector.js");
|
|
25
|
+
const workspace_files_thin_deploy_js_1 = require("./workspace-files-thin-deploy.js");
|
|
26
|
+
const workspace_files_thin_docs_js_1 = require("./workspace-files-thin-docs.js");
|
|
27
|
+
const workspace_files_thin_release_js_1 = require("./workspace-files-thin-release.js");
|
|
28
|
+
const workspace_files_stage_authority_js_1 = require("./workspace-files-stage-authority.js");
|
|
23
29
|
const GENERATED_CREDENTIAL_SHAPE_PATTERNS = `[${sensitive_patterns_1.CREDENTIAL_SHAPE_DEFINITIONS.map(({ pattern }) => `new RegExp(${JSON.stringify(pattern.source)}, ${JSON.stringify(pattern.flags)})`).join(',')}]`;
|
|
24
30
|
const GENERATED_RELEASE_ROLLOUT_PLAN_ID_PATTERN = JSON.stringify(index_js_5.RELEASE_ROLLOUT_PLAN_ID_PATTERN_SOURCE);
|
|
25
31
|
exports.BLUEPRINT_TRUST_AUTHORITY_PATH = 'tools/blueprint-trust-check.mjs';
|
|
@@ -60,11 +66,12 @@ exports.PLAN_PROVIDER_REFRESH_GLOBAL_READ_ACTIONS = [
|
|
|
60
66
|
'rds:DescribeDBProxies',
|
|
61
67
|
'rds:DescribeDBSubnetGroups',
|
|
62
68
|
];
|
|
63
|
-
function validatePlanProviderRefreshPolicy(value, region, account) {
|
|
69
|
+
function validatePlanProviderRefreshPolicy(value, region, account, stage) {
|
|
64
70
|
if (!Array.isArray(value) ||
|
|
65
71
|
value.length === 0 ||
|
|
66
72
|
!/^[a-z]{2}(?:-gov)?-[a-z]+-\d$/.test(region) ||
|
|
67
|
-
!/^\d{12}$/.test(account)
|
|
73
|
+
!/^\d{12}$/.test(account) ||
|
|
74
|
+
!['production', 'staging'].includes(stage)) {
|
|
68
75
|
throw new Error('Provider refresh policy must be a non-empty exact statement array for one account and region.');
|
|
69
76
|
}
|
|
70
77
|
const allowed = new Set(exports.PLAN_PROVIDER_REFRESH_READ_ACTIONS);
|
|
@@ -117,6 +124,14 @@ function validatePlanProviderRefreshPolicy(value, region, account) {
|
|
|
117
124
|
if (resources.some((resource) => {
|
|
118
125
|
const arn = resource.split(':');
|
|
119
126
|
const target = arn.slice(5).join(':');
|
|
127
|
+
const boundedPathWildcard = target.endsWith('/*') && !/[*?]/.test(target.slice(0, -2));
|
|
128
|
+
const boundedLambdaStagePrefix = arn[2] === 'lambda' &&
|
|
129
|
+
actions.every((action) => action.startsWith('lambda:') && !global.has(action)) &&
|
|
130
|
+
new RegExp('^function:(?:' +
|
|
131
|
+
stage +
|
|
132
|
+
'-|[A-Za-z0-9][A-Za-z0-9_-]*-' +
|
|
133
|
+
stage +
|
|
134
|
+
'-)[A-Za-z0-9_-]*\\*$').test(target);
|
|
120
135
|
return (arn[0] !== 'arn' ||
|
|
121
136
|
arn[1] !== 'aws' ||
|
|
122
137
|
!/^[a-z0-9-]+$/.test(arn[2] ?? '') ||
|
|
@@ -124,7 +139,8 @@ function validatePlanProviderRefreshPolicy(value, region, account) {
|
|
|
124
139
|
!['', account].includes(arn[4] ?? '') ||
|
|
125
140
|
!target ||
|
|
126
141
|
(/[*?]/.test(target) &&
|
|
127
|
-
|
|
142
|
+
!boundedPathWildcard &&
|
|
143
|
+
!boundedLambdaStagePrefix));
|
|
128
144
|
})) {
|
|
129
145
|
throw new Error(`Provider refresh statement ${index} is not bounded to exact targets in the selected account and region.`);
|
|
130
146
|
}
|
|
@@ -161,6 +177,51 @@ const TOOL_VERSIONS = {
|
|
|
161
177
|
function json(value) {
|
|
162
178
|
return `${JSON.stringify(value, null, 2)}\n`;
|
|
163
179
|
}
|
|
180
|
+
function indentWorkflowScript(source, spaces) {
|
|
181
|
+
const prefix = ' '.repeat(spaces);
|
|
182
|
+
return source
|
|
183
|
+
.trim()
|
|
184
|
+
.split('\n')
|
|
185
|
+
.map((line) => `${prefix}${line}`)
|
|
186
|
+
.join('\n');
|
|
187
|
+
}
|
|
188
|
+
function protectedSecretPolicyDerivationScript() {
|
|
189
|
+
return `node --input-type=module <<'NODE'
|
|
190
|
+
import { appendFileSync } from 'node:fs';
|
|
191
|
+
import {
|
|
192
|
+
configuredDeploymentAccountForStage,
|
|
193
|
+
configuredDeploymentRegion,
|
|
194
|
+
isActiveDeploymentStage,
|
|
195
|
+
readRepositoryOperatingPolicy,
|
|
196
|
+
} from '@empire-builder-kit/nx/repository-policy';
|
|
197
|
+
|
|
198
|
+
const output = process.env.GITHUB_OUTPUT?.trim() ?? '';
|
|
199
|
+
const renderedProfile = process.env.EBK_RENDERED_PROFILE?.trim() ?? '';
|
|
200
|
+
const stage = process.env.EBK_STAGE?.trim() ?? '';
|
|
201
|
+
if (!output) throw new Error('Secret workflow authority output is unavailable.');
|
|
202
|
+
const policy = readRepositoryOperatingPolicy(process.cwd());
|
|
203
|
+
if (policy.deploymentProfile !== renderedProfile) {
|
|
204
|
+
throw new Error('Generated and committed deployment profiles differ.');
|
|
205
|
+
}
|
|
206
|
+
if (!isActiveDeploymentStage(policy.deploymentProfile, stage)) {
|
|
207
|
+
throw new Error('Secret workflow stage is inactive for the committed deployment profile.');
|
|
208
|
+
}
|
|
209
|
+
configuredDeploymentAccountForStage(
|
|
210
|
+
policy.deploymentProfile,
|
|
211
|
+
policy.deploymentAccounts,
|
|
212
|
+
stage,
|
|
213
|
+
);
|
|
214
|
+
const accounts = policy.deploymentAccounts;
|
|
215
|
+
const region = configuredDeploymentRegion(policy.deploymentRegion);
|
|
216
|
+
appendFileSync(output, [
|
|
217
|
+
'deployment_region=' + region,
|
|
218
|
+
'development_account_id=' + (accounts.development ?? ''),
|
|
219
|
+
'staging_account_id=' + (accounts.staging ?? ''),
|
|
220
|
+
'production_account_id=' + (accounts.production ?? ''),
|
|
221
|
+
'',
|
|
222
|
+
].join('\\n'));
|
|
223
|
+
NODE`;
|
|
224
|
+
}
|
|
164
225
|
function planApplicationBindingCheck() {
|
|
165
226
|
return ` node - <<'NODE'
|
|
166
227
|
const declared = JSON.parse(process.env.AWS_PLAN_SST_APPS_JSON ?? '');
|
|
@@ -474,6 +535,8 @@ function planRoleAuditStep(branch, mode = 'produce') {
|
|
|
474
535
|
EBK_PLAN_ENVIRONMENT: infrastructure-plan-${stage}
|
|
475
536
|
EBK_PLAN_ROLE_ARN: \${{ env.AWS_PLAN_ROLE_ARN }}
|
|
476
537
|
EBK_PLAN_REPOSITORY: \${{ github.repository }}
|
|
538
|
+
EBK_PLAN_REPOSITORY_ID: \${{ github.repository_id }}
|
|
539
|
+
EBK_PLAN_REPOSITORY_OWNER_ID: \${{ github.repository_owner_id }}
|
|
477
540
|
run: |
|
|
478
541
|
role_name="\${EBK_PLAN_ROLE_ARN##*/}"
|
|
479
542
|
test "$(aws sts get-caller-identity --query Account --output text)" = "$AWS_EXPECTED_ACCOUNT_ID" ||
|
|
@@ -506,8 +569,23 @@ function planRoleAuditStep(branch, mode = 'produce') {
|
|
|
506
569
|
const role = JSON.parse(readFileSync(process.argv[2], 'utf8')).Role;
|
|
507
570
|
const policy = JSON.parse(readFileSync(process.argv[3], 'utf8')).PolicyDocument;
|
|
508
571
|
const account = process.env.AWS_EXPECTED_ACCOUNT_ID;
|
|
572
|
+
const repository = process.env.EBK_PLAN_REPOSITORY ?? '';
|
|
573
|
+
const repositoryId = process.env.EBK_PLAN_REPOSITORY_ID ?? '';
|
|
574
|
+
const repositoryOwnerId = process.env.EBK_PLAN_REPOSITORY_OWNER_ID ?? '';
|
|
509
575
|
const stateBucketArn = process.env.AWS_PLAN_STATE_BUCKET_ARN;
|
|
510
576
|
const stateKmsKeyArn = process.env.AWS_PLAN_STATE_KMS_KEY_ARN;
|
|
577
|
+
if (
|
|
578
|
+
!/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\\/[A-Za-z0-9_.-]{1,100}$/.test(
|
|
579
|
+
repository,
|
|
580
|
+
) ||
|
|
581
|
+
!/^[1-9][0-9]*$/.test(repositoryOwnerId) ||
|
|
582
|
+
!/^[1-9][0-9]*$/.test(repositoryId)
|
|
583
|
+
) throw new Error('Exact immutable GitHub repository identity is required.');
|
|
584
|
+
const [repositoryOwner, repositoryName] = repository.split('/');
|
|
585
|
+
const oidcSubject =
|
|
586
|
+
'repo:' + repositoryOwner + '@' + repositoryOwnerId + '/' +
|
|
587
|
+
repositoryName + '@' + repositoryId + ':environment:' +
|
|
588
|
+
process.env.EBK_PLAN_ENVIRONMENT;
|
|
511
589
|
const parseExactArnArray = (name, pattern) => {
|
|
512
590
|
let value;
|
|
513
591
|
try {
|
|
@@ -551,6 +629,7 @@ function planRoleAuditStep(branch, mode = 'produce') {
|
|
|
551
629
|
'AWS_PLAN_GENERATED_SECRET_ARNS_JSON',
|
|
552
630
|
new RegExp('^arn:aws:secretsmanager:' + process.env.AWS_REGION + ':' + account + ':secret:ebk/'),
|
|
553
631
|
);
|
|
632
|
+
const stateStage = process.env.EBK_PLAN_BRANCH === 'main' ? 'production' : 'staging';
|
|
554
633
|
let providerRefreshPolicy;
|
|
555
634
|
try {
|
|
556
635
|
providerRefreshPolicy = JSON.parse(
|
|
@@ -614,6 +693,23 @@ function planRoleAuditStep(branch, mode = 'produce') {
|
|
|
614
693
|
for (const resource of resources) {
|
|
615
694
|
const arn = resource.split(':');
|
|
616
695
|
const target = arn.slice(5).join(':');
|
|
696
|
+
const boundedPathWildcard =
|
|
697
|
+
target.endsWith('/*') &&
|
|
698
|
+
!/[*?]/.test(target.slice(0, -2));
|
|
699
|
+
const boundedLambdaStagePrefix =
|
|
700
|
+
arn[2] === 'lambda' &&
|
|
701
|
+
actions.every(
|
|
702
|
+
(action) =>
|
|
703
|
+
action.startsWith('lambda:') &&
|
|
704
|
+
!globalProviderReadActions.has(action),
|
|
705
|
+
) &&
|
|
706
|
+
new RegExp(
|
|
707
|
+
'^function:(?:' +
|
|
708
|
+
stateStage +
|
|
709
|
+
'-|[A-Za-z0-9][A-Za-z0-9_-]*-' +
|
|
710
|
+
stateStage +
|
|
711
|
+
'-)[A-Za-z0-9_-]*\\\\*$',
|
|
712
|
+
).test(target);
|
|
617
713
|
if (
|
|
618
714
|
arn[0] !== 'arn' ||
|
|
619
715
|
arn[1] !== 'aws' ||
|
|
@@ -622,7 +718,8 @@ function planRoleAuditStep(branch, mode = 'produce') {
|
|
|
622
718
|
!['', account].includes(arn[4] ?? '') ||
|
|
623
719
|
!target ||
|
|
624
720
|
(/[*?]/.test(target) &&
|
|
625
|
-
|
|
721
|
+
!boundedPathWildcard &&
|
|
722
|
+
!boundedLambdaStagePrefix)
|
|
626
723
|
) throw new Error('Provider refresh statement ' + index + ' has a target outside the selected account and region.');
|
|
627
724
|
}
|
|
628
725
|
}
|
|
@@ -637,7 +734,6 @@ function planRoleAuditStep(branch, mode = 'produce') {
|
|
|
637
734
|
).sort((left, right) =>
|
|
638
735
|
left.Sid < right.Sid ? -1 : left.Sid > right.Sid ? 1 : 0,
|
|
639
736
|
);
|
|
640
|
-
const stateStage = process.env.EBK_PLAN_BRANCH === 'main' ? 'production' : 'staging';
|
|
641
737
|
if (
|
|
642
738
|
stateKmsKeyArn &&
|
|
643
739
|
stateKmsKeyArn !==
|
|
@@ -681,7 +777,7 @@ function planRoleAuditStep(branch, mode = 'produce') {
|
|
|
681
777
|
Action: 'sts:AssumeRoleWithWebIdentity',
|
|
682
778
|
Condition: { StringEquals: {
|
|
683
779
|
'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com',
|
|
684
|
-
'token.actions.githubusercontent.com:sub':
|
|
780
|
+
'token.actions.githubusercontent.com:sub': oidcSubject,
|
|
685
781
|
}},
|
|
686
782
|
}],
|
|
687
783
|
};
|
|
@@ -703,11 +799,24 @@ function planRoleAuditStep(branch, mode = 'produce') {
|
|
|
703
799
|
Sid: 'ObserveInfrastructureIdentity',
|
|
704
800
|
Effect: 'Allow',
|
|
705
801
|
Action: [
|
|
706
|
-
'
|
|
802
|
+
'tag:GetResources',
|
|
707
803
|
'sts:GetCallerIdentity',
|
|
708
804
|
],
|
|
709
805
|
Resource: '*',
|
|
710
806
|
},
|
|
807
|
+
{
|
|
808
|
+
Sid: 'ObserveStageSecretVersionIdentity',
|
|
809
|
+
Effect: 'Allow',
|
|
810
|
+
Action: 'secretsmanager:ListSecretVersionIds',
|
|
811
|
+
Resource:
|
|
812
|
+
'arn:aws:secretsmanager:' + process.env.AWS_REGION + ':' +
|
|
813
|
+
account + ':secret:*',
|
|
814
|
+
Condition: {
|
|
815
|
+
StringEquals: {
|
|
816
|
+
'aws:ResourceTag/ebk:stage': stateStage,
|
|
817
|
+
},
|
|
818
|
+
},
|
|
819
|
+
},
|
|
711
820
|
{
|
|
712
821
|
Sid: 'ValidatePlannedIamPolicies',
|
|
713
822
|
Effect: 'Allow',
|
|
@@ -782,7 +891,10 @@ function planRoleAuditStep(branch, mode = 'produce') {
|
|
|
782
891
|
generatedSecretArns,
|
|
783
892
|
providerRefreshStatements,
|
|
784
893
|
region: process.env.AWS_REGION,
|
|
785
|
-
repository
|
|
894
|
+
repository,
|
|
895
|
+
repositoryId,
|
|
896
|
+
repositoryOwnerId,
|
|
897
|
+
oidcSubject,
|
|
786
898
|
role: {
|
|
787
899
|
arn: role.Arn,
|
|
788
900
|
policy: canonical(policy),
|
|
@@ -826,11 +938,17 @@ function planRoleAuditJob(stage, configureAwsAction) {
|
|
|
826
938
|
const production = stage === 'production';
|
|
827
939
|
const branch = production ? 'main' : 'develop';
|
|
828
940
|
const prefix = production ? 'AWS_PRODUCTION_PLAN' : 'AWS_STAGING_PLAN';
|
|
941
|
+
const profileAdmission = production
|
|
942
|
+
? ''
|
|
943
|
+
: ` (github.event_name == 'workflow_dispatch' ||\n needs.deployment-mode.outputs.profile != 'starter') &&\n`;
|
|
829
944
|
return ` infrastructure-plan-role-audit-${stage}:
|
|
830
945
|
name: audit ${stage} infrastructure plan role
|
|
831
|
-
needs: route
|
|
946
|
+
needs: [route, deployment-mode]
|
|
832
947
|
if: |
|
|
833
|
-
|
|
948
|
+
always() &&
|
|
949
|
+
needs.route.result == 'success' &&
|
|
950
|
+
needs.deployment-mode.result == 'success' &&
|
|
951
|
+
${profileAdmission} github.ref == 'refs/heads/${branch}' &&
|
|
834
952
|
(github.event_name == 'push' && 'deploy' || inputs.action) != 'unlock'
|
|
835
953
|
runs-on: ubuntu-latest
|
|
836
954
|
environment: infrastructure-plan-${stage}
|
|
@@ -938,6 +1056,7 @@ const SUPPLY_CHAIN_WORKSPACE_FILE_PLAN = (0, index_js_6.renderSupplyChainWorkspa
|
|
|
938
1056
|
generatedAt: '1970-01-01T00:00:00.000Z',
|
|
939
1057
|
});
|
|
940
1058
|
const WORKSPACE_SEEDED_PATHS = new Set([
|
|
1059
|
+
dependency_overrides_js_1.DEPENDENCY_OVERRIDES_PATH,
|
|
941
1060
|
...SUPPLY_CHAIN_WORKSPACE_FILE_PLAN.seeded,
|
|
942
1061
|
...workspace_files_developer_agent_js_1.DEVELOPER_AGENT_WORKSPACE_SEEDED_PATHS,
|
|
943
1062
|
'.github/CODEOWNERS',
|
|
@@ -947,6 +1066,8 @@ const WORKSPACE_SEEDED_PATHS = new Set([
|
|
|
947
1066
|
'docs/architecture.md',
|
|
948
1067
|
'docs/git-workflow.md',
|
|
949
1068
|
'docs/repository-operating-policy.md',
|
|
1069
|
+
'docs/stage-deployment-authority.md',
|
|
1070
|
+
'docs/workspace-deployment-ownership.md',
|
|
950
1071
|
'docs/work-markers.md',
|
|
951
1072
|
'ebk.repository-policy.json',
|
|
952
1073
|
]);
|
|
@@ -964,21 +1085,23 @@ exports.WORKSPACE_PROJECT_MANAGED_JSON_POINTERS = [
|
|
|
964
1085
|
'/root',
|
|
965
1086
|
'/tags',
|
|
966
1087
|
'/targets/blueprint-trust-check',
|
|
1088
|
+
'/targets/blueprint-trust-check-main',
|
|
967
1089
|
'/targets/blueprint-trust-check-protected',
|
|
968
1090
|
'/targets/developer-context',
|
|
969
1091
|
'/targets/deployment-plan',
|
|
1092
|
+
'/targets/deploy-local',
|
|
970
1093
|
'/targets/dev',
|
|
971
1094
|
'/targets/dev-doctor',
|
|
972
1095
|
'/targets/dev-setup',
|
|
973
1096
|
'/targets/github-bootstrap',
|
|
974
1097
|
'/targets/install-git-hooks',
|
|
1098
|
+
'/targets/stage-authority-render',
|
|
975
1099
|
'/targets/supply-chain-check',
|
|
976
1100
|
'/targets/supply-chain-check-protected',
|
|
977
1101
|
'/targets/supply-chain-scan',
|
|
978
1102
|
'/targets/verify',
|
|
1103
|
+
'/targets/verify-proposed',
|
|
979
1104
|
'/targets/work-markers',
|
|
980
|
-
'/targets/workload-role-candidate-report',
|
|
981
|
-
'/targets/workload-role-provisioner-build',
|
|
982
1105
|
'/targets/workspace-health',
|
|
983
1106
|
];
|
|
984
1107
|
const FOUNDATION_SEEDED_PATHS = new Set([
|
|
@@ -986,6 +1109,10 @@ const FOUNDATION_SEEDED_PATHS = new Set([
|
|
|
986
1109
|
'docs/costs.md',
|
|
987
1110
|
'docs/runbooks.md',
|
|
988
1111
|
'docs/security.md',
|
|
1112
|
+
'performance/account-cost-policy.json',
|
|
1113
|
+
'security/router-waf-blocking-rehearsal.json',
|
|
1114
|
+
'security/router-waf-live-evidence.json',
|
|
1115
|
+
'security/router-waf.json',
|
|
989
1116
|
]);
|
|
990
1117
|
function createFoundationOwnership(files, frameworkVersion) {
|
|
991
1118
|
const paths = Object.keys(files)
|
|
@@ -1048,7 +1175,48 @@ function createWorkspaceOwnership(files, frameworkVersion) {
|
|
|
1048
1175
|
}
|
|
1049
1176
|
function renderWorkspaceFiles(options) {
|
|
1050
1177
|
const { frameworkVersion, name } = options;
|
|
1178
|
+
const foundationPlan = (0, capability_plan_js_1.planFoundationCapabilities)({
|
|
1179
|
+
current: options.foundationDescriptor,
|
|
1180
|
+
requirements: options.foundationRequirements ?? [],
|
|
1181
|
+
});
|
|
1182
|
+
const dependencyOverrides = (0, dependency_overrides_js_1.parseDependencyOverrides)(options.dependencyOverrides === undefined
|
|
1183
|
+
? { schemaVersion: 1, overrides: {} }
|
|
1184
|
+
: options.dependencyOverrides);
|
|
1185
|
+
const deploymentProfile = options.deploymentProfile ?? 'starter';
|
|
1186
|
+
const deliveryLane = options.deliveryLane ?? 'protected-pair';
|
|
1187
|
+
if (deliveryLane === 'trunk' && deploymentProfile !== 'starter') {
|
|
1188
|
+
throw new Error('The trunk delivery lane requires the starter deployment profile.');
|
|
1189
|
+
}
|
|
1190
|
+
const trunkLane = deliveryLane === 'trunk';
|
|
1191
|
+
const productionApprovalMode = options.productionApprovalMode ?? 'promotion-pr';
|
|
1192
|
+
if (productionApprovalMode === 'trunk-promotion' &&
|
|
1193
|
+
(deploymentProfile === 'starter' || trunkLane)) {
|
|
1194
|
+
throw new Error('trunk-promotion requires an isolated deployment profile on the protected pair.');
|
|
1195
|
+
}
|
|
1196
|
+
const trunkPromotion = productionApprovalMode === 'trunk-promotion';
|
|
1197
|
+
const protectedBranches = trunkLane ? '[main]' : '[develop, main]';
|
|
1198
|
+
const activeStages = (0, index_js_4.activeDeploymentStages)(deploymentProfile);
|
|
1199
|
+
const protectedWorkflowStageChoices = deploymentProfile === 'starter'
|
|
1200
|
+
? 'development, production'
|
|
1201
|
+
: deploymentProfile === 'production-isolated'
|
|
1202
|
+
? 'production'
|
|
1203
|
+
: 'staging, production';
|
|
1204
|
+
const ecrInspectorWorkflowStageChoices = deploymentProfile === 'starter'
|
|
1205
|
+
? 'production'
|
|
1206
|
+
: protectedWorkflowStageChoices;
|
|
1207
|
+
const rehearsalStage = (0, index_js_4.productionRehearsalStage)(deploymentProfile);
|
|
1208
|
+
const protectedWorkflowStageBranchCases = `${rehearsalStage}:refs/heads/develop|production:refs/heads/main`;
|
|
1209
|
+
const workspaceDeploymentId = options.workspaceDeploymentId ??
|
|
1210
|
+
`ebkws-${(0, index_js_1.hashGeneratedContent)(`workspace-deployment-id:${name}`).slice(0, 32)}`;
|
|
1211
|
+
const initialRepositoryPolicy = (0, index_js_4.createIncubationRepositoryPolicy)(deploymentProfile, workspaceDeploymentId);
|
|
1212
|
+
initialRepositoryPolicy.deploymentAccounts = options.deploymentAccounts ?? {
|
|
1213
|
+
development: null,
|
|
1214
|
+
production: null,
|
|
1215
|
+
staging: null,
|
|
1216
|
+
};
|
|
1217
|
+
initialRepositoryPolicy.deploymentRegion = options.deploymentRegion ?? null;
|
|
1051
1218
|
const developerAgentFilePlan = (0, workspace_files_developer_agent_js_1.renderDeveloperAgentWorkspaceFilePlan)({
|
|
1219
|
+
deploymentProfile,
|
|
1052
1220
|
frameworkVersion,
|
|
1053
1221
|
name,
|
|
1054
1222
|
});
|
|
@@ -1059,7 +1227,38 @@ function renderWorkspaceFiles(options) {
|
|
|
1059
1227
|
const pnpmAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.pnpmSetup);
|
|
1060
1228
|
const setupNodeAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.setupNode);
|
|
1061
1229
|
const uploadArtifactAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.uploadArtifact);
|
|
1230
|
+
const cacheAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.cache);
|
|
1231
|
+
const nxSetShasAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.nxSetShas);
|
|
1232
|
+
// GitHub scopes cache reads to the current branch and its base, so protected
|
|
1233
|
+
// pushes never restore entries written by pull-request runs.
|
|
1234
|
+
const nxCacheSteps = ` - name: Restore the Nx task cache
|
|
1235
|
+
uses: ${cacheAction}
|
|
1236
|
+
with:
|
|
1237
|
+
path: .nx/cache
|
|
1238
|
+
key: nx-\${{ runner.os }}-\${{ hashFiles('pnpm-lock.yaml') }}-\${{ github.sha }}
|
|
1239
|
+
restore-keys: |
|
|
1240
|
+
nx-\${{ runner.os }}-\${{ hashFiles('pnpm-lock.yaml') }}-`;
|
|
1241
|
+
// Isolated profiles bind the staging release candidate when promoting to
|
|
1242
|
+
// production, so a main push never needs a rebuilt candidate.
|
|
1243
|
+
const releaseCandidateBranchCondition = deploymentProfile === 'starter'
|
|
1244
|
+
? ''
|
|
1245
|
+
: " &&\n github.ref != 'refs/heads/main'";
|
|
1246
|
+
// In the trunk lane no pull request is a protected route: main is the only
|
|
1247
|
+
// protected branch and every feature pull request targets it.
|
|
1248
|
+
const protectedDevelopPromotion = trunkLane
|
|
1249
|
+
? 'false'
|
|
1250
|
+
: "github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.head.ref == 'develop' && github.event.pull_request.base.ref == 'main'";
|
|
1251
|
+
const protectedMainSync = trunkLane
|
|
1252
|
+
? 'false'
|
|
1253
|
+
: "github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.head.ref == 'main' && github.event.pull_request.base.ref == 'develop'";
|
|
1254
|
+
const protectedPush = trunkLane
|
|
1255
|
+
? "github.event_name == 'push' && github.ref == 'refs/heads/main'"
|
|
1256
|
+
: "github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop')";
|
|
1257
|
+
const protectedDeploymentRouteJobName = 'protected deployment route';
|
|
1258
|
+
const stagingApplyJobPrefix = 'apply staging plan ';
|
|
1062
1259
|
const files = {
|
|
1260
|
+
...(0, workspace_files_ci_acceptance_js_1.renderWorkspaceCiAcceptanceFiles)(),
|
|
1261
|
+
...(0, workspace_files_ci_install_js_1.renderWorkspaceCiInstallFiles)(),
|
|
1063
1262
|
'.node-version': `${tool_versions_js_1.WORKSPACE_NODE_VERSION}\n`,
|
|
1064
1263
|
'.github/CODEOWNERS': `# Replace this placeholder with the GitHub user or team that owns protected source.
|
|
1065
1264
|
# GitHub bootstrap remains blocked until a non-placeholder catch-all owner is committed.
|
|
@@ -1069,21 +1268,83 @@ function renderWorkspaceFiles(options) {
|
|
|
1069
1268
|
|
|
1070
1269
|
on:
|
|
1071
1270
|
pull_request:
|
|
1072
|
-
branches:
|
|
1271
|
+
branches: ${protectedBranches}
|
|
1073
1272
|
push:
|
|
1074
|
-
branches:
|
|
1273
|
+
branches: ${protectedBranches}
|
|
1075
1274
|
workflow_dispatch:
|
|
1076
1275
|
|
|
1077
1276
|
concurrency:
|
|
1078
|
-
group: verify-\${{ github.ref }}
|
|
1079
|
-
cancel-in-progress:
|
|
1277
|
+
group: verify-\${{ github.event_name == 'push' && github.sha || github.ref }}
|
|
1278
|
+
cancel-in-progress: \${{ github.event_name != 'push' }}
|
|
1080
1279
|
|
|
1081
1280
|
permissions:
|
|
1281
|
+
actions: read
|
|
1082
1282
|
contents: read
|
|
1283
|
+
pull-requests: read
|
|
1284
|
+
|
|
1285
|
+
env:
|
|
1286
|
+
EBK_CI_CANDIDATE_SHA: \${{ github.event.pull_request.head.sha || github.sha }}
|
|
1083
1287
|
|
|
1084
1288
|
jobs:
|
|
1289
|
+
acceptance:
|
|
1290
|
+
name: accepted merge evidence
|
|
1291
|
+
if: ${protectedPush}
|
|
1292
|
+
runs-on: ubuntu-latest
|
|
1293
|
+
outputs:
|
|
1294
|
+
history_only: \${{ steps.resolve.outputs.history_only == 'true' && steps.restore.outcome == 'success' && steps.verify.outcome == 'success' }}
|
|
1295
|
+
reused: \${{ steps.resolve.outputs.reused == 'true' && steps.restore.outcome == 'success' && steps.verify.outcome == 'success' }}
|
|
1296
|
+
route: \${{ steps.resolve.outputs.reused == 'true' && steps.restore.outcome == 'success' && steps.verify.outcome == 'success' && steps.resolve.outputs.route || '' }}
|
|
1297
|
+
steps:
|
|
1298
|
+
- name: Check out immutable merged candidate
|
|
1299
|
+
if: ${protectedPush}
|
|
1300
|
+
uses: ${checkoutAction}
|
|
1301
|
+
with:
|
|
1302
|
+
fetch-depth: 0
|
|
1303
|
+
ref: \${{ github.sha }}
|
|
1304
|
+
- name: Resolve exact protected-route acceptance
|
|
1305
|
+
if: ${protectedPush}
|
|
1306
|
+
id: resolve
|
|
1307
|
+
env:
|
|
1308
|
+
GH_TOKEN: \${{ github.token }}
|
|
1309
|
+
run: node tools/ci/workspace-acceptance.mjs resolve
|
|
1310
|
+
- name: Restore exact protected-route acceptance
|
|
1311
|
+
if: steps.resolve.outputs.reused == 'true'
|
|
1312
|
+
id: restore
|
|
1313
|
+
continue-on-error: true
|
|
1314
|
+
env:
|
|
1315
|
+
EBK_ACCEPTANCE_ARTIFACT_DIGEST: \${{ steps.resolve.outputs.artifact_digest }}
|
|
1316
|
+
EBK_ACCEPTANCE_ARTIFACT_ID: \${{ steps.resolve.outputs.artifact_id }}
|
|
1317
|
+
EBK_ACCEPTANCE_ARTIFACT_SIZE: \${{ steps.resolve.outputs.artifact_size }}
|
|
1318
|
+
GH_TOKEN: \${{ github.token }}
|
|
1319
|
+
run: >-
|
|
1320
|
+
node tools/ci/workspace-acceptance.mjs download
|
|
1321
|
+
.ebk/evidence/reused-acceptance
|
|
1322
|
+
"$EBK_ACCEPTANCE_ARTIFACT_ID"
|
|
1323
|
+
"$EBK_ACCEPTANCE_ARTIFACT_DIGEST"
|
|
1324
|
+
"$EBK_ACCEPTANCE_ARTIFACT_SIZE"
|
|
1325
|
+
- name: Verify exact merged-tree acceptance
|
|
1326
|
+
if: steps.resolve.outputs.reused == 'true' && steps.restore.outcome == 'success'
|
|
1327
|
+
id: verify
|
|
1328
|
+
continue-on-error: true
|
|
1329
|
+
env:
|
|
1330
|
+
EBK_ACCEPTANCE_BASE: \${{ steps.resolve.outputs.base_commit }}
|
|
1331
|
+
EBK_ACCEPTANCE_HEAD: \${{ steps.resolve.outputs.head_commit }}
|
|
1332
|
+
EBK_ACCEPTANCE_ROUTE: \${{ steps.resolve.outputs.route }}
|
|
1333
|
+
EBK_ACCEPTANCE_RUN_ATTEMPT: \${{ steps.resolve.outputs.run_attempt }}
|
|
1334
|
+
EBK_ACCEPTANCE_RUN_ID: \${{ steps.resolve.outputs.run_id }}
|
|
1335
|
+
run: >-
|
|
1336
|
+
node tools/ci/workspace-acceptance.mjs verify
|
|
1337
|
+
.ebk/evidence/reused-acceptance/manifest.json
|
|
1338
|
+
"$EBK_ACCEPTANCE_HEAD"
|
|
1339
|
+
"$EBK_ACCEPTANCE_BASE"
|
|
1340
|
+
"$EBK_ACCEPTANCE_ROUTE"
|
|
1341
|
+
"$EBK_ACCEPTANCE_RUN_ID"
|
|
1342
|
+
"$EBK_ACCEPTANCE_RUN_ATTEMPT"
|
|
1343
|
+
|
|
1085
1344
|
policy:
|
|
1086
1345
|
name: policy
|
|
1346
|
+
needs: acceptance
|
|
1347
|
+
if: always() && needs.acceptance.outputs.reused != 'true'
|
|
1087
1348
|
runs-on: ubuntu-latest
|
|
1088
1349
|
steps:
|
|
1089
1350
|
- uses: ${checkoutAction}
|
|
@@ -1095,31 +1356,118 @@ jobs:
|
|
|
1095
1356
|
with:
|
|
1096
1357
|
node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
|
|
1097
1358
|
cache: pnpm
|
|
1098
|
-
- run:
|
|
1359
|
+
- run: node tools/ci/install.mjs
|
|
1099
1360
|
- run: node tools/ebk-git-policy.mjs
|
|
1361
|
+
env:
|
|
1362
|
+
EBK_HEAD_REPOSITORY: \${{ github.event.pull_request.head.repo.full_name }}
|
|
1100
1363
|
|
|
1101
1364
|
verify:
|
|
1102
1365
|
name: verify
|
|
1366
|
+
needs: acceptance
|
|
1367
|
+
if: always() && needs.acceptance.outputs.reused != 'true'
|
|
1103
1368
|
runs-on: ubuntu-latest
|
|
1104
1369
|
steps:
|
|
1105
1370
|
- uses: ${checkoutAction}
|
|
1371
|
+
with:
|
|
1372
|
+
fetch-depth: 0
|
|
1373
|
+
ref: \${{ github.event.pull_request.head.sha || github.sha }}
|
|
1106
1374
|
- uses: ${pnpmAction}
|
|
1107
1375
|
- uses: ${setupNodeAction}
|
|
1108
1376
|
with:
|
|
1109
1377
|
node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
|
|
1110
1378
|
cache: pnpm
|
|
1111
|
-
- run:
|
|
1112
|
-
|
|
1113
|
-
- run: pnpm
|
|
1379
|
+
- run: node tools/ci/install.mjs
|
|
1380
|
+
${nxCacheSteps}
|
|
1381
|
+
- run: pnpm format:check
|
|
1382
|
+
- name: Resolve the affected base and head for proposed work
|
|
1383
|
+
if: github.event_name == 'pull_request' && !(${protectedDevelopPromotion} || ${protectedMainSync})
|
|
1384
|
+
uses: ${nxSetShasAction}
|
|
1385
|
+
- name: Verify proposed workspace
|
|
1386
|
+
if: github.event_name == 'pull_request' && !(${protectedDevelopPromotion} || ${protectedMainSync})
|
|
1387
|
+
run: |
|
|
1388
|
+
pnpm nx run ${name}:verify-proposed
|
|
1389
|
+
pnpm nx affected -t verify --exclude=${name} --parallel=3
|
|
1390
|
+
- name: Verify protected develop promotion
|
|
1391
|
+
if: github.event_name == 'pull_request' && ${protectedDevelopPromotion}
|
|
1392
|
+
run: pnpm nx run-many -t verify --parallel=3
|
|
1393
|
+
- name: Verify protected main sync
|
|
1394
|
+
if: github.event_name == 'pull_request' && ${protectedMainSync}
|
|
1395
|
+
run: |
|
|
1396
|
+
pnpm nx run ${name}:blueprint-trust-check-main
|
|
1397
|
+
pnpm nx run ${name}:verify-proposed
|
|
1398
|
+
pnpm nx run-many -t verify --exclude=${name} --parallel=3
|
|
1399
|
+
- name: Verify adopted workspace
|
|
1400
|
+
if: github.event_name != 'pull_request'
|
|
1401
|
+
run: pnpm nx run-many -t verify --parallel=3
|
|
1402
|
+
|
|
1403
|
+
seal-acceptance:
|
|
1404
|
+
name: seal protected route acceptance
|
|
1405
|
+
needs: [policy, verify]
|
|
1406
|
+
if: >-
|
|
1407
|
+
always() &&
|
|
1408
|
+
needs.policy.result == 'success' &&
|
|
1409
|
+
needs.verify.result == 'success' &&
|
|
1410
|
+
github.event_name == 'pull_request' &&
|
|
1411
|
+
(${protectedDevelopPromotion} || ${protectedMainSync})
|
|
1412
|
+
runs-on: ubuntu-latest
|
|
1413
|
+
steps:
|
|
1414
|
+
- name: Check out immutable accepted candidate
|
|
1415
|
+
uses: ${checkoutAction}
|
|
1416
|
+
with:
|
|
1417
|
+
fetch-depth: 0
|
|
1418
|
+
ref: \${{ env.EBK_CI_CANDIDATE_SHA }}
|
|
1419
|
+
- name: Seal exact protected-route acceptance
|
|
1420
|
+
id: seal
|
|
1421
|
+
env:
|
|
1422
|
+
EBK_BASE_COMMIT: \${{ github.event.pull_request.base.sha }}
|
|
1423
|
+
EBK_HEAD_REPOSITORY: \${{ github.event.pull_request.head.repo.full_name }}
|
|
1424
|
+
run: >-
|
|
1425
|
+
node tools/ci/workspace-acceptance.mjs seal
|
|
1426
|
+
.ebk/evidence/ci/workspace-acceptance/manifest.json
|
|
1427
|
+
- name: Retain exact protected-route acceptance
|
|
1428
|
+
if: steps.seal.outputs.sealed == 'true'
|
|
1429
|
+
uses: ${uploadArtifactAction}
|
|
1430
|
+
with:
|
|
1431
|
+
name: workspace-acceptance-\${{ env.EBK_CI_CANDIDATE_SHA }}-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
1432
|
+
path: .ebk/evidence/ci/workspace-acceptance
|
|
1433
|
+
if-no-files-found: error
|
|
1434
|
+
include-hidden-files: true
|
|
1435
|
+
retention-days: 30
|
|
1436
|
+
|
|
1437
|
+
release-candidate:
|
|
1438
|
+
name: build immutable release candidates
|
|
1439
|
+
needs: [acceptance, policy, verify]
|
|
1440
|
+
if: >-
|
|
1441
|
+
always() &&
|
|
1442
|
+
github.event_name == 'push' &&
|
|
1443
|
+
needs.acceptance.outputs.history_only != 'true' &&
|
|
1444
|
+
(needs.acceptance.outputs.reused == 'true' ||
|
|
1445
|
+
(needs.policy.result == 'success' && needs.verify.result == 'success'))${releaseCandidateBranchCondition}
|
|
1446
|
+
runs-on: ubuntu-latest
|
|
1447
|
+
steps:
|
|
1448
|
+
- uses: ${checkoutAction}
|
|
1449
|
+
with:
|
|
1450
|
+
fetch-depth: 0
|
|
1451
|
+
ref: \${{ github.sha }}
|
|
1452
|
+
- uses: ${pnpmAction}
|
|
1453
|
+
- uses: ${setupNodeAction}
|
|
1454
|
+
with:
|
|
1455
|
+
node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
|
|
1456
|
+
cache: pnpm
|
|
1457
|
+
- run: node tools/ci/install.mjs
|
|
1458
|
+
${nxCacheSteps}
|
|
1114
1459
|
- name: Build immutable release candidates
|
|
1115
|
-
|
|
1460
|
+
id: candidates
|
|
1116
1461
|
env:
|
|
1117
1462
|
EBK_SOURCE_COMMIT: \${{ github.sha }}
|
|
1118
1463
|
run: |
|
|
1119
|
-
slice_projects="$(
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1464
|
+
slice_projects="$(node tools/ci/release-candidates.mjs)"
|
|
1465
|
+
if [ -z "$slice_projects" ]; then
|
|
1466
|
+
echo 'No application release producers; application artifacts are not applicable.'
|
|
1467
|
+
echo 'available=false' >> "$GITHUB_OUTPUT"
|
|
1468
|
+
exit 0
|
|
1469
|
+
fi
|
|
1470
|
+
pnpm nx run-many -t release-candidate --projects="$slice_projects" --parallel=3
|
|
1123
1471
|
mkdir -p .ebk/evidence/release-candidate/packages
|
|
1124
1472
|
IFS=',' read -r -a projects <<< "$slice_projects"
|
|
1125
1473
|
for project in "\${projects[@]}"; do
|
|
@@ -1129,8 +1477,9 @@ jobs:
|
|
|
1129
1477
|
cp packages/"$project"/.ebk/evidence/release-candidate/*.json \
|
|
1130
1478
|
".ebk/evidence/release-candidate/packages/$project/"
|
|
1131
1479
|
done
|
|
1480
|
+
echo 'available=true' >> "$GITHUB_OUTPUT"
|
|
1132
1481
|
- name: Retain exact immutable release candidate
|
|
1133
|
-
if:
|
|
1482
|
+
if: steps.candidates.outputs.available == 'true'
|
|
1134
1483
|
uses: ${uploadArtifactAction}
|
|
1135
1484
|
with:
|
|
1136
1485
|
name: release-candidate-\${{ github.sha }}
|
|
@@ -1138,6 +1487,7 @@ jobs:
|
|
|
1138
1487
|
include-hidden-files: true
|
|
1139
1488
|
retention-days: 30
|
|
1140
1489
|
path: .ebk/evidence/release-candidate
|
|
1490
|
+
${(0, workspace_files_thin_deploy_js_1.renderThinDeployVerifyCaller)(deploymentProfile, deliveryLane, productionApprovalMode)}
|
|
1141
1491
|
`,
|
|
1142
1492
|
'.github/workflows/deploy.yml': `name: Deploy
|
|
1143
1493
|
|
|
@@ -1165,7 +1515,97 @@ permissions:
|
|
|
1165
1515
|
contents: read
|
|
1166
1516
|
|
|
1167
1517
|
jobs:
|
|
1518
|
+
history-classification:
|
|
1519
|
+
name: classify protected history-only sync
|
|
1520
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/develop'
|
|
1521
|
+
runs-on: ubuntu-latest
|
|
1522
|
+
permissions:
|
|
1523
|
+
actions: read
|
|
1524
|
+
contents: read
|
|
1525
|
+
pull-requests: read
|
|
1526
|
+
outputs:
|
|
1527
|
+
history_only: \${{ steps.resolve.outputs.history_only == 'true' && steps.restore.outcome == 'success' && steps.verify.outcome == 'success' }}
|
|
1528
|
+
steps:
|
|
1529
|
+
- name: Check out immutable protected candidate
|
|
1530
|
+
uses: ${checkoutAction}
|
|
1531
|
+
with:
|
|
1532
|
+
fetch-depth: 0
|
|
1533
|
+
ref: \${{ github.sha }}
|
|
1534
|
+
- uses: ${setupNodeAction}
|
|
1535
|
+
with:
|
|
1536
|
+
node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
|
|
1537
|
+
- name: Resolve exact protected-route acceptance
|
|
1538
|
+
id: resolve
|
|
1539
|
+
env:
|
|
1540
|
+
GH_TOKEN: \${{ github.token }}
|
|
1541
|
+
run: node tools/ci/workspace-acceptance.mjs resolve
|
|
1542
|
+
- name: Restore exact protected-route acceptance
|
|
1543
|
+
if: steps.resolve.outputs.reused == 'true'
|
|
1544
|
+
id: restore
|
|
1545
|
+
continue-on-error: true
|
|
1546
|
+
env:
|
|
1547
|
+
EBK_ACCEPTANCE_ARTIFACT_DIGEST: \${{ steps.resolve.outputs.artifact_digest }}
|
|
1548
|
+
EBK_ACCEPTANCE_ARTIFACT_ID: \${{ steps.resolve.outputs.artifact_id }}
|
|
1549
|
+
EBK_ACCEPTANCE_ARTIFACT_SIZE: \${{ steps.resolve.outputs.artifact_size }}
|
|
1550
|
+
GH_TOKEN: \${{ github.token }}
|
|
1551
|
+
run: >-
|
|
1552
|
+
node tools/ci/workspace-acceptance.mjs download
|
|
1553
|
+
.ebk/evidence/reused-history-acceptance
|
|
1554
|
+
"$EBK_ACCEPTANCE_ARTIFACT_ID"
|
|
1555
|
+
"$EBK_ACCEPTANCE_ARTIFACT_DIGEST"
|
|
1556
|
+
"$EBK_ACCEPTANCE_ARTIFACT_SIZE"
|
|
1557
|
+
- name: Verify exact history-only acceptance
|
|
1558
|
+
if: steps.resolve.outputs.reused == 'true' && steps.restore.outcome == 'success'
|
|
1559
|
+
id: verify
|
|
1560
|
+
continue-on-error: true
|
|
1561
|
+
env:
|
|
1562
|
+
EBK_ACCEPTANCE_BASE: \${{ steps.resolve.outputs.base_commit }}
|
|
1563
|
+
EBK_ACCEPTANCE_HEAD: \${{ steps.resolve.outputs.head_commit }}
|
|
1564
|
+
EBK_ACCEPTANCE_ROUTE: \${{ steps.resolve.outputs.route }}
|
|
1565
|
+
EBK_ACCEPTANCE_RUN_ATTEMPT: \${{ steps.resolve.outputs.run_attempt }}
|
|
1566
|
+
EBK_ACCEPTANCE_RUN_ID: \${{ steps.resolve.outputs.run_id }}
|
|
1567
|
+
run: >-
|
|
1568
|
+
node tools/ci/workspace-acceptance.mjs verify
|
|
1569
|
+
.ebk/evidence/reused-history-acceptance/manifest.json
|
|
1570
|
+
"$EBK_ACCEPTANCE_HEAD"
|
|
1571
|
+
"$EBK_ACCEPTANCE_BASE"
|
|
1572
|
+
"$EBK_ACCEPTANCE_ROUTE"
|
|
1573
|
+
"$EBK_ACCEPTANCE_RUN_ID"
|
|
1574
|
+
"$EBK_ACCEPTANCE_RUN_ATTEMPT"
|
|
1575
|
+
|
|
1576
|
+
deployment-mode:
|
|
1577
|
+
name: classify deployment profile
|
|
1578
|
+
runs-on: ubuntu-latest
|
|
1579
|
+
outputs:
|
|
1580
|
+
profile: \${{ steps.profile.outputs.profile }}
|
|
1581
|
+
steps:
|
|
1582
|
+
- name: Check out immutable deployment policy
|
|
1583
|
+
uses: ${checkoutAction}
|
|
1584
|
+
with:
|
|
1585
|
+
ref: \${{ github.sha }}
|
|
1586
|
+
- id: profile
|
|
1587
|
+
name: Read exact committed deployment profile
|
|
1588
|
+
run: |
|
|
1589
|
+
node - <<'NODE'
|
|
1590
|
+
const { readFileSync } = require('node:fs');
|
|
1591
|
+
const policy = JSON.parse(readFileSync('ebk.repository-policy.json', 'utf8'));
|
|
1592
|
+
const profiles = new Set(['starter', 'production-isolated', 'fully-isolated']);
|
|
1593
|
+
if (!profiles.has(policy.deploymentProfile)) {
|
|
1594
|
+
throw new Error('Committed deployment profile is absent or invalid.');
|
|
1595
|
+
}
|
|
1596
|
+
require('node:fs').appendFileSync(
|
|
1597
|
+
process.env.GITHUB_OUTPUT,
|
|
1598
|
+
'profile=' + policy.deploymentProfile + '\\n',
|
|
1599
|
+
);
|
|
1600
|
+
NODE
|
|
1601
|
+
|
|
1168
1602
|
route:
|
|
1603
|
+
name: ${protectedDeploymentRouteJobName}
|
|
1604
|
+
needs: [history-classification, deployment-mode]
|
|
1605
|
+
if: >-
|
|
1606
|
+
always() &&
|
|
1607
|
+
needs.history-classification.outputs.history_only != 'true' &&
|
|
1608
|
+
needs.deployment-mode.result == 'success'
|
|
1169
1609
|
runs-on: ubuntu-latest
|
|
1170
1610
|
steps:
|
|
1171
1611
|
- name: Validate protected ref
|
|
@@ -1178,14 +1618,25 @@ jobs:
|
|
|
1178
1618
|
esac
|
|
1179
1619
|
|
|
1180
1620
|
plan-staging:
|
|
1181
|
-
needs: route
|
|
1182
|
-
if:
|
|
1621
|
+
needs: [route, deployment-mode]
|
|
1622
|
+
if: >-
|
|
1623
|
+
always() &&
|
|
1624
|
+
needs.route.result == 'success' &&
|
|
1625
|
+
needs.deployment-mode.result == 'success' &&
|
|
1626
|
+
github.ref == 'refs/heads/develop' &&
|
|
1627
|
+
(github.event_name == 'workflow_dispatch' ||
|
|
1628
|
+
needs.deployment-mode.outputs.profile != 'starter')
|
|
1183
1629
|
runs-on: ubuntu-latest
|
|
1184
1630
|
outputs:
|
|
1631
|
+
deployment_profile: \${{ steps.plan.outputs.deployment_profile }}
|
|
1632
|
+
deployment_region: \${{ steps.plan.outputs.deployment_region }}
|
|
1633
|
+
development_account_id: \${{ steps.plan.outputs.development_account_id }}
|
|
1185
1634
|
foundation_project: \${{ steps.plan.outputs.foundation_project }}
|
|
1186
1635
|
has_slices: \${{ steps.plan.outputs.has_slices }}
|
|
1187
1636
|
ordered_projects: \${{ steps.plan.outputs.ordered_projects }}
|
|
1637
|
+
production_account_id: \${{ steps.plan.outputs.production_account_id }}
|
|
1188
1638
|
slice_projects: \${{ steps.plan.outputs.slice_projects }}
|
|
1639
|
+
staging_account_id: \${{ steps.plan.outputs.staging_account_id }}
|
|
1189
1640
|
steps:
|
|
1190
1641
|
- uses: ${checkoutAction}
|
|
1191
1642
|
with:
|
|
@@ -1208,7 +1659,7 @@ jobs:
|
|
|
1208
1659
|
release-source-staging:
|
|
1209
1660
|
name: select and seal staging release source
|
|
1210
1661
|
needs: plan-staging
|
|
1211
|
-
if: \${{ needs.plan-staging.result == 'success' && (github.event_name == 'push' && 'deploy' || inputs.action) != 'unlock' }}
|
|
1662
|
+
if: \${{ always() && needs.plan-staging.result == 'success' && (github.event_name == 'push' && 'deploy' || inputs.action) != 'unlock' }}
|
|
1212
1663
|
runs-on: ubuntu-latest
|
|
1213
1664
|
permissions:
|
|
1214
1665
|
actions: read
|
|
@@ -1396,16 +1847,25 @@ jobs:
|
|
|
1396
1847
|
path: .ebk/evidence/staging-release-source/**
|
|
1397
1848
|
|
|
1398
1849
|
plan-production:
|
|
1399
|
-
needs: route
|
|
1400
|
-
if:
|
|
1850
|
+
needs: [route, deployment-mode]
|
|
1851
|
+
if: >-
|
|
1852
|
+
always() &&
|
|
1853
|
+
needs.route.result == 'success' &&
|
|
1854
|
+
needs.deployment-mode.result == 'success' &&
|
|
1855
|
+
github.ref == 'refs/heads/main'
|
|
1401
1856
|
runs-on: ubuntu-latest
|
|
1402
1857
|
outputs:
|
|
1858
|
+
deployment_profile: \${{ steps.plan.outputs.deployment_profile }}
|
|
1859
|
+
deployment_region: \${{ steps.plan.outputs.deployment_region }}
|
|
1860
|
+
development_account_id: \${{ steps.plan.outputs.development_account_id }}
|
|
1403
1861
|
foundation_project: \${{ steps.plan.outputs.foundation_project }}
|
|
1404
1862
|
foundation_profile: \${{ steps.plan.outputs.foundation_profile }}
|
|
1405
1863
|
has_slices: \${{ steps.plan.outputs.has_slices }}
|
|
1406
1864
|
ordered_projects: \${{ steps.plan.outputs.ordered_projects }}
|
|
1407
1865
|
production_approval_mode: \${{ steps.plan.outputs.production_approval_mode }}
|
|
1866
|
+
production_account_id: \${{ steps.plan.outputs.production_account_id }}
|
|
1408
1867
|
slice_projects: \${{ steps.plan.outputs.slice_projects }}
|
|
1868
|
+
staging_account_id: \${{ steps.plan.outputs.staging_account_id }}
|
|
1409
1869
|
steps:
|
|
1410
1870
|
- uses: ${checkoutAction}
|
|
1411
1871
|
with:
|
|
@@ -1699,7 +2159,7 @@ jobs:
|
|
|
1699
2159
|
container-platform-staging:
|
|
1700
2160
|
name: native staging OCI \${{ matrix.platform }}
|
|
1701
2161
|
needs: [plan-staging, release-source-staging]
|
|
1702
|
-
if: \${{ (github.event_name == 'push' && 'deploy' || inputs.action) == 'deploy' && needs.plan-staging.outputs.
|
|
2162
|
+
if: \${{ always() && (github.event_name == 'push' && 'deploy' || inputs.action) == 'deploy' && needs.plan-staging.result == 'success' && needs.plan-staging.outputs.slice_projects != '' && needs.release-source-staging.result == 'success' }}
|
|
1703
2163
|
strategy:
|
|
1704
2164
|
fail-fast: false
|
|
1705
2165
|
matrix:
|
|
@@ -1726,6 +2186,8 @@ jobs:
|
|
|
1726
2186
|
EBK_RELEASE_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
|
|
1727
2187
|
EBK_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
|
|
1728
2188
|
EBK_STAGE: staging
|
|
2189
|
+
EBK_POLICY_ACCOUNT_ID: \${{ needs.plan-staging.outputs.staging_account_id }}
|
|
2190
|
+
EBK_POLICY_REGION: \${{ needs.plan-staging.outputs.deployment_region }}
|
|
1729
2191
|
SLICE_PROJECTS: \${{ needs.plan-staging.outputs.slice_projects }}
|
|
1730
2192
|
steps:
|
|
1731
2193
|
- uses: ${checkoutAction}
|
|
@@ -1741,6 +2203,10 @@ jobs:
|
|
|
1741
2203
|
- run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile
|
|
1742
2204
|
- name: Verify managed workspace integrity before AWS authority
|
|
1743
2205
|
run: pnpm nx run ${name}:workspace-health
|
|
2206
|
+
- name: Bind staging account to committed deployment topology
|
|
2207
|
+
run: |
|
|
2208
|
+
test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_ACCOUNT_ID"
|
|
2209
|
+
test "$AWS_REGION" = "$EBK_POLICY_REGION"
|
|
1744
2210
|
${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - uses: ${configureAwsAction}
|
|
1745
2211
|
with:
|
|
1746
2212
|
role-to-assume: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
|
|
@@ -1780,6 +2246,8 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - uses: ${c
|
|
|
1780
2246
|
EBK_RELEASE_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
|
|
1781
2247
|
EBK_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
|
|
1782
2248
|
EBK_STAGE: staging
|
|
2249
|
+
EBK_POLICY_ACCOUNT_ID: \${{ needs.plan-staging.outputs.staging_account_id }}
|
|
2250
|
+
EBK_POLICY_REGION: \${{ needs.plan-staging.outputs.deployment_region }}
|
|
1783
2251
|
SLICE_PROJECTS: \${{ needs.plan-staging.outputs.slice_projects }}
|
|
1784
2252
|
steps:
|
|
1785
2253
|
- uses: ${checkoutAction}
|
|
@@ -1795,6 +2263,10 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - uses: ${c
|
|
|
1795
2263
|
- run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile
|
|
1796
2264
|
- name: Verify managed workspace integrity before AWS authority
|
|
1797
2265
|
run: pnpm nx run ${name}:workspace-health
|
|
2266
|
+
- name: Bind staging account to committed deployment topology
|
|
2267
|
+
run: |
|
|
2268
|
+
test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_ACCOUNT_ID"
|
|
2269
|
+
test "$AWS_REGION" = "$EBK_POLICY_REGION"
|
|
1798
2270
|
${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Restore both native platform records
|
|
1799
2271
|
uses: ${downloadArtifactAction}
|
|
1800
2272
|
with:
|
|
@@ -1825,7 +2297,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
1825
2297
|
container-promote-production:
|
|
1826
2298
|
name: promote reviewed OCI indexes to production
|
|
1827
2299
|
needs: [plan-production, release-source-production]
|
|
1828
|
-
if: \${{ always() && needs.plan-production.result == 'success' && needs.release-source-production.result == 'success' && needs.plan-production.outputs.
|
|
2300
|
+
if: \${{ always() && needs.plan-production.result == 'success' && needs.release-source-production.result == 'success' && needs.plan-production.outputs.slice_projects != '' }}
|
|
1829
2301
|
runs-on: ubuntu-latest
|
|
1830
2302
|
environment: production
|
|
1831
2303
|
permissions:
|
|
@@ -1836,6 +2308,10 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
1836
2308
|
AWS_DEPLOY_ROLE_ARN: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
|
|
1837
2309
|
AWS_EXPECTED_ACCOUNT_ID: \${{ vars.AWS_EXPECTED_ACCOUNT_ID }}
|
|
1838
2310
|
AWS_REGION: \${{ vars.AWS_REGION }}
|
|
2311
|
+
EBK_DEPLOYMENT_PROFILE: \${{ needs.plan-production.outputs.deployment_profile }}
|
|
2312
|
+
EBK_POLICY_PRODUCTION_ACCOUNT_ID: \${{ needs.plan-production.outputs.production_account_id }}
|
|
2313
|
+
EBK_POLICY_REGION: \${{ needs.plan-production.outputs.deployment_region }}
|
|
2314
|
+
EBK_POLICY_STAGING_ACCOUNT_ID: \${{ needs.plan-production.outputs.staging_account_id }}
|
|
1839
2315
|
EBK_CANDIDATE_RUN_ATTEMPT: \${{ needs.release-source-production.outputs.candidate_run_attempt }}
|
|
1840
2316
|
EBK_CANDIDATE_RUN_ID: \${{ needs.release-source-production.outputs.candidate_run_id }}
|
|
1841
2317
|
EBK_RELEASE_SOURCE_COMMIT: \${{ needs.release-source-production.outputs.source_commit }}
|
|
@@ -1936,11 +2412,13 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
1936
2412
|
cp "$source_sidecar" "$target"
|
|
1937
2413
|
done
|
|
1938
2414
|
- uses: ${configureAwsAction}
|
|
2415
|
+
if: env.EBK_DEPLOYMENT_PROFILE != 'starter'
|
|
1939
2416
|
with:
|
|
1940
2417
|
role-to-assume: \${{ secrets.AWS_STAGING_PROMOTION_PULL_ROLE_ARN }}
|
|
1941
2418
|
role-session-name: ebk-staging-container-pull-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
1942
2419
|
aws-region: \${{ vars.AWS_STAGING_PROMOTION_REGION }}
|
|
1943
2420
|
- name: Authenticate exact staging source registry
|
|
2421
|
+
if: env.EBK_DEPLOYMENT_PROFILE != 'starter'
|
|
1944
2422
|
env:
|
|
1945
2423
|
EBK_STAGING_ACCOUNT_ID: \${{ vars.AWS_STAGING_PROMOTION_EXPECTED_ACCOUNT_ID }}
|
|
1946
2424
|
EBK_STAGING_REGION: \${{ vars.AWS_STAGING_PROMOTION_REGION }}
|
|
@@ -1959,10 +2437,27 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
1959
2437
|
role-to-assume: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
|
|
1960
2438
|
role-session-name: ebk-production-container-push-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
1961
2439
|
aws-region: \${{ vars.AWS_REGION }}
|
|
2440
|
+
- name: Bind production authority to committed deployment topology
|
|
2441
|
+
run: |
|
|
2442
|
+
test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_PRODUCTION_ACCOUNT_ID" ||
|
|
2443
|
+
{ echo 'Production environment account differs from committed deployment topology'; exit 1; }
|
|
2444
|
+
test "$AWS_REGION" = "$EBK_POLICY_REGION" ||
|
|
2445
|
+
{ echo 'Production environment Region differs from committed deployment topology'; exit 1; }
|
|
2446
|
+
- name: Authenticate shared-account staging source registry
|
|
2447
|
+
if: env.EBK_DEPLOYMENT_PROFILE == 'starter'
|
|
2448
|
+
run: |
|
|
2449
|
+
test "$EBK_POLICY_STAGING_ACCOUNT_ID" = "$EBK_POLICY_PRODUCTION_ACCOUNT_ID" ||
|
|
2450
|
+
{ echo 'Starter promotion requires one shared AWS account'; exit 1; }
|
|
2451
|
+
registry_suffix=.com
|
|
2452
|
+
case "$EBK_POLICY_REGION" in cn-*) registry_suffix=.com.cn ;; esac
|
|
2453
|
+
aws ecr get-login-password --region "$EBK_POLICY_REGION" |
|
|
2454
|
+
docker login --username AWS --password-stdin \
|
|
2455
|
+
"$EBK_POLICY_STAGING_ACCOUNT_ID.dkr.ecr.$EBK_POLICY_REGION.amazonaws$registry_suffix"
|
|
1962
2456
|
- name: Promote exact staging indexes without rebuilding
|
|
1963
2457
|
env:
|
|
1964
|
-
|
|
1965
|
-
|
|
2458
|
+
EBK_DEPLOYMENT_PROFILE: \${{ needs.plan-production.outputs.deployment_profile }}
|
|
2459
|
+
EBK_STAGING_PROMOTION_EXPECTED_ACCOUNT_ID: \${{ needs.plan-production.outputs.deployment_profile == 'starter' && needs.plan-production.outputs.staging_account_id || vars.AWS_STAGING_PROMOTION_EXPECTED_ACCOUNT_ID }}
|
|
2460
|
+
EBK_STAGING_PROMOTION_REGION: \${{ needs.plan-production.outputs.deployment_profile == 'starter' && needs.plan-production.outputs.deployment_region || vars.AWS_STAGING_PROMOTION_REGION }}
|
|
1966
2461
|
run: |
|
|
1967
2462
|
test "$(aws sts get-caller-identity --query Account --output text)" = "$AWS_EXPECTED_ACCOUNT_ID" ||
|
|
1968
2463
|
{ echo 'Production promotion role account differs from policy'; exit 1; }
|
|
@@ -1995,7 +2490,7 @@ ${planRoleAuditJob('production', configureAwsAction)}${planRoleAuditJob('staging
|
|
|
1995
2490
|
(
|
|
1996
2491
|
needs.release-source-production.result == 'success' &&
|
|
1997
2492
|
(
|
|
1998
|
-
needs.plan-production.outputs.
|
|
2493
|
+
needs.plan-production.outputs.slice_projects == '' ||
|
|
1999
2494
|
needs.container-promote-production.result == 'success'
|
|
2000
2495
|
)
|
|
2001
2496
|
)
|
|
@@ -2089,7 +2584,7 @@ ${infrastructureCandidateDownloadSteps(downloadArtifactAction, {
|
|
|
2089
2584
|
runId: '${{ needs.release-source-production.outputs.candidate_run_id }}',
|
|
2090
2585
|
verifiedStagingRunId: '${{ needs.release-source-production.outputs.verified_staging_run_id }}',
|
|
2091
2586
|
})} - name: Restore exact production container bindings
|
|
2092
|
-
if: env.EBK_ACTION == 'deploy' && needs.plan-production.outputs.
|
|
2587
|
+
if: env.EBK_ACTION == 'deploy' && needs.plan-production.outputs.slice_projects != ''
|
|
2093
2588
|
env:
|
|
2094
2589
|
ACTIONS_ID_TOKEN_REQUEST_TOKEN: ''
|
|
2095
2590
|
ACTIONS_ID_TOKEN_REQUEST_URL: ''
|
|
@@ -2150,7 +2645,7 @@ ${planRoleAuditStep('main', 'verify')}
|
|
|
2150
2645
|
(github.event_name == 'push' && 'deploy' || inputs.action) != 'unlock' &&
|
|
2151
2646
|
(
|
|
2152
2647
|
(github.event_name == 'push' && 'deploy' || inputs.action) != 'deploy' ||
|
|
2153
|
-
needs.plan-staging.outputs.
|
|
2648
|
+
needs.plan-staging.outputs.slice_projects == '' ||
|
|
2154
2649
|
needs.container-assemble-staging.result == 'success'
|
|
2155
2650
|
)
|
|
2156
2651
|
runs-on: ubuntu-latest
|
|
@@ -2181,6 +2676,8 @@ ${planRoleAuditStep('main', 'verify')}
|
|
|
2181
2676
|
EBK_RELEASE_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
|
|
2182
2677
|
EBK_SOURCE_COMMIT: \${{ github.sha }}
|
|
2183
2678
|
EBK_STAGE: staging
|
|
2679
|
+
EBK_POLICY_ACCOUNT_ID: \${{ needs.plan-staging.outputs.staging_account_id }}
|
|
2680
|
+
EBK_POLICY_REGION: \${{ needs.plan-staging.outputs.deployment_region }}
|
|
2184
2681
|
FOUNDATION_PROJECT: \${{ needs.plan-staging.outputs.foundation_project }}
|
|
2185
2682
|
ORDERED_PROJECTS: \${{ needs.plan-staging.outputs.ordered_projects }}
|
|
2186
2683
|
SLICE_PROJECTS: \${{ needs.plan-staging.outputs.slice_projects }}
|
|
@@ -2238,7 +2735,7 @@ ${planApplicationBindingCheck()} case "$AWS_PLAN_ROLE_ARN" in
|
|
|
2238
2735
|
ACTIONS_ID_TOKEN_REQUEST_URL: ''
|
|
2239
2736
|
run: pnpm nx run ${name}:workspace-health
|
|
2240
2737
|
${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Restore exact staging container bindings
|
|
2241
|
-
if: env.EBK_ACTION == 'deploy' && needs.plan-staging.outputs.
|
|
2738
|
+
if: env.EBK_ACTION == 'deploy' && needs.plan-staging.outputs.slice_projects != ''
|
|
2242
2739
|
env:
|
|
2243
2740
|
ACTIONS_ID_TOKEN_REQUEST_TOKEN: ''
|
|
2244
2741
|
ACTIONS_ID_TOKEN_REQUEST_URL: ''
|
|
@@ -2288,14 +2785,14 @@ ${planRoleAuditStep('develop', 'verify')}
|
|
|
2288
2785
|
packages/*/.ebk/evidence/infrastructure-plan-command.json
|
|
2289
2786
|
|
|
2290
2787
|
deploy-staging:
|
|
2291
|
-
name:
|
|
2788
|
+
name: ${stagingApplyJobPrefix}\${{ needs.infrastructure-plan-staging.outputs.plan_digest || 'recovery-without-resource-plan' }}
|
|
2292
2789
|
needs: [plan-staging, release-source-staging, container-assemble-staging, infrastructure-plan-staging]
|
|
2293
2790
|
if: |
|
|
2294
2791
|
always() &&
|
|
2295
2792
|
needs.plan-staging.result == 'success' &&
|
|
2296
2793
|
(
|
|
2297
2794
|
(github.event_name == 'push' && 'deploy' || inputs.action) != 'deploy' ||
|
|
2298
|
-
needs.plan-staging.outputs.
|
|
2795
|
+
needs.plan-staging.outputs.slice_projects == '' ||
|
|
2299
2796
|
needs.container-assemble-staging.result == 'success'
|
|
2300
2797
|
) &&
|
|
2301
2798
|
(
|
|
@@ -2357,7 +2854,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
2357
2854
|
name: infrastructure-plan-staging-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
2358
2855
|
path: .
|
|
2359
2856
|
- name: Restore exact staging container bindings
|
|
2360
|
-
if: env.EBK_ACTION == 'deploy' && needs.plan-staging.outputs.
|
|
2857
|
+
if: env.EBK_ACTION == 'deploy' && needs.plan-staging.outputs.slice_projects != ''
|
|
2361
2858
|
uses: ${downloadArtifactAction}
|
|
2362
2859
|
with:
|
|
2363
2860
|
name: container-release-staging-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
@@ -2367,6 +2864,10 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
2367
2864
|
test -n "$AWS_DEPLOY_ROLE_ARN" || { echo 'staging AWS_DEPLOY_ROLE_ARN is required'; exit 1; }
|
|
2368
2865
|
test -n "$AWS_EXPECTED_ACCOUNT_ID" || { echo 'staging AWS_EXPECTED_ACCOUNT_ID is required'; exit 1; }
|
|
2369
2866
|
test -n "$AWS_REGION" || { echo 'staging AWS_REGION is required'; exit 1; }
|
|
2867
|
+
test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_ACCOUNT_ID" ||
|
|
2868
|
+
{ echo 'staging environment account differs from committed deployment topology'; exit 1; }
|
|
2869
|
+
test "$AWS_REGION" = "$EBK_POLICY_REGION" ||
|
|
2870
|
+
{ echo 'staging environment Region differs from committed deployment topology'; exit 1; }
|
|
2370
2871
|
test -n "$EBK_FLEET_ORIGIN" ||
|
|
2371
2872
|
{ echo 'staging environment EBK_FLEET_ORIGIN is required'; exit 1; }
|
|
2372
2873
|
test "$EBK_FLEET_ORIGIN" = "$EBK_PLANNED_FLEET_ORIGIN" ||
|
|
@@ -2378,8 +2879,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
2378
2879
|
test -n "$AWS_PLAN_GENERATED_SECRET_ARNS_JSON" &&
|
|
2379
2880
|
test -n "$AWS_PLAN_PROVIDER_REFRESH_POLICY_JSON" ||
|
|
2380
2881
|
{ echo 'staging exact plan-discovery inputs are required'; exit 1; }
|
|
2381
|
-
test -n "$AWS_PLAN_ROLE_ARN"
|
|
2382
|
-
test -n "$AWS_PLAN_STATE_KMS_KEY_ARN" ||
|
|
2882
|
+
test -n "$AWS_PLAN_ROLE_ARN" ||
|
|
2383
2883
|
{ echo 'staging exact plan-authority inputs are required'; exit 1; }
|
|
2384
2884
|
case "$AWS_DEPLOY_ROLE_ARN" in
|
|
2385
2885
|
arn:aws:iam::$AWS_EXPECTED_ACCOUNT_ID:role/*) ;;
|
|
@@ -2946,7 +3446,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
2946
3446
|
needs.plan-production.result == 'success' &&
|
|
2947
3447
|
(
|
|
2948
3448
|
(github.event_name == 'push' && 'deploy' || inputs.action) != 'deploy' ||
|
|
2949
|
-
needs.plan-production.outputs.
|
|
3449
|
+
needs.plan-production.outputs.slice_projects == '' ||
|
|
2950
3450
|
needs.container-promote-production.result == 'success'
|
|
2951
3451
|
) &&
|
|
2952
3452
|
(
|
|
@@ -3003,6 +3503,8 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
3003
3503
|
EBK_APPROVED_MAIN_COMMIT: \${{ github.sha }}
|
|
3004
3504
|
EBK_SOURCE_COMMIT: \${{ github.sha }}
|
|
3005
3505
|
EBK_STAGE: production
|
|
3506
|
+
EBK_POLICY_ACCOUNT_ID: \${{ needs.plan-production.outputs.production_account_id }}
|
|
3507
|
+
EBK_POLICY_REGION: \${{ needs.plan-production.outputs.deployment_region }}
|
|
3006
3508
|
EBK_VERIFIED_STAGING_RUN_ID: \${{ needs.release-source-production.outputs.verified_staging_run_id }}
|
|
3007
3509
|
FOUNDATION_PROJECT: \${{ needs.plan-production.outputs.foundation_project }}
|
|
3008
3510
|
ORDERED_PROJECTS: \${{ needs.plan-production.outputs.ordered_projects }}
|
|
@@ -3089,7 +3591,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
3089
3591
|
name: infrastructure-plan-production-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
3090
3592
|
path: .
|
|
3091
3593
|
- name: Restore exact production container bindings
|
|
3092
|
-
if: env.EBK_ACTION == 'deploy' && needs.plan-production.outputs.
|
|
3594
|
+
if: env.EBK_ACTION == 'deploy' && needs.plan-production.outputs.slice_projects != ''
|
|
3093
3595
|
uses: ${downloadArtifactAction}
|
|
3094
3596
|
with:
|
|
3095
3597
|
name: container-release-production-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
@@ -3099,6 +3601,10 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
3099
3601
|
test -n "$AWS_DEPLOY_ROLE_ARN" || { echo 'production AWS_DEPLOY_ROLE_ARN is required'; exit 1; }
|
|
3100
3602
|
test -n "$AWS_EXPECTED_ACCOUNT_ID" || { echo 'production AWS_EXPECTED_ACCOUNT_ID is required'; exit 1; }
|
|
3101
3603
|
test -n "$AWS_REGION" || { echo 'production AWS_REGION is required'; exit 1; }
|
|
3604
|
+
test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_ACCOUNT_ID" ||
|
|
3605
|
+
{ echo 'production environment account differs from committed deployment topology'; exit 1; }
|
|
3606
|
+
test "$AWS_REGION" = "$EBK_POLICY_REGION" ||
|
|
3607
|
+
{ echo 'production environment Region differs from committed deployment topology'; exit 1; }
|
|
3102
3608
|
test -n "$EBK_FLEET_ORIGIN" ||
|
|
3103
3609
|
{ echo 'production environment EBK_FLEET_ORIGIN is required'; exit 1; }
|
|
3104
3610
|
test "$EBK_FLEET_ORIGIN" = "$EBK_PLANNED_FLEET_ORIGIN" ||
|
|
@@ -3110,8 +3616,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
3110
3616
|
test -n "$AWS_PLAN_GENERATED_SECRET_ARNS_JSON" &&
|
|
3111
3617
|
test -n "$AWS_PLAN_PROVIDER_REFRESH_POLICY_JSON" ||
|
|
3112
3618
|
{ echo 'production exact plan-discovery inputs are required'; exit 1; }
|
|
3113
|
-
test -n "$AWS_PLAN_ROLE_ARN"
|
|
3114
|
-
test -n "$AWS_PLAN_STATE_KMS_KEY_ARN" ||
|
|
3619
|
+
test -n "$AWS_PLAN_ROLE_ARN" ||
|
|
3115
3620
|
{ echo 'production exact plan-authority inputs are required'; exit 1; }
|
|
3116
3621
|
case "$AWS_DEPLOY_ROLE_ARN" in
|
|
3117
3622
|
arn:aws:iam::$AWS_EXPECTED_ACCOUNT_ID:role/*) ;;
|
|
@@ -3492,7 +3997,7 @@ on:
|
|
|
3492
3997
|
description: Exact protected deployment stage
|
|
3493
3998
|
required: true
|
|
3494
3999
|
type: choice
|
|
3495
|
-
options: [
|
|
4000
|
+
options: [${ecrInspectorWorkflowStageChoices}]
|
|
3496
4001
|
deployment_commit:
|
|
3497
4002
|
description: Exact successful protected deployment commit
|
|
3498
4003
|
required: true
|
|
@@ -3501,8 +4006,8 @@ on:
|
|
|
3501
4006
|
description: Exact successful Deploy workflow run ID
|
|
3502
4007
|
required: true
|
|
3503
4008
|
type: string
|
|
3504
|
-
|
|
3505
|
-
description: Exact
|
|
4009
|
+
deployment_receipt_artifact_name:
|
|
4010
|
+
description: Exact deployment-receipt artifact from that run
|
|
3506
4011
|
required: true
|
|
3507
4012
|
type: string
|
|
3508
4013
|
slice:
|
|
@@ -3556,7 +4061,7 @@ jobs:
|
|
|
3556
4061
|
EBK_DEPLOYMENT_ARN: \${{ inputs.deployment_arn }}
|
|
3557
4062
|
EBK_DEPLOYMENT_KIND: \${{ inputs.deployment_kind }}
|
|
3558
4063
|
EBK_DEPLOY_RUN_ID: \${{ inputs.deploy_run_id }}
|
|
3559
|
-
|
|
4064
|
+
EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME: \${{ inputs.deployment_receipt_artifact_name }}
|
|
3560
4065
|
EBK_SLICE: \${{ inputs.slice }}
|
|
3561
4066
|
EBK_DEPLOYMENT_COMMIT: \${{ inputs.deployment_commit }}
|
|
3562
4067
|
EBK_STAGE: \${{ inputs.stage }}
|
|
@@ -3579,10 +4084,10 @@ jobs:
|
|
|
3579
4084
|
esac
|
|
3580
4085
|
expected_branch=develop
|
|
3581
4086
|
if [ "$EBK_STAGE" = production ]; then expected_branch=main; fi
|
|
3582
|
-
expected_artifact="
|
|
3583
|
-
case "$
|
|
3584
|
-
"$expected_artifact"*) attempt="\${
|
|
3585
|
-
*) echo '
|
|
4087
|
+
expected_artifact="deployment-receipt-$EBK_STAGE-$EBK_DEPLOY_RUN_ID-"
|
|
4088
|
+
case "$EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME" in
|
|
4089
|
+
"$expected_artifact"*) attempt="\${EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME#"$expected_artifact"}" ;;
|
|
4090
|
+
*) echo 'Deployment receipt artifact does not bind the exact deploy run'; exit 1 ;;
|
|
3586
4091
|
esac
|
|
3587
4092
|
case "$attempt" in
|
|
3588
4093
|
''|0*|*[!0-9]*) echo 'Release evidence artifact has an invalid run attempt'; exit 1 ;;
|
|
@@ -3595,7 +4100,7 @@ jobs:
|
|
|
3595
4100
|
test "$(printf '%s' "$run" | jq -r .path)" = ".github/workflows/deploy.yml" ||
|
|
3596
4101
|
{ echo 'Deploy run is not the exact successful protected source authority'; exit 1; }
|
|
3597
4102
|
artifacts="$(gh api "/repos/$GH_REPO/actions/runs/$EBK_DEPLOY_RUN_ID/artifacts?per_page=100")"
|
|
3598
|
-
count="$(printf '%s' "$artifacts" | jq --arg name "$
|
|
4103
|
+
count="$(printf '%s' "$artifacts" | jq --arg name "$EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME" \
|
|
3599
4104
|
'[.artifacts[] | select(.name == $name and .expired == false)] | length')"
|
|
3600
4105
|
test "$count" = 1 ||
|
|
3601
4106
|
{ echo 'Exact unexpired release evidence artifact is absent or ambiguous'; exit 1; }
|
|
@@ -3612,7 +4117,7 @@ jobs:
|
|
|
3612
4117
|
- uses: ${downloadArtifactAction}
|
|
3613
4118
|
with:
|
|
3614
4119
|
github-token: \${{ github.token }}
|
|
3615
|
-
name: \${{ inputs.
|
|
4120
|
+
name: \${{ inputs.deployment_receipt_artifact_name }}
|
|
3616
4121
|
path: .
|
|
3617
4122
|
run-id: \${{ inputs.deploy_run_id }}
|
|
3618
4123
|
- uses: ${pnpmAction}
|
|
@@ -3719,7 +4224,7 @@ jobs:
|
|
|
3719
4224
|
{ encoding: "utf8" },
|
|
3720
4225
|
).trim()
|
|
3721
4226
|
) fail("Production deployment tree differs from its reviewed release source.");
|
|
3722
|
-
} else if (stage !== "
|
|
4227
|
+
} else if (stage !== "${rehearsalStage}") {
|
|
3723
4228
|
fail("Protected release stage is invalid.");
|
|
3724
4229
|
}
|
|
3725
4230
|
const productionKeys = [
|
|
@@ -3775,7 +4280,7 @@ jobs:
|
|
|
3775
4280
|
!digest.test(binding.candidateDigest ?? "") ||
|
|
3776
4281
|
(stage === "production" &&
|
|
3777
4282
|
!digest.test(binding.lifecycleDigest ?? "")) ||
|
|
3778
|
-
(stage === "
|
|
4283
|
+
(stage === "${rehearsalStage}" && binding.stage !== stage) ||
|
|
3779
4284
|
binding.runId !== process.env.EBK_DEPLOY_RUN_ID ||
|
|
3780
4285
|
binding.runAttempt !== process.env.EBK_DEPLOY_RUN_ATTEMPT ||
|
|
3781
4286
|
binding.workflow !== expectedWorkflow
|
|
@@ -3854,7 +4359,7 @@ on:
|
|
|
3854
4359
|
description: Protected deployed stage
|
|
3855
4360
|
required: true
|
|
3856
4361
|
type: choice
|
|
3857
|
-
options: [
|
|
4362
|
+
options: [${protectedWorkflowStageChoices}]
|
|
3858
4363
|
project:
|
|
3859
4364
|
description: Exact Product Slice project
|
|
3860
4365
|
required: true
|
|
@@ -3954,7 +4459,7 @@ jobs:
|
|
|
3954
4459
|
EBK_REPORT_RUN_ID: \${{ inputs.report_run_id }}
|
|
3955
4460
|
run: |
|
|
3956
4461
|
case "$EBK_STAGE:$EBK_PROTECTED_REF" in
|
|
3957
|
-
|
|
4462
|
+
${protectedWorkflowStageBranchCases}) ;;
|
|
3958
4463
|
*) echo 'Async recovery stage does not match its protected branch'; exit 1 ;;
|
|
3959
4464
|
esac
|
|
3960
4465
|
case "$EBK_PROJECT" in
|
|
@@ -4765,13 +5270,78 @@ permissions:
|
|
|
4765
5270
|
contents: read
|
|
4766
5271
|
|
|
4767
5272
|
jobs:
|
|
5273
|
+
deployment-classification:
|
|
5274
|
+
name: classify exact staging deployment
|
|
5275
|
+
if: >-
|
|
5276
|
+
github.event_name == 'workflow_run' &&
|
|
5277
|
+
github.event.workflow_run.event == 'push' &&
|
|
5278
|
+
github.event.workflow_run.head_branch == 'develop' &&
|
|
5279
|
+
github.event.workflow_run.conclusion == 'success'
|
|
5280
|
+
permissions:
|
|
5281
|
+
actions: read
|
|
5282
|
+
contents: read
|
|
5283
|
+
outputs:
|
|
5284
|
+
deployed: \${{ steps.classify.outputs.deployed }}
|
|
5285
|
+
runs-on: ubuntu-latest
|
|
5286
|
+
steps:
|
|
5287
|
+
- name: Check out immutable deployment policy
|
|
5288
|
+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
5289
|
+
with:
|
|
5290
|
+
persist-credentials: false
|
|
5291
|
+
ref: \${{ github.event.workflow_run.head_sha }}
|
|
5292
|
+
- id: profile
|
|
5293
|
+
name: Read exact committed deployment profile
|
|
5294
|
+
run: |
|
|
5295
|
+
node - <<'NODE'
|
|
5296
|
+
const { appendFileSync, readFileSync } = require('node:fs');
|
|
5297
|
+
const policy = JSON.parse(
|
|
5298
|
+
readFileSync('ebk.repository-policy.json', 'utf8'),
|
|
5299
|
+
);
|
|
5300
|
+
const profiles = new Set([
|
|
5301
|
+
'starter',
|
|
5302
|
+
'production-isolated',
|
|
5303
|
+
'fully-isolated',
|
|
5304
|
+
]);
|
|
5305
|
+
if (!profiles.has(policy.deploymentProfile)) {
|
|
5306
|
+
throw new Error('Committed deployment profile is absent or invalid.');
|
|
5307
|
+
}
|
|
5308
|
+
appendFileSync(
|
|
5309
|
+
process.env.GITHUB_OUTPUT,
|
|
5310
|
+
'profile=' + policy.deploymentProfile + '\\n',
|
|
5311
|
+
);
|
|
5312
|
+
NODE
|
|
5313
|
+
- id: classify
|
|
5314
|
+
name: Require one successful thin staging deployment job
|
|
5315
|
+
env:
|
|
5316
|
+
EBK_DEPLOYMENT_PROFILE: \${{ steps.profile.outputs.profile }}
|
|
5317
|
+
EBK_DEPLOYMENT_RUN_ID: \${{ github.event.workflow_run.id }}
|
|
5318
|
+
GH_TOKEN: \${{ github.token }}
|
|
5319
|
+
GH_REPO: \${{ github.repository }}
|
|
5320
|
+
run: |
|
|
5321
|
+
if [ "$EBK_DEPLOYMENT_PROFILE" = starter ]; then
|
|
5322
|
+
echo 'deployed=false' >> "$GITHUB_OUTPUT"
|
|
5323
|
+
exit 0
|
|
5324
|
+
fi
|
|
5325
|
+
jobs="$(gh api \
|
|
5326
|
+
"/repos/$GH_REPO/actions/runs/$EBK_DEPLOYMENT_RUN_ID/jobs?filter=latest&per_page=100")"
|
|
5327
|
+
execute_count="$(printf '%s' "$jobs" | jq \
|
|
5328
|
+
'[.jobs[] | select(.name == "native deploy staging" and .conclusion == "success")] | length')"
|
|
5329
|
+
[ "$execute_count" = 1 ] || {
|
|
5330
|
+
echo 'Deploy must contain exactly one successful thin staging execution'
|
|
5331
|
+
exit 1
|
|
5332
|
+
}
|
|
5333
|
+
echo 'deployed=true' >> "$GITHUB_OUTPUT"
|
|
5334
|
+
|
|
4768
5335
|
plan:
|
|
5336
|
+
needs: deployment-classification
|
|
4769
5337
|
if: >-
|
|
4770
|
-
(
|
|
5338
|
+
always() &&
|
|
5339
|
+
((github.event_name == 'workflow_run' &&
|
|
4771
5340
|
github.event.workflow_run.conclusion == 'success' &&
|
|
4772
5341
|
github.event.workflow_run.event == 'push' &&
|
|
4773
|
-
github.event.workflow_run.head_branch == 'develop'
|
|
4774
|
-
|
|
5342
|
+
github.event.workflow_run.head_branch == 'develop' &&
|
|
5343
|
+
needs.deployment-classification.outputs.deployed == 'true') ||
|
|
5344
|
+
github.event_name == 'workflow_dispatch')
|
|
4775
5345
|
outputs:
|
|
4776
5346
|
deployment_run_attempt: \${{ steps.identity.outputs.deployment_run_attempt }}
|
|
4777
5347
|
deployment_run_id: \${{ steps.identity.outputs.deployment_run_id }}
|
|
@@ -4878,9 +5448,10 @@ jobs:
|
|
|
4878
5448
|
EBK_SMOKE_AUTHORITY_VERSION: \${{ vars.EBK_SMOKE_AUTHORITY_VERSION }}
|
|
4879
5449
|
EBK_SMOKE_TARGETS: \${{ vars.EBK_SMOKE_TARGETS }}
|
|
4880
5450
|
EBK_SOURCE_COMMIT: \${{ needs.plan.outputs.source_commit }}
|
|
4881
|
-
PLAYWRIGHT_BROWSERS_PATH: \${{ runner.temp }}/ebk-playwright-browsers
|
|
4882
5451
|
runs-on: ubuntu-latest
|
|
4883
5452
|
steps:
|
|
5453
|
+
- name: Initialize private browser cache path
|
|
5454
|
+
run: echo "PLAYWRIGHT_BROWSERS_PATH=$RUNNER_TEMP/ebk-playwright-browsers" >> "$GITHUB_ENV"
|
|
4884
5455
|
- name: Validate credential-free preparation
|
|
4885
5456
|
run: |
|
|
4886
5457
|
test -n "$AWS_EXPECTED_ACCOUNT_ID" || { echo 'AWS_EXPECTED_ACCOUNT_ID is required'; exit 1; }
|
|
@@ -5264,14 +5835,14 @@ jobs:
|
|
|
5264
5835
|
test "$(node --version | cut -d. -f1)" = v22
|
|
5265
5836
|
- name: Run exact deployed smoke
|
|
5266
5837
|
working-directory: \${{ runner.temp }}/ebk-prepared-workspace
|
|
5267
|
-
run: node node_modules/nx/bin/nx.js run "$EBK_PROJECT:smoke-deployed"
|
|
5838
|
+
run: node node_modules/nx/dist/bin/nx.js run "$EBK_PROJECT:smoke-deployed"
|
|
5268
5839
|
- name: Verify reviewed frontend visual baseline
|
|
5269
5840
|
working-directory: \${{ runner.temp }}/ebk-prepared-workspace
|
|
5270
|
-
run: node node_modules/nx/bin/nx.js run "$EBK_PROJECT:frontend-visual"
|
|
5841
|
+
run: node node_modules/nx/dist/bin/nx.js run "$EBK_PROJECT:frontend-visual"
|
|
5271
5842
|
- name: Verify deployed frontend security headers
|
|
5272
5843
|
working-directory: \${{ runner.temp }}/ebk-prepared-workspace
|
|
5273
5844
|
run: |
|
|
5274
|
-
node node_modules/nx/bin/nx.js run \
|
|
5845
|
+
node node_modules/nx/dist/bin/nx.js run \
|
|
5275
5846
|
"$EBK_PROJECT:frontend-security-headers" -- \
|
|
5276
5847
|
--url="$EBK_SMOKE_URL"
|
|
5277
5848
|
- name: Bind exact alarm and data-integrity evidence
|
|
@@ -6642,7 +7213,7 @@ on:
|
|
|
6642
7213
|
description: Protected stage
|
|
6643
7214
|
required: true
|
|
6644
7215
|
type: choice
|
|
6645
|
-
options: [
|
|
7216
|
+
options: [${protectedWorkflowStageChoices}]
|
|
6646
7217
|
project:
|
|
6647
7218
|
description: Exact Product Slice project
|
|
6648
7219
|
required: true
|
|
@@ -6668,11 +7239,9 @@ jobs:
|
|
|
6668
7239
|
contents: read
|
|
6669
7240
|
id-token: write
|
|
6670
7241
|
env:
|
|
6671
|
-
AWS_DEPLOY_ROLE_ARN: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
|
|
6672
|
-
AWS_EXPECTED_ACCOUNT_ID: \${{ vars.AWS_EXPECTED_ACCOUNT_ID }}
|
|
6673
|
-
AWS_REGION: \${{ vars.AWS_REGION }}
|
|
6674
7242
|
EBK_ACTION: \${{ inputs.action }}
|
|
6675
7243
|
EBK_PROJECT: \${{ inputs.project }}
|
|
7244
|
+
EBK_RENDERED_PROFILE: ${deploymentProfile}
|
|
6676
7245
|
EBK_STAGE: \${{ inputs.stage }}
|
|
6677
7246
|
runs-on: ubuntu-latest
|
|
6678
7247
|
steps:
|
|
@@ -6681,19 +7250,12 @@ jobs:
|
|
|
6681
7250
|
EBK_PROTECTED_REF: \${{ github.ref }}
|
|
6682
7251
|
run: |
|
|
6683
7252
|
case "$EBK_STAGE:$EBK_PROTECTED_REF" in
|
|
6684
|
-
|
|
7253
|
+
${protectedWorkflowStageBranchCases}) ;;
|
|
6685
7254
|
*) echo 'Secret metadata stage does not match the protected branch'; exit 1 ;;
|
|
6686
7255
|
esac
|
|
6687
7256
|
case "$EBK_PROJECT" in
|
|
6688
7257
|
''|-*|*-|*[!a-z0-9-]*) echo 'Secret metadata project must be an exact normalized Slice name'; exit 1 ;;
|
|
6689
7258
|
esac
|
|
6690
|
-
test -n "$AWS_DEPLOY_ROLE_ARN" || { echo "$EBK_STAGE AWS_DEPLOY_ROLE_ARN is required"; exit 1; }
|
|
6691
|
-
test -n "$AWS_EXPECTED_ACCOUNT_ID" || { echo "$EBK_STAGE AWS_EXPECTED_ACCOUNT_ID is required"; exit 1; }
|
|
6692
|
-
test -n "$AWS_REGION" || { echo "$EBK_STAGE AWS_REGION is required"; exit 1; }
|
|
6693
|
-
case "$AWS_DEPLOY_ROLE_ARN" in
|
|
6694
|
-
arn:aws:iam::$AWS_EXPECTED_ACCOUNT_ID:role/*) ;;
|
|
6695
|
-
*) echo 'Secret metadata role does not belong to the expected account'; exit 1 ;;
|
|
6696
|
-
esac
|
|
6697
7259
|
- uses: ${checkoutAction}
|
|
6698
7260
|
with:
|
|
6699
7261
|
persist-credentials: false
|
|
@@ -6705,24 +7267,153 @@ jobs:
|
|
|
6705
7267
|
- run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile
|
|
6706
7268
|
- name: Verify managed workspace integrity before AWS authority
|
|
6707
7269
|
run: pnpm nx run ${name}:workspace-health
|
|
7270
|
+
- id: policy
|
|
7271
|
+
name: Derive committed secret metadata stage policy
|
|
7272
|
+
run: |
|
|
7273
|
+
${indentWorkflowScript(protectedSecretPolicyDerivationScript(), 10)}
|
|
7274
|
+
- id: authority
|
|
7275
|
+
name: Derive canonical committed stage authority
|
|
7276
|
+
env:
|
|
7277
|
+
EBK_DEVELOPMENT_ACCOUNT: \${{ steps.policy.outputs.development_account_id }}
|
|
7278
|
+
EBK_PRODUCTION_ACCOUNT: \${{ steps.policy.outputs.production_account_id }}
|
|
7279
|
+
EBK_REGION: \${{ steps.policy.outputs.deployment_region }}
|
|
7280
|
+
EBK_STAGING_ACCOUNT: \${{ steps.policy.outputs.staging_account_id }}
|
|
7281
|
+
run: |
|
|
7282
|
+
${indentWorkflowScript(`set -euo pipefail\n${(0, workspace_files_thin_deploy_js_1.canonicalStageAuthorityDerivationScript)()}`, 10)}
|
|
6708
7283
|
- uses: ${configureAwsAction}
|
|
6709
7284
|
with:
|
|
6710
|
-
|
|
7285
|
+
audience: \${{ steps.authority.outputs.audience }}
|
|
7286
|
+
role-to-assume: \${{ steps.authority.outputs.role_arn }}
|
|
6711
7287
|
role-session-name: ebk-secret-metadata-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
6712
|
-
aws-region: \${{
|
|
6713
|
-
- name: Verify
|
|
6714
|
-
|
|
7288
|
+
aws-region: \${{ steps.policy.outputs.deployment_region }}
|
|
7289
|
+
- name: Verify exact stage authority
|
|
7290
|
+
env:
|
|
7291
|
+
AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
|
|
7292
|
+
AWS_PARTITION: \${{ steps.authority.outputs.partition }}
|
|
7293
|
+
EBK_POLICY_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
|
|
7294
|
+
run: |
|
|
7295
|
+
${indentWorkflowScript((0, workspace_files_thin_deploy_js_1.exactStageDeployerGuardScript)('Secret metadata', '$EBK_POLICY_ACCOUNT_ID'), 10)}
|
|
6715
7296
|
- name: Reconcile value-blind metadata
|
|
7297
|
+
env:
|
|
7298
|
+
AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
|
|
7299
|
+
AWS_EXPECTED_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
|
|
7300
|
+
AWS_REGION: \${{ steps.policy.outputs.deployment_region }}
|
|
6716
7301
|
run: |
|
|
6717
7302
|
args=("--stage=$EBK_STAGE" "--region=$AWS_REGION")
|
|
6718
7303
|
if [ "$EBK_ACTION" = apply ]; then args+=("--apply"); fi
|
|
6719
7304
|
pnpm nx run "$EBK_PROJECT:secret-metadata" -- "\${args[@]}"
|
|
6720
7305
|
`,
|
|
6721
|
-
'.github/workflows/
|
|
7306
|
+
'.github/workflows/secret-initialize.yml': `name: Secret initialize
|
|
7307
|
+
|
|
7308
|
+
on:
|
|
7309
|
+
workflow_dispatch:
|
|
7310
|
+
inputs:
|
|
7311
|
+
stage:
|
|
7312
|
+
description: Protected stage
|
|
7313
|
+
required: true
|
|
7314
|
+
type: choice
|
|
7315
|
+
options: [${protectedWorkflowStageChoices}]
|
|
7316
|
+
project:
|
|
7317
|
+
description: Exact Product Slice project
|
|
7318
|
+
required: true
|
|
7319
|
+
type: string
|
|
7320
|
+
logical_name:
|
|
7321
|
+
description: Exact machine-generated secret logical name
|
|
7322
|
+
required: true
|
|
7323
|
+
type: string
|
|
7324
|
+
action:
|
|
7325
|
+
description: Value-blind initialization action
|
|
7326
|
+
required: true
|
|
7327
|
+
default: report
|
|
7328
|
+
type: choice
|
|
7329
|
+
options: [report, apply]
|
|
7330
|
+
|
|
7331
|
+
concurrency:
|
|
7332
|
+
group: secret-initialize-\${{ inputs.stage }}-\${{ inputs.project }}-\${{ inputs.logical_name }}
|
|
7333
|
+
cancel-in-progress: false
|
|
7334
|
+
|
|
7335
|
+
permissions:
|
|
7336
|
+
contents: read
|
|
7337
|
+
|
|
7338
|
+
jobs:
|
|
7339
|
+
initialize:
|
|
7340
|
+
environment: \${{ inputs.stage }}
|
|
7341
|
+
permissions:
|
|
7342
|
+
contents: read
|
|
7343
|
+
id-token: write
|
|
7344
|
+
env:
|
|
7345
|
+
EBK_ACTION: \${{ inputs.action }}
|
|
7346
|
+
EBK_LOGICAL_NAME: \${{ inputs.logical_name }}
|
|
7347
|
+
EBK_PROJECT: \${{ inputs.project }}
|
|
7348
|
+
EBK_RENDERED_PROFILE: ${deploymentProfile}
|
|
7349
|
+
EBK_STAGE: \${{ inputs.stage }}
|
|
7350
|
+
runs-on: ubuntu-latest
|
|
7351
|
+
steps:
|
|
7352
|
+
- name: Validate protected request
|
|
7353
|
+
env:
|
|
7354
|
+
EBK_PROTECTED_REF: \${{ github.ref }}
|
|
7355
|
+
run: |
|
|
7356
|
+
case "$EBK_STAGE:$EBK_PROTECTED_REF" in
|
|
7357
|
+
${protectedWorkflowStageBranchCases}) ;;
|
|
7358
|
+
*) echo 'Secret initialize stage does not match the protected branch'; exit 1 ;;
|
|
7359
|
+
esac
|
|
7360
|
+
case "$EBK_PROJECT" in
|
|
7361
|
+
''|-*|*-|*[!a-z0-9-]*) echo 'Secret initialize project must be an exact normalized Slice name'; exit 1 ;;
|
|
7362
|
+
esac
|
|
7363
|
+
[[ "$EBK_LOGICAL_NAME" =~ ^[A-Za-z][A-Za-z0-9]{0,63}$ ]] ||
|
|
7364
|
+
{ echo 'Secret initialize logical name is invalid'; exit 1; }
|
|
7365
|
+
- uses: ${checkoutAction}
|
|
7366
|
+
with:
|
|
7367
|
+
persist-credentials: false
|
|
7368
|
+
- uses: ${pnpmAction}
|
|
7369
|
+
- uses: ${setupNodeAction}
|
|
7370
|
+
with:
|
|
7371
|
+
node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
|
|
7372
|
+
cache: pnpm
|
|
7373
|
+
- run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile
|
|
7374
|
+
- name: Verify managed workspace integrity before AWS authority
|
|
7375
|
+
run: pnpm nx run ${name}:workspace-health
|
|
7376
|
+
- id: policy
|
|
7377
|
+
name: Derive committed secret initialize stage policy
|
|
7378
|
+
run: |
|
|
7379
|
+
${indentWorkflowScript(protectedSecretPolicyDerivationScript(), 10)}
|
|
7380
|
+
- id: authority
|
|
7381
|
+
name: Derive canonical committed stage authority
|
|
7382
|
+
env:
|
|
7383
|
+
EBK_DEVELOPMENT_ACCOUNT: \${{ steps.policy.outputs.development_account_id }}
|
|
7384
|
+
EBK_PRODUCTION_ACCOUNT: \${{ steps.policy.outputs.production_account_id }}
|
|
7385
|
+
EBK_REGION: \${{ steps.policy.outputs.deployment_region }}
|
|
7386
|
+
EBK_STAGING_ACCOUNT: \${{ steps.policy.outputs.staging_account_id }}
|
|
7387
|
+
run: |
|
|
7388
|
+
${indentWorkflowScript(`set -euo pipefail\n${(0, workspace_files_thin_deploy_js_1.canonicalStageAuthorityDerivationScript)()}`, 10)}
|
|
7389
|
+
- uses: ${configureAwsAction}
|
|
7390
|
+
with:
|
|
7391
|
+
audience: \${{ steps.authority.outputs.audience }}
|
|
7392
|
+
role-to-assume: \${{ steps.authority.outputs.role_arn }}
|
|
7393
|
+
role-session-name: ebk-secret-initialize-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
7394
|
+
aws-region: \${{ steps.policy.outputs.deployment_region }}
|
|
7395
|
+
- name: Verify exact stage authority
|
|
7396
|
+
env:
|
|
7397
|
+
AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
|
|
7398
|
+
AWS_PARTITION: \${{ steps.authority.outputs.partition }}
|
|
7399
|
+
EBK_POLICY_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
|
|
7400
|
+
run: |
|
|
7401
|
+
${indentWorkflowScript((0, workspace_files_thin_deploy_js_1.exactStageDeployerGuardScript)('Secret initialize', '$EBK_POLICY_ACCOUNT_ID'), 10)}
|
|
7402
|
+
- name: Initialize machine-generated secret value-blind
|
|
7403
|
+
env:
|
|
7404
|
+
AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
|
|
7405
|
+
AWS_EXPECTED_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
|
|
7406
|
+
AWS_REGION: \${{ steps.policy.outputs.deployment_region }}
|
|
7407
|
+
run: |
|
|
7408
|
+
args=("--logicalName=$EBK_LOGICAL_NAME" "--stage=$EBK_STAGE" "--region=$AWS_REGION")
|
|
7409
|
+
if [ "$EBK_ACTION" = apply ]; then args+=("--apply"); fi
|
|
7410
|
+
pnpm nx run "$EBK_PROJECT:secret-initialize" -- "\${args[@]}"
|
|
7411
|
+
`,
|
|
7412
|
+
'.github/workflows/sync-back.yml': `name: Sync protected main history
|
|
6722
7413
|
|
|
6723
7414
|
on:
|
|
6724
7415
|
workflow_run:
|
|
6725
|
-
workflows: [
|
|
7416
|
+
workflows: [Verify]
|
|
6726
7417
|
types: [completed]
|
|
6727
7418
|
|
|
6728
7419
|
permissions:
|
|
@@ -6731,8 +7422,12 @@ permissions:
|
|
|
6731
7422
|
|
|
6732
7423
|
jobs:
|
|
6733
7424
|
sync-back:
|
|
7425
|
+
# The sync carries history, not deployment evidence: open it after any
|
|
7426
|
+
# completed main run so a failed production deploy cannot lock develop
|
|
7427
|
+
# behind a sync that never opens.
|
|
6734
7428
|
if: >-
|
|
6735
|
-
github.event.workflow_run.conclusion
|
|
7429
|
+
github.event.workflow_run.conclusion != 'cancelled' &&
|
|
7430
|
+
github.event.workflow_run.event == 'push' &&
|
|
6736
7431
|
github.event.workflow_run.head_branch == 'main'
|
|
6737
7432
|
runs-on: ubuntu-latest
|
|
6738
7433
|
steps:
|
|
@@ -6746,8 +7441,8 @@ jobs:
|
|
|
6746
7441
|
gh pr create \
|
|
6747
7442
|
--head main \
|
|
6748
7443
|
--base develop \
|
|
6749
|
-
--title 'chore: sync
|
|
6750
|
-
--body 'Managed EBK sync
|
|
7444
|
+
--title 'chore(git): sync protected main to develop' \
|
|
7445
|
+
--body 'Managed EBK source-history sync. Review and merge; this does not claim or waive independent staging or production deployment evidence, and this workflow never pushes or auto-merges.'
|
|
6751
7446
|
number=$(gh pr list --state open --head main --base develop --json number --jq '.[0].number')
|
|
6752
7447
|
fi
|
|
6753
7448
|
echo "Sync-back pull request #$number is ready for review."
|
|
@@ -7005,9 +7700,16 @@ infrastructure or protected deployment policy. Preserve the ownership boundary r
|
|
|
7005
7700
|
\`.ebk/ownership.json\`: managed files belong to EBK, seeded files are generated starting
|
|
7006
7701
|
points that adopters may change, and unrecorded files are adopter-owned.
|
|
7007
7702
|
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7703
|
+
Ordinary feature work uses affected tests, typecheck, lint, and ownership checks.
|
|
7704
|
+
At a local milestone, \`pnpm verify\` checks current source through
|
|
7705
|
+
\`${name}:verify-proposed\`, including before the first Git commit. This is not
|
|
7706
|
+
protected Blueprint acceptance. The separate \`${name}:verify\` target and
|
|
7707
|
+
protected GitHub workflows retain their committed-history and trust gates.
|
|
7708
|
+
|
|
7709
|
+
Human-supplied secret values are a human-only TTY ceremony. Agents and CI must
|
|
7710
|
+
never invoke \`ebk secret set\` or \`ebk secret remove\`. They may use
|
|
7711
|
+
value-blind metadata, status, verification, and the exact generated-secret
|
|
7712
|
+
initializer, which accepts and returns no plaintext value.
|
|
7011
7713
|
|
|
7012
7714
|
## Work markers
|
|
7013
7715
|
|
|
@@ -7067,40 +7769,6 @@ export default tseslint.config(
|
|
|
7067
7769
|
rules: { 'no-console': 'error' },
|
|
7068
7770
|
},
|
|
7069
7771
|
);
|
|
7070
|
-
`,
|
|
7071
|
-
'README.md': `# ${name}
|
|
7072
|
-
|
|
7073
|
-
This workspace was created with Empire Builder Kit v1.
|
|
7074
|
-
|
|
7075
|
-
## Start here
|
|
7076
|
-
|
|
7077
|
-
1. Review \`docs/architecture.md\`.
|
|
7078
|
-
2. Review the generated Foundation before deploying it.
|
|
7079
|
-
3. Add product slices through the Empire Builder Kit Nx plugin.
|
|
7080
|
-
4. Review \`docs/repository-operating-policy.md\` and \`docs/git-workflow.md\` before enabling production.
|
|
7081
|
-
5. Read \`AGENTS.md\` and \`docs/work-markers.md\` before assigning generated customization work to agents.
|
|
7082
|
-
|
|
7083
|
-
For one-origin fleet development, initialize the workspace-pinned CLI once with
|
|
7084
|
-
\`ebk dev configure --profile=<bounded-development-profile>\`, inspect the certificate reported
|
|
7085
|
-
by \`ebk dev setup\`, and then explicitly approve \`ebk dev setup --trust\`. The supported v1
|
|
7086
|
-
origin uses the complete explicit-port HTTPS URL reported at startup; optional
|
|
7087
|
-
\`--portless\` setup is experimental. \`ebk dev doctor\` requires a real exact-host TLS
|
|
7088
|
-
handshake for trust and connectivity readiness, and reports portless access ready
|
|
7089
|
-
only after a live port-443 probe. Complete
|
|
7090
|
-
\`aws sso login --profile <bounded-development-profile>\`, then start selected Slices with
|
|
7091
|
-
\`ebk dev up billing accounts\`. The CLI derives the personal stage from the operating-system
|
|
7092
|
-
account, reads the saved profile and Region, propagates that AWS context to child Slices, and
|
|
7093
|
-
uses SST \`mono\` mode so agents and non-cursor-addressable terminals receive ordinary logs.
|
|
7094
|
-
Use \`ebk dev status\`, \`ebk dev logs\`, \`ebk dev tail\`, \`ebk dev restart\`,
|
|
7095
|
-
\`ebk dev down\`, and \`ebk dev doctor\` without supplying a stage. Setup, doctor, and the
|
|
7096
|
-
managed root Fleet target remain internal Nx-backed implementation surfaces; \`ebk dev\` is the
|
|
7097
|
-
framework-local development interface. For the development namespace, if the shell does not
|
|
7098
|
-
expose workspace binaries, \`pnpm exec ebk dev ...\` only locates the workspace-pinned binary;
|
|
7099
|
-
it is not another framework command surface. Protected Blueprint receipt review must instead
|
|
7100
|
-
use the direct installed \`.bin/ebk blueprint verification-review\` entry documented in
|
|
7101
|
-
\`docs/blueprints.md\`.
|
|
7102
|
-
|
|
7103
|
-
Workspace creation only writes source files. It does not create or change cloud resources.
|
|
7104
7772
|
`,
|
|
7105
7773
|
'docs/architecture.md': `# Workspace architecture
|
|
7106
7774
|
|
|
@@ -7114,248 +7782,18 @@ The Standard Foundation lives at \`packages/foundation\`. Product slices live be
|
|
|
7114
7782
|
\`packages/<slice-name>\` and consume versioned Foundation contracts instead of importing
|
|
7115
7783
|
Foundation implementation files.
|
|
7116
7784
|
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
7122
|
-
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
review including an approval from the committed catch-all CODEOWNER and someone other than the
|
|
7130
|
-
most recent pusher, dismissed stale reviews, resolved conversations, and merge commits. Replace
|
|
7131
|
-
the placeholder in \`.github/CODEOWNERS\` with a GitHub user or team that has repository access
|
|
7132
|
-
before bootstrap. An approval from \`github-actions[bot]\` cannot satisfy the independent
|
|
7133
|
-
code-owner gate; the Actions repository setting is needed to create the managed sync-back pull
|
|
7134
|
-
request, not to approve it.
|
|
7135
|
-
Direct updates, force pushes, deletion, squash merging, and rebase merging are prohibited.
|
|
7136
|
-
GitHub rulesets, environments, and AWS IAM are the authorization boundaries; local hooks are
|
|
7137
|
-
only defense in depth.
|
|
7138
|
-
|
|
7139
|
-
The \`staging\` environment accepts only \`develop\`; \`production\` accepts only \`main\`.
|
|
7140
|
-
The committed \`productionApproval.mode\` selects one of two production authorization
|
|
7141
|
-
contracts:
|
|
7142
|
-
|
|
7143
|
-
- \`promotion-pr\` is compatible with private repositories on GitHub Team. The required
|
|
7144
|
-
non-author CODEOWNER approval on the exact \`develop\` to \`main\` promotion pull request
|
|
7145
|
-
is the production authorization. Merging that pull request starts production deployment;
|
|
7146
|
-
there is no second EBK-managed environment approval or wait timer. The approver may also be
|
|
7147
|
-
the human whose merge triggers deployment. Roll back through a reviewed revert on the
|
|
7148
|
-
protected branches and let the resulting \`main\` push redeploy. A repository write actor who
|
|
7149
|
-
can run Actions may also manually dispatch \`deploy\`, \`refresh\`, or \`unlock\` against the
|
|
7150
|
-
already approved \`main\` tip without another human gate. That residual cannot introduce
|
|
7151
|
-
unreviewed source; production \`remove\` remains separately variable-gated. Restrict write and
|
|
7152
|
-
Actions access, audit dispatches, or select \`environment-reviewer\` when every production
|
|
7153
|
-
action needs separate approval.
|
|
7154
|
-
- \`environment-reviewer\` adds a separate production environment reviewer with self-review
|
|
7155
|
-
prevention. A private repository needs GitHub Enterprise capability for this mode. Bootstrap
|
|
7156
|
-
inspects that capability and fails closed on an unsupported plan; it never silently changes
|
|
7157
|
-
the committed mode.
|
|
7158
|
-
|
|
7159
|
-
Both modes retain required checks, a non-author CODEOWNER approval, no ruleset bypass, the
|
|
7160
|
-
literal production environment, and its branch-scoped credentials. The base \`staging\` and
|
|
7161
|
-
\`production\` environments own deployment, smoke, and ECR evidence roles plus their
|
|
7162
|
-
variables; production also owns the staging-registry promotion pull role. Dedicated
|
|
7163
|
-
\`infrastructure-plan-<stage>\` environments gate read-only role use. Separate
|
|
7164
|
-
\`async-recovery-<stage>\`, \`performance-aggregate-<stage>\`,
|
|
7165
|
-
\`performance-cost-<stage>\`, \`performance-load-<stage>\`, and
|
|
7166
|
-
\`reliability-read-<stage>\` environments own only the role and variables required by that
|
|
7167
|
-
capability. Infrastructure-plan jobs use a repository-variable read-only role/input inventory;
|
|
7168
|
-
an independent clean audit job assumes that exact environment-subject role and verifies its
|
|
7169
|
-
trust and complete inline policy before any repository-backed plan job may assume it. Every
|
|
7170
|
-
\`*-staging\` environment accepts only \`develop\`; every
|
|
7171
|
-
\`*-production\` environment accepts only \`main\`; reviewer, self-review, and wait protection
|
|
7172
|
-
matches the corresponding base stage. The credential-free \`npm-prerelease\` environment
|
|
7173
|
-
accepts only \`main\` and contains no secrets or variables. In Team-compatible
|
|
7174
|
-
\`promotion-pr\` mode, protected-main review plus the exact current-main tag binding is
|
|
7175
|
-
publication authorization, so it has no reviewer or wait rule. In
|
|
7176
|
-
\`environment-reviewer\` mode it mirrors production protection. Never add repository- or
|
|
7177
|
-
organization-visible fallbacks for protected values. Keep repository default workflow
|
|
7178
|
-
permissions read-only; generated jobs request only the additional permissions they need.
|
|
7179
|
-
|
|
7180
|
-
\`EBK_FLEET_ORIGIN\` is the sole protected deployment and smoke origin. Read-only plan jobs
|
|
7181
|
-
enter only their dedicated \`infrastructure-plan-<stage>\` environment; their stage-named
|
|
7182
|
-
repository variables \`EBK_STAGING_FLEET_ORIGIN\` and
|
|
7183
|
-
\`EBK_PRODUCTION_FLEET_ORIGIN\` remain plan inputs only. Before mutation, each
|
|
7184
|
-
base-environment-protected deployment requires
|
|
7185
|
-
its \`EBK_FLEET_ORIGIN\` to equal the origin retained by the plan.
|
|
7186
|
-
|
|
7187
|
-
\`AWS_ASYNC_RECOVERY_ROLE_ARN\` is a separate mutation role. Trust only this
|
|
7188
|
-
repository's exact \`async-recovery-<stage>\` GitHub OIDC subject and grant only
|
|
7189
|
-
the recovery operations/resources declared by generated capability adapters.
|
|
7190
|
-
|
|
7191
|
-
\`AWS_PERFORMANCE_LOAD_ROLE_ARN\` is a separate read-only OIDC role. Trust only
|
|
7192
|
-
this repository's exact \`performance-load-<stage>\` GitHub OIDC subject.
|
|
7193
|
-
Set that environment's \`EBK_PERFORMANCE_ORIGINS_JSON\` to a JSON object that
|
|
7194
|
-
maps every reviewed Slice to the exact \`performanceEndpoint\` emitted by its
|
|
7195
|
-
current deployment; never copy the map to repository scope or substitute the
|
|
7196
|
-
fleet web origin. The workflow selects only its requested Slice, and the
|
|
7197
|
-
committed load plan must name that same origin and exact reserved Slice path.
|
|
7198
|
-
Grant \`sts:GetCallerIdentity\` and \`secretsmanager:GetSecretValue\` only on
|
|
7199
|
-
\`arn:<partition>:secretsmanager:<region>:<account>:secret:ebk/<stage>/<slice>/*\` for
|
|
7200
|
-
reviewed Slices. Do not grant secret writes, deployment, CloudFormation, SST,
|
|
7201
|
-
or CloudWatch Logs access. The protected workflow never logs or uploads HMAC
|
|
7202
|
-
material and removes its new job-scoped 0600 file after cleanup.
|
|
7203
|
-
Bootstrap verifies environment-only secret placement, not the role's hidden
|
|
7204
|
-
AWS trust or permission policy.
|
|
7205
|
-
|
|
7206
|
-
\`AWS_PERFORMANCE_AGGREGATE_ROLE_ARN\` is a separate adopter-owned read-only
|
|
7207
|
-
OIDC role. Trust only this repository's exact
|
|
7208
|
-
\`performance-aggregate-<stage>\` GitHub OIDC subject. Grant
|
|
7209
|
-
\`sts:GetCallerIdentity\` plus only the exact data-read actions and resource
|
|
7210
|
-
ARNs required by the adopted \`performance/aggregate-count-provider.ts\`.
|
|
7211
|
-
Do not grant writes, deployment, CloudFormation, SST, IAM, Cost Explorer, or
|
|
7212
|
-
unrelated data access. The generated workflow validates the protected Deploy
|
|
7213
|
-
artifact before role assumption, verifies the STS account and environment
|
|
7214
|
-
Region, withholds the OIDC bearer, and clears the AWS session before artifact
|
|
7215
|
-
retention. Bootstrap verifies environment-only secret placement, not the
|
|
7216
|
-
role's hidden AWS trust or least-privilege permission policy.
|
|
7217
|
-
|
|
7218
|
-
\`AWS_PERFORMANCE_COST_ROLE_ARN\` is a separate read-only OIDC role. Trust only
|
|
7219
|
-
this repository's exact \`performance-cost-<stage>\` GitHub OIDC subject.
|
|
7220
|
-
Grant only \`sts:GetCallerIdentity\`, \`ce:ListCostAllocationTags\`, and
|
|
7221
|
-
\`ce:GetCostAndUsage\`; Cost Explorer
|
|
7222
|
-
queries are account-global, while the retained evidence still binds the
|
|
7223
|
-
selected resource Region. Do not grant budget, anomaly-monitor, tag, billing,
|
|
7224
|
-
deployment, CloudFormation, SST, or other mutation authority. Bootstrap verifies
|
|
7225
|
-
environment-only secret placement, not the role's hidden AWS trust or
|
|
7226
|
-
permission policy.
|
|
7227
|
-
|
|
7228
|
-
The framework manages
|
|
7229
|
-
\`.github/workflows/performance-aggregate-counts.yml\`, its exact Deploy
|
|
7230
|
-
binding, evidence wrapper, artifact name, and canonical schema. The seeded
|
|
7231
|
-
\`performance/aggregate-count-provider.ts\` remains adopter-owned and fails
|
|
7232
|
-
closed until it returns only the reviewed business counts and provider request
|
|
7233
|
-
reference. The provider runs only with the dedicated aggregate-read role after
|
|
7234
|
-
the framework validates the exact protected release and STS account. The
|
|
7235
|
-
workflow binds those untrusted counts to the environment-owned account/Region,
|
|
7236
|
-
closed period, exact current protected release, Slice, stage, and workspace
|
|
7237
|
-
and records the dedicated OIDC access mode and adopter-owned least-privilege
|
|
7238
|
-
read contract before it emits the single
|
|
7239
|
-
\`aggregate-count-<stage>-<slice>-<run>-<attempt>\` artifact. The framework
|
|
7240
|
-
does not invent counts or copy them from request-level dimensions.
|
|
7241
|
-
|
|
7242
|
-
\`performance-cost-actual.yml\` treats supplied run IDs and artifact names only
|
|
7243
|
-
as selectors. Before OIDC it queries GitHub for the fixed aggregate workflow
|
|
7244
|
-
and generated \`Deploy\` workflow runs, requiring the same repository,
|
|
7245
|
-
candidate, protected branch, successful conclusion, allowed event, exact
|
|
7246
|
-
workflow name/path, and one exact nonexpired artifact. It derives the compact
|
|
7247
|
-
performance release binding from that Deploy artifact and separately verifies
|
|
7248
|
-
the independently approved aggregate-count and release-binding byte digests.
|
|
7249
|
-
|
|
7250
|
-
\`AWS_RELIABILITY_READ_ROLE_ARN\` is a separate read-only OIDC role. Trust only
|
|
7251
|
-
this repository's exact \`reliability-read-<stage>\` GitHub OIDC subject.
|
|
7252
|
-
Grant \`sts:GetCallerIdentity\`, \`cloudwatch:DescribeAlarms\`,
|
|
7253
|
-
\`cloudwatch:DescribeAlarmHistory\`, \`cloudwatch:GetMetricData\`,
|
|
7254
|
-
\`ecs:DescribeServices\`, and \`states:DescribeExecution\`. Scope ECS and Step
|
|
7255
|
-
Functions resources to the reviewed Slice services, state machines, and
|
|
7256
|
-
executions wherever IAM supports resource-level constraints. Do not grant log
|
|
7257
|
-
reads, deployment, CloudFormation, SST, or any AWS mutation.
|
|
7258
|
-
|
|
7259
|
-
The generated \`reliability-read.yml\` workflow selects the newest nonexpired
|
|
7260
|
-
binding artifact from a successful deployed-smoke run, reconciles it to the
|
|
7261
|
-
current successful protected deployment, and checks out that exact commit
|
|
7262
|
-
before assuming the role. Callers choose only a bounded operation and workload;
|
|
7263
|
-
they cannot supply the AWS account, Region, role, or binding path. The binding
|
|
7264
|
-
allowlists exact alarm names, API ID, ECS services, and Step Functions
|
|
7265
|
-
resources before the adapter makes a service call. Bootstrap verifies
|
|
7266
|
-
environment-only secret placement, not the role's hidden AWS trust or
|
|
7267
|
-
permission policy.
|
|
7268
|
-
|
|
7269
|
-
The async-recovery, performance-load, performance-cost, and reliability-read
|
|
7270
|
-
jobs have job-scoped \`id-token: write\`. They install the frozen dependency
|
|
7271
|
-
graph with lifecycle scripts and pnpm hooks disabled before assuming AWS
|
|
7272
|
-
authority. Immediately after the one pinned credential action, they blank the
|
|
7273
|
-
OIDC request URL and bearer for future steps and explicitly shadow both values
|
|
7274
|
-
empty on every later step. After bounded AWS use, they blank current AWS
|
|
7275
|
-
credential variables before evidence upload. This withholds credentials from
|
|
7276
|
-
later processes; it does not claim that GitHub revokes the job-level
|
|
7277
|
-
permission.
|
|
7278
|
-
|
|
7279
|
-
\`AWS_SMOKE_ROLE_ARN\` is separate from deployment authority. Trust only this
|
|
7280
|
-
repository's GitHub OIDC subject for the matching protected environment. Grant
|
|
7281
|
-
only run-scoped Cognito principal setup/cleanup on reviewed user-pool ARNs and
|
|
7282
|
-
\`logs:FilterLogEvents\` on exact
|
|
7283
|
-
\`/ebk/<stage>/<slice>/event-consumer\` log-group ARNs. Do not grant
|
|
7284
|
-
CloudFormation, SST mutation, broad Logs query, \`GetLogEvents\`,
|
|
7285
|
-
\`StartQuery\`, or \`logs:Unmask\`. Bootstrap verifies environment-only secret
|
|
7286
|
-
inventory but never creates or reads the secret, materializes the role, or
|
|
7287
|
-
attests its hidden policy.
|
|
7288
|
-
|
|
7289
|
-
Run the GitHub bootstrap in report mode first. Source upgrades never alter remote GitHub
|
|
7290
|
-
settings. Apply a reviewed report only after \`develop\` exists and the candidate's exact
|
|
7291
|
-
\`policy\` and \`verify\` check contexts have run.
|
|
7292
|
-
|
|
7293
|
-
Set \`GH_REPO=<owner>/<repository>\` and a suitable \`GH_TOKEN\`, then run
|
|
7294
|
-
\`pnpm nx run ${name}:github-bootstrap\`. Review the deterministic report before rerunning
|
|
7295
|
-
with \`--apply\`. The command never creates branches, credentials, secrets, or AWS resources.
|
|
7296
|
-
Supply \`--npmPrereleaseReviewerIds=<id,...>\` only in
|
|
7297
|
-
\`environment-reviewer\` mode when intentionally setting a distinct npm
|
|
7298
|
-
mutation reviewer set.
|
|
7299
|
-
Supply \`--productionReviewerIds=<id,...>\` only when committed policy selects
|
|
7300
|
-
\`environment-reviewer\`; it is rejected in \`promotion-pr\` mode.
|
|
7301
|
-
|
|
7302
|
-
Install the optional defense-in-depth pre-push hook explicitly with
|
|
7303
|
-
\`pnpm nx run ${name}:install-git-hooks\`. It refuses to overwrite an unrelated existing hook.
|
|
7304
|
-
|
|
7305
|
-
The sync-back workflow uses only \`GITHUB_TOKEN\` and never pushes or auto-merges. GitHub may
|
|
7306
|
-
place its pull-request workflows in an approval-required state. Approve them; if no run
|
|
7307
|
-
materializes, close and reopen the pull request as a human and then approve it.
|
|
7308
|
-
`,
|
|
7309
|
-
'docs/repository-operating-policy.md': `# Repository Operating Policy
|
|
7310
|
-
|
|
7311
|
-
\`ebk.repository-policy.json\` is the reviewed source of truth for production access,
|
|
7312
|
-
production approval, per-Slice readiness, and the one shared production Foundation profile.
|
|
7313
|
-
New workspaces start with production disabled and explicit Team-compatible
|
|
7314
|
-
\`productionApproval.mode: "promotion-pr"\`. A missing approval mode or Slice entry fails
|
|
7315
|
-
closed; no GitHub capability is allowed to choose a mode implicitly.
|
|
7316
|
-
|
|
7317
|
-
The named repository modes are source-writing presets, not runtime state. Report a change
|
|
7318
|
-
first and inspect the complete current/next policy:
|
|
7319
|
-
|
|
7320
|
-
\`\`\`bash
|
|
7321
|
-
pnpm nx g @empire-builder-kit/nx:repository-policy \\
|
|
7322
|
-
--preset=controlled-production --slices=billing
|
|
7323
|
-
\`\`\`
|
|
7324
|
-
|
|
7325
|
-
Apply the reviewed source change separately with \`--apply\`. This command never contacts AWS
|
|
7326
|
-
or GitHub. Use \`--status=blocked|pilot|ready\` for explicit Slice transitions and
|
|
7327
|
-
\`--profile=cost-optimized|standard\` for an explicit shared Foundation decision.
|
|
7328
|
-
Use \`--productionApprovalMode=promotion-pr|environment-reviewer\` for an explicit approval
|
|
7329
|
-
transition. This changes committed policy only; a separate bootstrap report/apply reconciles
|
|
7330
|
-
remote GitHub settings.
|
|
7331
|
-
|
|
7332
|
-
Validation requires both an explicit production approval mode and an explicit Foundation
|
|
7333
|
-
profile. It does not infer omitted values. Use \`promotion-pr\` for a private GitHub Team
|
|
7334
|
-
repository. Use \`environment-reviewer\` only when the repository plan can enforce a separate
|
|
7335
|
-
environment reviewer.
|
|
7336
|
-
|
|
7337
|
-
- \`blocked\` prohibits production create, update, and migration.
|
|
7338
|
-
- \`pilot\` permits protected evidence gathering without claiming normal latency or maturity.
|
|
7339
|
-
- \`ready\` records an adopter decision after its evidence and review; it is not inferred.
|
|
7340
|
-
- removal and unlock remain explicit protected recovery paths so disabled policy cannot strand
|
|
7341
|
-
resources.
|
|
7342
|
-
|
|
7343
|
-
Protected production deploy and refresh validate the complete Foundation/Slice set before AWS
|
|
7344
|
-
access. The Foundation is always ordered first. The set is derived from committed policy and
|
|
7345
|
-
cannot be narrowed with an ad hoc project argument.
|
|
7346
|
-
|
|
7347
|
-
\`standard\` keeps Aurora warm at 2–16 ACU. \`cost-optimized\` is a prerelease candidate at
|
|
7348
|
-
0–4 ACU with a five-minute auto-pause and potential cold-resume latency. Both keep managed NAT,
|
|
7349
|
-
30-day backups, deletion protection, retained data, and the protected GitHub/OIDC/AWS path.
|
|
7350
|
-
Changing policy affects only later lifecycle requests; it never deploys or removes resources.
|
|
7351
|
-
Changing profile requires a separate reviewed Foundation deployment and retained operational
|
|
7352
|
-
evidence.
|
|
7353
|
-
|
|
7354
|
-
\`EBK_PRODUCTION_FOUNDATION_PROFILE\` is reserved managed executor input. The lifecycle
|
|
7355
|
-
executor derives it from validated committed policy immediately before SST and removes any
|
|
7356
|
-
ambient shell value. Do not set it as a local, repository, organization, or environment
|
|
7357
|
-
variable. Incubation keeps the last configured profile so protected remove and unlock can still
|
|
7358
|
-
render existing production resources; production deploy remains disabled.
|
|
7785
|
+
This workspace's \`${deploymentProfile}\` profile activates only:
|
|
7786
|
+
${activeStages.join(', ')}. Each active canonical consumer stage uses the matching Foundation.
|
|
7787
|
+
Inactive canonical stages are rejected. Framework-derived personal stages may use only the
|
|
7788
|
+
\`development\` Foundation; generic preview stages are unsupported.
|
|
7789
|
+
|
|
7790
|
+
Every active canonical stage owns a separate Router/domain, Cognito user pool,
|
|
7791
|
+
event bus, secret namespace, IAM boundary, logs, queues, ECR namespace, and
|
|
7792
|
+
stage-qualified SSM discovery contract. Discovery rejects a record for an inactive or different
|
|
7793
|
+
canonical stage. DynamoDB tables belong to individual Slices/tasks, not one global application
|
|
7794
|
+
table. Declared relational features add an independent Aurora cluster per canonical stage,
|
|
7795
|
+
with separate logical databases and migration histories per Slice. Existing relational resources
|
|
7796
|
+
remain declared on upgrade and after removing their last consumer; that is not database retirement.
|
|
7359
7797
|
`,
|
|
7360
7798
|
'docs/work-markers.md': `# Agent work markers
|
|
7361
7799
|
|
|
@@ -7416,7 +7854,7 @@ deploy or refresh before AWS access. Protected remove and unlock remain usable f
|
|
|
7416
7854
|
There is no local or silent bypass in this prerelease contract; any future waiver mechanism
|
|
7417
7855
|
must be committed, reviewable, and auditable.
|
|
7418
7856
|
`,
|
|
7419
|
-
'ebk.repository-policy.json': json(
|
|
7857
|
+
'ebk.repository-policy.json': json(initialRepositoryPolicy),
|
|
7420
7858
|
'nx.json': json({
|
|
7421
7859
|
$schema: './node_modules/nx/schemas/nx-schema.json',
|
|
7422
7860
|
defaultBase: 'develop',
|
|
@@ -7456,6 +7894,7 @@ must be committed, reviewable, and auditable.
|
|
|
7456
7894
|
inputs: ['default', '^production'],
|
|
7457
7895
|
},
|
|
7458
7896
|
},
|
|
7897
|
+
analytics: false,
|
|
7459
7898
|
neverConnectToCloud: true,
|
|
7460
7899
|
useDaemonProcess: false,
|
|
7461
7900
|
}),
|
|
@@ -7471,23 +7910,25 @@ must be committed, reviewable, and auditable.
|
|
|
7471
7910
|
scripts: {
|
|
7472
7911
|
build: `nx run-many -t build --exclude=${name}`,
|
|
7473
7912
|
format: 'nx format:write',
|
|
7474
|
-
'format:check': 'nx format:check',
|
|
7913
|
+
'format:check': 'nx format:check --all',
|
|
7475
7914
|
graph: 'nx graph',
|
|
7476
7915
|
health: `nx run ${name}:workspace-health`,
|
|
7477
7916
|
lint: `nx run-many -t lint --exclude=${name}`,
|
|
7478
7917
|
test: `nx run-many -t test --exclude=${name}`,
|
|
7479
7918
|
typecheck: `nx run-many -t typecheck --exclude=${name}`,
|
|
7480
|
-
verify: `nx run ${name}:verify && nx run-many -t verify --exclude=${name}`,
|
|
7919
|
+
verify: `nx format:check --all && nx run ${name}:verify-proposed && nx run-many -t verify --exclude=${name}`,
|
|
7481
7920
|
},
|
|
7482
7921
|
dependencies: {
|
|
7483
7922
|
'@empire-builder-kit/runtime': frameworkVersion,
|
|
7484
7923
|
},
|
|
7485
7924
|
devDependencies: {
|
|
7486
7925
|
'@aws-sdk/client-iam': TOOL_VERSIONS.awsSdk,
|
|
7926
|
+
'@aws-sdk/client-rds': TOOL_VERSIONS.awsSdk,
|
|
7487
7927
|
'@aws-sdk/client-s3': TOOL_VERSIONS.awsSdk,
|
|
7488
7928
|
'@aws-sdk/client-secrets-manager': TOOL_VERSIONS.awsSdk,
|
|
7489
7929
|
'@aws-sdk/client-ssm': TOOL_VERSIONS.awsSdk,
|
|
7490
7930
|
'@aws-sdk/client-sts': TOOL_VERSIONS.awsSdk,
|
|
7931
|
+
'@aws-sdk/credential-providers': TOOL_VERSIONS.awsSdk,
|
|
7491
7932
|
'@eslint/js': TOOL_VERSIONS.eslintJs,
|
|
7492
7933
|
'@empire-builder-kit/nx': frameworkVersion,
|
|
7493
7934
|
'@types/node': TOOL_VERSIONS.nodeTypes,
|
|
@@ -7520,6 +7961,15 @@ must be committed, reviewable, and auditable.
|
|
|
7520
7961
|
protectedRef: 'refs/remotes/origin/develop',
|
|
7521
7962
|
},
|
|
7522
7963
|
},
|
|
7964
|
+
'blueprint-trust-check-main': {
|
|
7965
|
+
executor: '@empire-builder-kit/nx:blueprint-trust',
|
|
7966
|
+
options: {
|
|
7967
|
+
operation: 'adopt',
|
|
7968
|
+
output: '.ebk/evidence/supply-chain/blueprint-trust.json',
|
|
7969
|
+
protectedBranch: 'main',
|
|
7970
|
+
protectedRef: 'refs/remotes/origin/main',
|
|
7971
|
+
},
|
|
7972
|
+
},
|
|
7523
7973
|
'blueprint-trust-check-protected': {
|
|
7524
7974
|
executor: '@empire-builder-kit/nx:blueprint-trust',
|
|
7525
7975
|
options: {
|
|
@@ -7562,6 +8012,23 @@ must be committed, reviewable, and auditable.
|
|
|
7562
8012
|
executor: 'nx:run-commands',
|
|
7563
8013
|
options: { command: 'node tools/install-ebk-hooks.mjs' },
|
|
7564
8014
|
},
|
|
8015
|
+
'stage-authority-render': {
|
|
8016
|
+
cache: false,
|
|
8017
|
+
executor: 'nx:run-commands',
|
|
8018
|
+
outputs: ['{workspaceRoot}/.ebk/authority/{args.stage}/*'],
|
|
8019
|
+
options: {
|
|
8020
|
+
command: 'node tools/stage-authority-render.mjs',
|
|
8021
|
+
forwardAllArgs: true,
|
|
8022
|
+
},
|
|
8023
|
+
},
|
|
8024
|
+
'deploy-local': {
|
|
8025
|
+
cache: false,
|
|
8026
|
+
dependsOn: ['workspace-health'],
|
|
8027
|
+
executor: '@empire-builder-kit/nx:local-deploy',
|
|
8028
|
+
options: {
|
|
8029
|
+
stage: 'development',
|
|
8030
|
+
},
|
|
8031
|
+
},
|
|
7565
8032
|
'supply-chain-check': {
|
|
7566
8033
|
executor: 'nx:run-commands',
|
|
7567
8034
|
options: {
|
|
@@ -7585,64 +8052,27 @@ must be committed, reviewable, and auditable.
|
|
|
7585
8052
|
executor: '@empire-builder-kit/nx:work-markers',
|
|
7586
8053
|
options: {},
|
|
7587
8054
|
},
|
|
7588
|
-
'workload-role-candidate-report': {
|
|
7589
|
-
executor: 'nx:run-commands',
|
|
7590
|
-
options: {
|
|
7591
|
-
command: 'node tools/workload-role-baseline/report-candidate-bundle.mjs',
|
|
7592
|
-
forwardAllArgs: true,
|
|
7593
|
-
},
|
|
7594
|
-
},
|
|
7595
|
-
'workload-role-provisioner-build': {
|
|
7596
|
-
executor: 'nx:run-commands',
|
|
7597
|
-
options: {
|
|
7598
|
-
command: 'node tools/workload-role-baseline/build.mjs',
|
|
7599
|
-
},
|
|
7600
|
-
},
|
|
7601
8055
|
verify: {
|
|
7602
8056
|
dependsOn: ['blueprint-trust-check', 'supply-chain-check'],
|
|
7603
8057
|
executor: 'nx:run-commands',
|
|
7604
8058
|
options: { command: 'node tools/workspace-health.mjs' },
|
|
7605
8059
|
},
|
|
8060
|
+
'verify-proposed': {
|
|
8061
|
+
dependsOn: ['supply-chain-check'],
|
|
8062
|
+
executor: 'nx:run-commands',
|
|
8063
|
+
options: { command: 'node tools/workspace-health.mjs' },
|
|
8064
|
+
},
|
|
7606
8065
|
'workspace-health': {
|
|
7607
8066
|
executor: 'nx:run-commands',
|
|
7608
8067
|
options: { command: 'node tools/workspace-health.mjs' },
|
|
7609
8068
|
},
|
|
7610
8069
|
},
|
|
7611
8070
|
}),
|
|
7612
|
-
'packages/foundation/
|
|
7613
|
-
|
|
7614
|
-
This project owns the workspace-wide AWS platform for each canonical \`development\`,
|
|
7615
|
-
\`staging\`, or \`production\` stage. It is a separate SST state boundary from product slices.
|
|
7616
|
-
Never deploy it at a framework-derived personal stage; that consumer maps to
|
|
7617
|
-
\`development\`.
|
|
7618
|
-
|
|
7619
|
-
It provides the v1 network, routing, container compute, Postgres, identity, events,
|
|
7620
|
-
observability, configuration, and deployment capabilities. Product slices consume its
|
|
7621
|
-
versioned contract; they do not import files from \`infra/\`.
|
|
7622
|
-
|
|
7623
|
-
Workspace creation does not deploy this project. Review \`docs/costs.md\`,
|
|
7624
|
-
\`docs/security.md\`, and \`docs/runbooks.md\` before the first deployment.
|
|
7625
|
-
|
|
7626
|
-
Set \`EBK_ROOT_DOMAIN\` before deploying when Route 53 in the target account owns a public
|
|
7627
|
-
domain. Without it, the shared Router uses its functional provider URL.
|
|
7628
|
-
`,
|
|
7629
|
-
'packages/foundation/contracts/foundation.json': json({
|
|
7630
|
-
contractVersion: '1.0.0',
|
|
7631
|
-
capabilities: [
|
|
7632
|
-
'network',
|
|
7633
|
-
'routing',
|
|
7634
|
-
'container-compute',
|
|
7635
|
-
'postgres',
|
|
7636
|
-
'identity',
|
|
7637
|
-
'events',
|
|
7638
|
-
'observability',
|
|
7639
|
-
'configuration',
|
|
7640
|
-
'deployment',
|
|
7641
|
-
],
|
|
7642
|
-
}),
|
|
8071
|
+
'packages/foundation/contracts/foundation.json': json(foundationPlan.descriptor),
|
|
7643
8072
|
'packages/foundation/contracts/costs.json': json({
|
|
7644
|
-
schemaVersion:
|
|
8073
|
+
schemaVersion: 3,
|
|
7645
8074
|
aurora: {
|
|
8075
|
+
declared: foundationPlan.resources.postgres,
|
|
7646
8076
|
engine: index_js_4.FOUNDATION_AURORA_ENGINE_FAMILY,
|
|
7647
8077
|
engineVersion: index_js_4.FOUNDATION_AURORA_ENGINE_VERSION,
|
|
7648
8078
|
},
|
|
@@ -7650,19 +8080,29 @@ domain. Without it, the shared Router uses its functional provider URL.
|
|
|
7650
8080
|
productionProfileSource: 'ebk.repository-policy.json',
|
|
7651
8081
|
stages: {
|
|
7652
8082
|
development: {
|
|
7653
|
-
aurora:
|
|
8083
|
+
aurora: foundationPlan.resources.postgres
|
|
8084
|
+
? 'scale-to-zero'
|
|
8085
|
+
: 'not-declared',
|
|
7654
8086
|
dataRetention: 'retained',
|
|
7655
|
-
nat: 'ec2',
|
|
7656
|
-
},
|
|
7657
|
-
staging: {
|
|
7658
|
-
aurora: 'scale-to-zero',
|
|
7659
|
-
dataRetention: 'retained',
|
|
7660
|
-
nat: 'ec2',
|
|
8087
|
+
nat: foundationPlan.resources.network ? 'ec2' : 'none',
|
|
7661
8088
|
},
|
|
8089
|
+
...(deploymentProfile === 'starter'
|
|
8090
|
+
? {}
|
|
8091
|
+
: {
|
|
8092
|
+
staging: {
|
|
8093
|
+
aurora: foundationPlan.resources.postgres
|
|
8094
|
+
? 'scale-to-zero'
|
|
8095
|
+
: 'not-declared',
|
|
8096
|
+
dataRetention: 'retained',
|
|
8097
|
+
nat: foundationPlan.resources.network ? 'ec2' : 'none',
|
|
8098
|
+
},
|
|
8099
|
+
}),
|
|
7662
8100
|
production: {
|
|
7663
|
-
aurora:
|
|
8101
|
+
aurora: foundationPlan.resources.postgres
|
|
8102
|
+
? 'repository-policy'
|
|
8103
|
+
: 'not-declared',
|
|
7664
8104
|
dataRetention: 'retained-and-protected',
|
|
7665
|
-
nat: 'managed',
|
|
8105
|
+
nat: foundationPlan.resources.network ? 'managed' : 'none',
|
|
7666
8106
|
},
|
|
7667
8107
|
},
|
|
7668
8108
|
usagePriced: [
|
|
@@ -7677,68 +8117,51 @@ domain. Without it, the shared Router uses its functional provider URL.
|
|
|
7677
8117
|
'packages/foundation/contracts/frontend-security-headers.json': json((0, index_js_2.foundationFrontendSecurityHeaderPolicy)()),
|
|
7678
8118
|
'packages/foundation/docs/costs.md': `# Foundation cost contract
|
|
7679
8119
|
|
|
7680
|
-
The
|
|
8120
|
+
The default Foundation has no Aurora, VPC/NAT, or ECS cluster. Slice-scoped DynamoDB
|
|
8121
|
+
is the first persistence choice. A relational requirement adds Aurora and its
|
|
8122
|
+
network/container migration dependencies; an explicit network workload can also
|
|
8123
|
+
require those dependencies without SQL. Previously provisioned capabilities stay
|
|
8124
|
+
declared until an explicit retirement operation, even with no remaining consumers.
|
|
7681
8125
|
The machine-readable stage inventory is \`../contracts/costs.json\`; keep this
|
|
7682
8126
|
seeded decision record aligned with organization-specific budgets and alerts.
|
|
7683
8127
|
|
|
7684
|
-
-
|
|
7685
|
-
|
|
7686
|
-
-
|
|
7687
|
-
Operating Policy profile selects
|
|
7688
|
-
|
|
8128
|
+
- When network/SQL is declared, development uses EC2 NAT, a development bastion,
|
|
8129
|
+
and Aurora scale-to-zero.
|
|
8130
|
+
${deploymentProfile === 'starter' ? '' : '- Declared staging network/SQL uses EC2 NAT and Aurora scale-to-zero by default.\n'}- Declared production networking uses managed NAT; production retains protected data. Its explicit Repository
|
|
8131
|
+
Operating Policy profile selects cost-optimized (0–2 ACUs, pause after five idle
|
|
8132
|
+
minutes), balanced (0.5–4 ACUs, always warm), or standard (2–16 ACUs, always warm).
|
|
8133
|
+
Newly generated repositories default to cost-optimized in every deployment profile.
|
|
8134
|
+
Cold resume adds latency; measure it for the workload and validate
|
|
8135
|
+
connection retries, request timeouts, migrations, and background polling.
|
|
8136
|
+
Capacity is shared by all Slices. Open connections can prevent pausing; storage
|
|
8137
|
+
and managed NAT remain billable while Aurora compute is paused.
|
|
8138
|
+
Existing 0.5–4 cost-optimized adopters must explicitly select balanced before
|
|
8139
|
+
upgrade, then opt into cost-optimized separately if cold starts are acceptable.
|
|
7689
8140
|
- ECS, EventBridge, Cognito, SNS, SSM, and CloudWatch are primarily usage-priced; logs,
|
|
7690
8141
|
metrics, alarms, and retained data still require active cost review.
|
|
7691
8142
|
|
|
7692
8143
|
Run \`foundation:remove\` only for disposable stages. Production is retained and protected by
|
|
7693
8144
|
the SST configuration.
|
|
7694
|
-
`,
|
|
7695
|
-
'packages/foundation/docs/runbooks.md': `# Foundation runbooks
|
|
7696
|
-
|
|
7697
|
-
## Deployment
|
|
7698
|
-
|
|
7699
|
-
Use Nx for every supported lifecycle action:
|
|
7700
|
-
|
|
7701
|
-
- \`pnpm nx run foundation:deploy --stage=development\`
|
|
7702
|
-
- \`pnpm nx run foundation:refresh --stage=development\`
|
|
7703
|
-
- \`pnpm nx run foundation:unlock --stage=development\`
|
|
7704
|
-
- \`pnpm nx run foundation:remove --stage=development\`
|
|
7705
|
-
|
|
7706
|
-
Do not invoke SST lifecycle commands directly. Production deployment belongs in the trusted
|
|
7707
|
-
CI path.
|
|
7708
|
-
|
|
7709
|
-
Foundation stages are canonical. \`ebk dev\` derives a Product Slice personal
|
|
7710
|
-
stage from the operating-system account and maps that consumer to the
|
|
7711
|
-
development Foundation. Callers cannot select that stage. Slice preflight
|
|
7712
|
-
reports both identities, never deploys a personal Foundation, and full mapped
|
|
7713
|
-
personal-stage deploys fail closed.
|
|
7714
|
-
|
|
7715
|
-
## Database restore
|
|
7716
|
-
|
|
7717
|
-
Production Aurora retains 30 days of automated backups; non-production retains 7 days.
|
|
7718
|
-
Restore into a new cluster, validate application migrations and data integrity, then cut over
|
|
7719
|
-
consumers deliberately. Never expose the Foundation master credential to a product slice.
|
|
7720
|
-
|
|
7721
|
-
## Protected database migrations
|
|
7722
|
-
|
|
7723
|
-
Each database-backed Product Slice deploys a one-off Fargate migration task and
|
|
7724
|
-
Standard Step Functions workflow into the shared cluster. The existing
|
|
7725
|
-
environment-scoped \`AWS_DEPLOY_ROLE_ARN\` starts and observes that exact
|
|
7726
|
-
workflow; do not create a second migration OIDC role. GitHub has no direct SQL,
|
|
7727
|
-
database-secret-value, or \`rds-db:connect\` authority. The generated task role
|
|
7728
|
-
connects as the Slice's no-password IAM-authenticated migrator user.
|
|
7729
8145
|
`,
|
|
7730
8146
|
'packages/foundation/docs/security.md': `# Foundation security contract
|
|
7731
8147
|
|
|
7732
8148
|
- Production resources are retained and protected.
|
|
7733
|
-
-
|
|
7734
|
-
- Aurora IAM authentication is enabled for generated short-lived migration
|
|
8149
|
+
- When relational persistence is declared, the shared Aurora master credential is Foundation-internal.
|
|
8150
|
+
- Declared Aurora IAM authentication is enabled for generated short-lived migration
|
|
7735
8151
|
tasks. The exact cluster resource ID is non-secret Foundation discovery.
|
|
7736
|
-
- Product
|
|
8152
|
+
- Relational Product Slices receive scoped logical-database credentials through the provisioning contract.
|
|
8153
|
+
- DynamoDB tables and their access are Slice/task scoped; no shared Foundation application table exists.
|
|
7737
8154
|
- Cognito owns authentication; product slices own authorization policy and app clients.
|
|
7738
8155
|
- Lambda and server-side web workloads use resource linking. Containers use the documented
|
|
7739
8156
|
stage-scoped SSM/secret path and least-privilege IAM.
|
|
7740
8157
|
- Public domains are configured explicitly through \`EBK_ROOT_DOMAIN\`; generated source never
|
|
7741
8158
|
contains credentials or customer domain assumptions.
|
|
8159
|
+
- Every stage's CloudFront Router has its own global Web ACL. The baseline always includes AWS
|
|
8160
|
+
Common Rule Set, Known Bad Inputs, Amazon IP Reputation, rate blocking, CloudWatch metrics,
|
|
8161
|
+
and sampled requests. Nonproduction is allowlisted by default. Production is public by
|
|
8162
|
+
default, but cannot deploy until the exact policy has passed a ${rehearsalStage} blocking rehearsal.
|
|
8163
|
+
- Full WAF request logging is disabled by default. Enabling it keeps blocked requests only,
|
|
8164
|
+
uses bounded retention, and redacts query strings, cookies, authorization, and API keys.
|
|
7742
8165
|
|
|
7743
8166
|
The managed \`contracts/frontend-security-headers.json\` file owns the transport
|
|
7744
8167
|
minimum: HTTPS in deployed environments, one-year HSTS there, and the common
|
|
@@ -7770,18 +8193,32 @@ declare const $app: {
|
|
|
7770
8193
|
|
|
7771
8194
|
import {
|
|
7772
8195
|
FOUNDATION_SSM_SCHEMA_VERSION,
|
|
7773
|
-
|
|
8196
|
+
createRouterWafRules,
|
|
8197
|
+
createRouterWafStagePlan,
|
|
8198
|
+
${foundationPlan.resources.postgres ? ' foundationDatabaseEndpointHost,' : ''}
|
|
7774
8199
|
foundationSsmPath,
|
|
8200
|
+
normalizeRouterWafPolicy,
|
|
8201
|
+
reconcileRouterWafBlockingRehearsal,
|
|
8202
|
+
routerWafContractDigest,
|
|
7775
8203
|
type FoundationSsmKey,
|
|
7776
8204
|
} from '@empire-builder-kit/nx/foundation';
|
|
7777
8205
|
import {
|
|
7778
|
-
FOUNDATION_AURORA_ENGINE,
|
|
7779
|
-
FOUNDATION_AURORA_ENGINE_VERSION,
|
|
8206
|
+
${foundationPlan.resources.postgres ? ' FOUNDATION_AURORA_ENGINE,\n FOUNDATION_AURORA_ENGINE_VERSION,' : ''}
|
|
7780
8207
|
productionFoundationProfileContract,
|
|
7781
8208
|
} from '@empire-builder-kit/nx/repository-policy';
|
|
8209
|
+
import { installStageIamBoundaryTransforms } from '@empire-builder-kit/nx/aws-authority';
|
|
8210
|
+
import { createStageAuthorityIdentity } from '@empire-builder-kit/nx/aws-authority';
|
|
8211
|
+
import {
|
|
8212
|
+
configuredDeploymentAccountForStage,
|
|
8213
|
+
configuredDeploymentRegion,
|
|
8214
|
+
productionRehearsalStage,
|
|
8215
|
+
} from '@empire-builder-kit/nx/repository-policy';
|
|
7782
8216
|
import { createFoundationAccountCostControlPlan } from '@empire-builder-kit/nx/performance';
|
|
7783
8217
|
import accountCostPolicy from '../performance/account-cost-policy.json';
|
|
7784
|
-
import
|
|
8218
|
+
import routerWafBlockingRehearsal from '../security/router-waf-blocking-rehearsal.json';
|
|
8219
|
+
import routerWafLiveEvidence from '../security/router-waf-live-evidence.json';
|
|
8220
|
+
import routerWafPolicy from '../security/router-waf.json';
|
|
8221
|
+
import repositoryPolicy from '../../../ebk.repository-policy.json';
|
|
7785
8222
|
|
|
7786
8223
|
function identityDomainPrefix(stage: string, accountId: string): string {
|
|
7787
8224
|
if (!/^\\d{12}$/.test(accountId)) {
|
|
@@ -7802,7 +8239,7 @@ function identityDomainPrefix(stage: string, accountId: string): string {
|
|
|
7802
8239
|
return value.slice(0, 50) + '-' + digest;
|
|
7803
8240
|
}
|
|
7804
8241
|
|
|
7805
|
-
function publishFoundation(values: Record<FoundationSsmKey, any
|
|
8242
|
+
function publishFoundation(values: Partial<Record<FoundationSsmKey, any>>) {
|
|
7806
8243
|
return Object.fromEntries(
|
|
7807
8244
|
Object.entries(values).map(([key, value]) => [
|
|
7808
8245
|
key,
|
|
@@ -7815,45 +8252,78 @@ function publishFoundation(values: Record<FoundationSsmKey, any>) {
|
|
|
7815
8252
|
);
|
|
7816
8253
|
}
|
|
7817
8254
|
|
|
7818
|
-
export function foundation(
|
|
7819
|
-
workloadRoles?: FoundationWorkloadRolePreflight,
|
|
7820
|
-
) {
|
|
7821
|
-
if (!$dev && !workloadRoles) {
|
|
7822
|
-
throw new Error(
|
|
7823
|
-
'Protected Foundation workload-role preflight must complete before infrastructure construction.',
|
|
7824
|
-
);
|
|
7825
|
-
}
|
|
8255
|
+
export function foundation() {
|
|
7826
8256
|
const isProduction = $app.stage === 'production';
|
|
7827
|
-
const isStaging = $app.stage === 'staging';
|
|
8257
|
+
${foundationPlan.resources.postgres ? " const isStaging = $app.stage === 'staging';" : ''}
|
|
7828
8258
|
const rootDomain = process.env.EBK_ROOT_DOMAIN?.trim();
|
|
7829
8259
|
const productionConfiguration = isProduction
|
|
7830
8260
|
? productionFoundationProfileContract(
|
|
7831
8261
|
process.env.EBK_PRODUCTION_FOUNDATION_PROFILE,
|
|
7832
8262
|
)
|
|
7833
8263
|
: undefined;
|
|
7834
|
-
const natMode = productionConfiguration?.contract.nat ?? 'ec2';
|
|
7835
|
-
const
|
|
7836
|
-
if (
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
8264
|
+
${foundationPlan.resources.network ? " const natMode = productionConfiguration?.contract.nat ?? 'ec2';" : ''}
|
|
8265
|
+
const normalizedRouterWafPolicy = normalizeRouterWafPolicy(routerWafPolicy);
|
|
8266
|
+
if (repositoryPolicy.deploymentProfile !== normalizedRouterWafPolicy.deploymentProfile) {
|
|
8267
|
+
throw new Error('Router WAF policy profile differs from committed repository policy.');
|
|
8268
|
+
}
|
|
8269
|
+
const rehearsalStage = productionRehearsalStage(normalizedRouterWafPolicy.deploymentProfile);
|
|
8270
|
+
const rehearsalAccount = configuredDeploymentAccountForStage(
|
|
8271
|
+
normalizedRouterWafPolicy.deploymentProfile,
|
|
8272
|
+
repositoryPolicy.deploymentAccounts,
|
|
8273
|
+
rehearsalStage,
|
|
8274
|
+
);
|
|
8275
|
+
const rehearsalRegion = configuredDeploymentRegion(repositoryPolicy.deploymentRegion);
|
|
8276
|
+
const rehearsalRoleArn = createStageAuthorityIdentity({
|
|
8277
|
+
accountId: rehearsalAccount,
|
|
8278
|
+
partition: rehearsalRegion.startsWith('us-gov-') ? 'aws-us-gov' : 'aws',
|
|
8279
|
+
region: rehearsalRegion,
|
|
8280
|
+
stage: rehearsalStage,
|
|
8281
|
+
}).deploymentRoleArn;
|
|
8282
|
+
const wafPlan = createRouterWafStagePlan({
|
|
8283
|
+
policy: normalizedRouterWafPolicy,
|
|
8284
|
+
rehearsal: isProduction
|
|
8285
|
+
? reconcileRouterWafBlockingRehearsal({
|
|
8286
|
+
evidence: routerWafLiveEvidence,
|
|
8287
|
+
expected: {
|
|
8288
|
+
accountId: rehearsalAccount,
|
|
8289
|
+
deploymentRoleArn: rehearsalRoleArn,
|
|
8290
|
+
region: rehearsalRegion,
|
|
8291
|
+
stage: rehearsalStage,
|
|
8292
|
+
wafContractDigest: routerWafContractDigest(normalizedRouterWafPolicy),
|
|
8293
|
+
workspace: '${name}',
|
|
8294
|
+
},
|
|
8295
|
+
policy: normalizedRouterWafPolicy,
|
|
8296
|
+
receipt: routerWafBlockingRehearsal,
|
|
8297
|
+
})
|
|
8298
|
+
: null,
|
|
8299
|
+
stage: $app.stage,
|
|
8300
|
+
});
|
|
8301
|
+
const deploymentAccount = process.env.AWS_EXPECTED_ACCOUNT_ID?.trim() ?? '';
|
|
8302
|
+
if (!/^\\d{12}$/.test(deploymentAccount)) {
|
|
8303
|
+
throw new Error(
|
|
8304
|
+
'Foundation deployment requires the exact committed AWS account before infrastructure construction.',
|
|
7845
8305
|
);
|
|
7846
8306
|
}
|
|
8307
|
+
installStageIamBoundaryTransforms({
|
|
8308
|
+
accountId: deploymentAccount,
|
|
8309
|
+
instanceProfileResource: aws.iam.InstanceProfile,
|
|
8310
|
+
registerTransform: $transform,
|
|
8311
|
+
roleResource: aws.iam.Role,
|
|
8312
|
+
stage: ($dev ? 'development' : $app.stage) as
|
|
8313
|
+
| 'development'
|
|
8314
|
+
| 'staging'
|
|
8315
|
+
| 'production',
|
|
8316
|
+
});
|
|
7847
8317
|
|
|
7848
|
-
|
|
8318
|
+
${foundationPlan.resources.network
|
|
8319
|
+
? ` const vpc = new sst.aws.Vpc('FoundationVpc', {
|
|
7849
8320
|
az: 2,
|
|
7850
8321
|
bastion: $app.stage === 'development',
|
|
7851
8322
|
nat:
|
|
7852
|
-
natMode === 'ec2'
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
});
|
|
7856
|
-
const cluster = new sst.aws.Cluster('FoundationCluster', { vpc });
|
|
8323
|
+
natMode === 'ec2' ? 'ec2' : 'managed',
|
|
8324
|
+
});`
|
|
8325
|
+
: ''}
|
|
8326
|
+
${foundationPlan.resources.containerCompute ? " const cluster = new sst.aws.Cluster('FoundationCluster', { vpc });" : ''}
|
|
7857
8327
|
const router = new sst.aws.Router('FoundationRouter', {
|
|
7858
8328
|
...(rootDomain
|
|
7859
8329
|
? {
|
|
@@ -7862,10 +8332,138 @@ export function foundation(
|
|
|
7862
8332
|
aliases: ['*.' + rootDomain],
|
|
7863
8333
|
dns: sst.aws.dns(),
|
|
7864
8334
|
},
|
|
7865
|
-
|
|
8335
|
+
}
|
|
7866
8336
|
: {}),
|
|
8337
|
+
waf: {
|
|
8338
|
+
logging: wafPlan.logging,
|
|
8339
|
+
managedRules: {
|
|
8340
|
+
coreRuleSet: true,
|
|
8341
|
+
knownBadInputs: true,
|
|
8342
|
+
sqlInjection: false,
|
|
8343
|
+
},
|
|
8344
|
+
rateLimitPerIp: wafPlan.rateLimitPerIp,
|
|
8345
|
+
},
|
|
8346
|
+
transform: {
|
|
8347
|
+
cdn(args: any) {
|
|
8348
|
+
if (!args.webAclArn) {
|
|
8349
|
+
throw new Error(
|
|
8350
|
+
'Foundation Router must attach its exact global Web ACL.',
|
|
8351
|
+
);
|
|
8352
|
+
}
|
|
8353
|
+
args.tags = {
|
|
8354
|
+
...(args.tags ?? {}),
|
|
8355
|
+
'ebk:managed-by': 'empire-builder-kit',
|
|
8356
|
+
'ebk:owner': 'foundation',
|
|
8357
|
+
'ebk:stage': wafPlan.stage,
|
|
8358
|
+
'ebk:workspace': '${name}',
|
|
8359
|
+
};
|
|
8360
|
+
},
|
|
8361
|
+
waf(args: any, opts: any) {
|
|
8362
|
+
if (args.scope !== 'CLOUDFRONT') {
|
|
8363
|
+
throw new Error(
|
|
8364
|
+
'Foundation Router Web ACL must use global CloudFront scope.',
|
|
8365
|
+
);
|
|
8366
|
+
}
|
|
8367
|
+
const ipSets: { ipv4Arn?: any; ipv6Arn?: any; smokeArn?: any } = {};
|
|
8368
|
+
if (wafPlan.ipv4Cidrs.length > 0) {
|
|
8369
|
+
ipSets.ipv4Arn = new aws.wafv2.IpSet(
|
|
8370
|
+
'FoundationRouterIpv4Allowlist',
|
|
8371
|
+
{
|
|
8372
|
+
addresses: wafPlan.ipv4Cidrs,
|
|
8373
|
+
ipAddressVersion: 'IPV4',
|
|
8374
|
+
name: 'foundation-' + wafPlan.stage + '-router-ipv4-allowlist',
|
|
8375
|
+
scope: 'CLOUDFRONT',
|
|
8376
|
+
tags: {
|
|
8377
|
+
'ebk:managed-by': 'empire-builder-kit',
|
|
8378
|
+
'ebk:owner': 'foundation',
|
|
8379
|
+
'ebk:policy-digest': wafPlan.policyDigest,
|
|
8380
|
+
'ebk:stage': wafPlan.stage,
|
|
8381
|
+
'ebk:workspace': '${name}',
|
|
8382
|
+
},
|
|
8383
|
+
},
|
|
8384
|
+
{ provider: opts.provider },
|
|
8385
|
+
).arn;
|
|
8386
|
+
}
|
|
8387
|
+
if (wafPlan.ipv6Cidrs.length > 0) {
|
|
8388
|
+
ipSets.ipv6Arn = new aws.wafv2.IpSet(
|
|
8389
|
+
'FoundationRouterIpv6Allowlist',
|
|
8390
|
+
{
|
|
8391
|
+
addresses: wafPlan.ipv6Cidrs,
|
|
8392
|
+
ipAddressVersion: 'IPV6',
|
|
8393
|
+
name: 'foundation-' + wafPlan.stage + '-router-ipv6-allowlist',
|
|
8394
|
+
scope: 'CLOUDFRONT',
|
|
8395
|
+
tags: {
|
|
8396
|
+
'ebk:managed-by': 'empire-builder-kit',
|
|
8397
|
+
'ebk:owner': 'foundation',
|
|
8398
|
+
'ebk:policy-digest': wafPlan.policyDigest,
|
|
8399
|
+
'ebk:stage': wafPlan.stage,
|
|
8400
|
+
'ebk:workspace': '${name}',
|
|
8401
|
+
},
|
|
8402
|
+
},
|
|
8403
|
+
{ provider: opts.provider },
|
|
8404
|
+
).arn;
|
|
8405
|
+
}
|
|
8406
|
+
if (wafPlan.access === 'allowlist') {
|
|
8407
|
+
// Smoke rotates WAF's LockToken outside Pulumi. Keep the existing
|
|
8408
|
+
// baseline IP sets untouched; AWS's Cloud Control handler owns the
|
|
8409
|
+
// separate ephemeral set's lifecycle without a cached lock token.
|
|
8410
|
+
const smokeAccess = new aws.cloudcontrol.Resource(
|
|
8411
|
+
'FoundationRouterSmokeAccess',
|
|
8412
|
+
{
|
|
8413
|
+
typeName: 'AWS::WAFv2::IPSet',
|
|
8414
|
+
desiredState: JSON.stringify({
|
|
8415
|
+
Addresses: [],
|
|
8416
|
+
IPAddressVersion: 'IPV4',
|
|
8417
|
+
Name: 'foundation-' + wafPlan.stage + '-router-smoke-access',
|
|
8418
|
+
Scope: 'CLOUDFRONT',
|
|
8419
|
+
Tags: Object.entries({
|
|
8420
|
+
'ebk:managed-by': 'empire-builder-kit',
|
|
8421
|
+
'ebk:owner': 'foundation',
|
|
8422
|
+
'ebk:purpose': 'private-smoke-access',
|
|
8423
|
+
'ebk:stage': wafPlan.stage,
|
|
8424
|
+
'ebk:workspace': '${name}',
|
|
8425
|
+
}).map(([Key, Value]) => ({ Key, Value })),
|
|
8426
|
+
}),
|
|
8427
|
+
},
|
|
8428
|
+
{
|
|
8429
|
+
provider: opts.provider,
|
|
8430
|
+
// This dedicated set has creation-only identity/ownership inputs.
|
|
8431
|
+
// Admission owns its addresses; the Web ACL owns policy binding.
|
|
8432
|
+
// Retain old inputs on upgrade too: Cloud Control patches arrays
|
|
8433
|
+
// by index, which is unsafe for service-reordered Tags (including
|
|
8434
|
+
// removing an older policy-digest tag). Never patch this document.
|
|
8435
|
+
ignoreChanges: ['desiredState'],
|
|
8436
|
+
},
|
|
8437
|
+
);
|
|
8438
|
+
ipSets.smokeArn = smokeAccess.properties.apply(
|
|
8439
|
+
(properties: string) => JSON.parse(properties).Arn,
|
|
8440
|
+
);
|
|
8441
|
+
}
|
|
8442
|
+
args.defaultAction = { allow: {} };
|
|
8443
|
+
args.rules = createRouterWafRules(wafPlan, ipSets);
|
|
8444
|
+
args.scope = 'CLOUDFRONT';
|
|
8445
|
+
args.tags = {
|
|
8446
|
+
...(args.tags ?? {}),
|
|
8447
|
+
'ebk:managed-by': 'empire-builder-kit',
|
|
8448
|
+
'ebk:owner': 'foundation',
|
|
8449
|
+
'ebk:policy-digest': wafPlan.policyDigest,
|
|
8450
|
+
'ebk:stage': wafPlan.stage,
|
|
8451
|
+
'ebk:workspace': '${name}',
|
|
8452
|
+
};
|
|
8453
|
+
args.visibilityConfig = {
|
|
8454
|
+
cloudwatchMetricsEnabled: true,
|
|
8455
|
+
metricName:
|
|
8456
|
+
'Ebk' +
|
|
8457
|
+
wafPlan.stage[0].toUpperCase() +
|
|
8458
|
+
wafPlan.stage.slice(1) +
|
|
8459
|
+
'FoundationRouterWaf',
|
|
8460
|
+
sampledRequestsEnabled: true,
|
|
8461
|
+
};
|
|
8462
|
+
},
|
|
8463
|
+
},
|
|
7867
8464
|
});
|
|
7868
|
-
|
|
8465
|
+
${foundationPlan.resources.postgres
|
|
8466
|
+
? ` const database = new sst.aws.Aurora('FoundationDatabase', {
|
|
7869
8467
|
// SQL bootstrap and migrations run inside the VPC. Keep the public HTTPS
|
|
7870
8468
|
// Data API surface disabled; protected GitHub roles also deny its actions.
|
|
7871
8469
|
dataApi: false,
|
|
@@ -7887,6 +8485,47 @@ export function foundation(
|
|
|
7887
8485
|
},
|
|
7888
8486
|
vpc,
|
|
7889
8487
|
});
|
|
8488
|
+
const databaseClusterEndpoint = database.nodes.cluster.endpoint.apply(
|
|
8489
|
+
foundationDatabaseEndpointHost,
|
|
8490
|
+
);
|
|
8491
|
+
const databaseReaderEndpoint = database.nodes.cluster.readerEndpoint.apply(
|
|
8492
|
+
foundationDatabaseEndpointHost,
|
|
8493
|
+
);
|
|
8494
|
+
// SST v4.17.1 creates a top-level LinkRef on the next tick and its Aurora
|
|
8495
|
+
// implementation unconditionally splits cluster endpoints. During a
|
|
8496
|
+
// governed refresh after an interrupted first deploy, those provider
|
|
8497
|
+
// outputs are legitimately absent. Preserve the normal link contract while
|
|
8498
|
+
// keeping that read-only recovery program total; a real deploy resolves the
|
|
8499
|
+
// exact endpoints before any discovery resource is created.
|
|
8500
|
+
database.getSSTLink = () => ({
|
|
8501
|
+
include: [
|
|
8502
|
+
sst.aws.permission({
|
|
8503
|
+
actions: ['secretsmanager:GetSecretValue'],
|
|
8504
|
+
resources: [database.secretArn],
|
|
8505
|
+
}),
|
|
8506
|
+
sst.aws.permission({
|
|
8507
|
+
actions: [
|
|
8508
|
+
'rds-data:BatchExecuteStatement',
|
|
8509
|
+
'rds-data:BeginTransaction',
|
|
8510
|
+
'rds-data:CommitTransaction',
|
|
8511
|
+
'rds-data:ExecuteStatement',
|
|
8512
|
+
'rds-data:RollbackTransaction',
|
|
8513
|
+
],
|
|
8514
|
+
resources: [database.clusterArn],
|
|
8515
|
+
}),
|
|
8516
|
+
],
|
|
8517
|
+
properties: {
|
|
8518
|
+
clusterArn: database.clusterArn,
|
|
8519
|
+
database: database.database,
|
|
8520
|
+
host: databaseClusterEndpoint,
|
|
8521
|
+
password: database.password,
|
|
8522
|
+
port: database.port,
|
|
8523
|
+
reader: databaseReaderEndpoint,
|
|
8524
|
+
secretArn: database.secretArn,
|
|
8525
|
+
username: database.username,
|
|
8526
|
+
},
|
|
8527
|
+
});`
|
|
8528
|
+
: ''}
|
|
7890
8529
|
const foundationAccountId = aws.getCallerIdentityOutput({}).accountId;
|
|
7891
8530
|
const foundationPartition = aws.getPartitionOutput({});
|
|
7892
8531
|
const foundationRegion = aws.getRegionOutput({});
|
|
@@ -7915,11 +8554,12 @@ export function foundation(
|
|
|
7915
8554
|
const accountCostControl = createFoundationAccountCostControlPlan({
|
|
7916
8555
|
evaluatedAt: new Date().toISOString(),
|
|
7917
8556
|
policy: accountCostPolicy,
|
|
8557
|
+
rehearsalStage: '${rehearsalStage}',
|
|
7918
8558
|
region: process.env.AWS_REGION,
|
|
7919
8559
|
stage: $app.stage,
|
|
7920
8560
|
workspace: '${name}',
|
|
7921
8561
|
});
|
|
7922
|
-
const databaseInstanceId = database.nodes.instance.identifier;
|
|
8562
|
+
${foundationPlan.resources.postgres ? ' const databaseInstanceId = database.nodes.instance.identifier;' : ''}
|
|
7923
8563
|
const alertTopicPolicy = new aws.sns.TopicPolicy(
|
|
7924
8564
|
'FoundationAlertTopicPolicy',
|
|
7925
8565
|
{
|
|
@@ -7949,6 +8589,7 @@ export function foundation(
|
|
|
7949
8589
|
},
|
|
7950
8590
|
],
|
|
7951
8591
|
resources: [alertTopic.arn],
|
|
8592
|
+
sid: 'AllowBudgetAlerts',
|
|
7952
8593
|
},
|
|
7953
8594
|
{
|
|
7954
8595
|
actions: ['sns:Publish'],
|
|
@@ -7962,8 +8603,10 @@ export function foundation(
|
|
|
7962
8603
|
type: 'Service',
|
|
7963
8604
|
}],
|
|
7964
8605
|
resources: [alertTopic.arn],
|
|
8606
|
+
sid: 'AllowCostAnomalyAlerts',
|
|
7965
8607
|
},
|
|
7966
|
-
|
|
8608
|
+
${foundationPlan.resources.postgres
|
|
8609
|
+
? ` {
|
|
7967
8610
|
actions: ['sns:Publish'],
|
|
7968
8611
|
conditions: [
|
|
7969
8612
|
{
|
|
@@ -7984,7 +8627,9 @@ export function foundation(
|
|
|
7984
8627
|
type: 'Service',
|
|
7985
8628
|
}],
|
|
7986
8629
|
resources: [alertTopic.arn],
|
|
7987
|
-
|
|
8630
|
+
sid: 'AllowRdsEventAlerts',
|
|
8631
|
+
},`
|
|
8632
|
+
: ''}
|
|
7988
8633
|
{
|
|
7989
8634
|
actions: ['sns:Publish'],
|
|
7990
8635
|
conditions: [
|
|
@@ -8008,6 +8653,7 @@ export function foundation(
|
|
|
8008
8653
|
},
|
|
8009
8654
|
],
|
|
8010
8655
|
resources: [alertTopic.arn],
|
|
8656
|
+
sid: 'AllowCloudWatchAlarmAlerts',
|
|
8011
8657
|
},
|
|
8012
8658
|
],
|
|
8013
8659
|
}).json,
|
|
@@ -8036,7 +8682,43 @@ export function foundation(
|
|
|
8036
8682
|
{ dependsOn: [alertTopicPolicy] },
|
|
8037
8683
|
)
|
|
8038
8684
|
: undefined;
|
|
8039
|
-
const
|
|
8685
|
+
const accountCostAnomalyMonitor = isProduction
|
|
8686
|
+
? new aws.costexplorer.AnomalyMonitor(
|
|
8687
|
+
'FoundationAccountCostAnomalyMonitor',
|
|
8688
|
+
{
|
|
8689
|
+
monitorDimension: 'SERVICE',
|
|
8690
|
+
monitorType: 'DIMENSIONAL',
|
|
8691
|
+
name: '${name}-account-service-cost-anomalies',
|
|
8692
|
+
tags: {
|
|
8693
|
+
'ebk:cost-policy-digest': accountCostControl.policyDigest,
|
|
8694
|
+
'ebk:cost-scope': '${name}/account/production',
|
|
8695
|
+
'ebk:owner': accountCostControl.owner,
|
|
8696
|
+
},
|
|
8697
|
+
},
|
|
8698
|
+
)
|
|
8699
|
+
: undefined;
|
|
8700
|
+
const accountCostAnomalySubscription =
|
|
8701
|
+
accountCostControl.anomaly && accountCostAnomalyMonitor
|
|
8702
|
+
? new aws.costexplorer.AnomalySubscription(
|
|
8703
|
+
'FoundationAccountCostAnomalySubscription',
|
|
8704
|
+
{
|
|
8705
|
+
frequency: accountCostControl.anomaly.subscription.frequency,
|
|
8706
|
+
monitorArnLists: [accountCostAnomalyMonitor.arn],
|
|
8707
|
+
name: accountCostControl.anomaly.subscription.name,
|
|
8708
|
+
subscribers: [{ address: alertTopic.arn, type: 'SNS' }],
|
|
8709
|
+
tags: {
|
|
8710
|
+
'ebk:cost-policy-digest': accountCostControl.policyDigest,
|
|
8711
|
+
'ebk:cost-scope': '${name}/account/production',
|
|
8712
|
+
'ebk:owner': accountCostControl.owner,
|
|
8713
|
+
},
|
|
8714
|
+
thresholdExpression:
|
|
8715
|
+
accountCostControl.anomaly.subscription.thresholdExpression,
|
|
8716
|
+
},
|
|
8717
|
+
{ dependsOn: [accountCostAnomalyMonitor, alertTopicPolicy] },
|
|
8718
|
+
)
|
|
8719
|
+
: undefined;
|
|
8720
|
+
${foundationPlan.resources.postgres
|
|
8721
|
+
? ` const databaseEvents = isProduction
|
|
8040
8722
|
? new aws.rds.EventSubscription(
|
|
8041
8723
|
'FoundationDatabaseEvents',
|
|
8042
8724
|
{
|
|
@@ -8118,7 +8800,14 @@ export function foundation(
|
|
|
8118
8800
|
threshold: 90,
|
|
8119
8801
|
treatMissingData: 'notBreaching',
|
|
8120
8802
|
},
|
|
8121
|
-
)
|
|
8803
|
+
);`
|
|
8804
|
+
: ` const dashboard = new aws.cloudwatch.Dashboard('FoundationDashboard', {
|
|
8805
|
+
dashboardName: 'ebk-' + $app.stage + '-foundation',
|
|
8806
|
+
dashboardBody: JSON.stringify({ widgets: [{
|
|
8807
|
+
type: 'text', x: 0, y: 0, width: 24, height: 3,
|
|
8808
|
+
properties: { markdown: '# Empire Builder Kit Foundation\\nStage: ' + $app.stage + '\\nPersistence: Slice-scoped DynamoDB; SQL not declared.' },
|
|
8809
|
+
}] }),
|
|
8810
|
+
});`}
|
|
8122
8811
|
|
|
8123
8812
|
if (new Set(['development', 'staging', 'production']).has($app.stage)) {
|
|
8124
8813
|
new aws.cloudwatch.EventArchive('FoundationEventArchive', {
|
|
@@ -8134,18 +8823,18 @@ export function foundation(
|
|
|
8134
8823
|
accountCostControl.authority.monthlyMaximumUsd,
|
|
8135
8824
|
),
|
|
8136
8825
|
alertTopicArn: alertTopic.arn,
|
|
8137
|
-
capabilities:
|
|
8138
|
-
clusterId: cluster.id,
|
|
8139
|
-
contractVersion: '
|
|
8140
|
-
|
|
8141
|
-
|
|
8142
|
-
|
|
8143
|
-
),
|
|
8826
|
+
capabilities: ${JSON.stringify(foundationPlan.descriptor.capabilities.join(','))},
|
|
8827
|
+
${foundationPlan.resources.containerCompute ? ' clusterId: cluster.id,' : ''}
|
|
8828
|
+
contractVersion: '${foundationPlan.descriptor.contractVersion}',
|
|
8829
|
+
${foundationPlan.resources.postgres
|
|
8830
|
+
? ` databaseClusterArn: database.clusterArn,
|
|
8831
|
+
databaseClusterEndpoint,
|
|
8144
8832
|
databaseClusterId: database.id,
|
|
8145
8833
|
databaseClusterResourceId: database.nodes.cluster.clusterResourceId,
|
|
8146
|
-
databaseHost:
|
|
8834
|
+
databaseHost: databaseClusterEndpoint,
|
|
8147
8835
|
databaseMasterSecretArn: database.secretArn,
|
|
8148
|
-
databasePort: database.port.apply(String)
|
|
8836
|
+
databasePort: database.port.apply(String),`
|
|
8837
|
+
: ''}
|
|
8149
8838
|
eventBusArn: eventBus.arn,
|
|
8150
8839
|
eventBusName: eventBus.name,
|
|
8151
8840
|
hasPublicDomain: rootDomain ? 'true' : 'false',
|
|
@@ -8156,19 +8845,22 @@ export function foundation(
|
|
|
8156
8845
|
routerDistributionId: router.distributionID,
|
|
8157
8846
|
routerUrl: router.url,
|
|
8158
8847
|
schemaVersion: String(FOUNDATION_SSM_SCHEMA_VERSION),
|
|
8159
|
-
vpcId: vpc.id,
|
|
8848
|
+
${foundationPlan.resources.network ? ' vpcId: vpc.id,' : ''}
|
|
8160
8849
|
});
|
|
8161
8850
|
|
|
8162
8851
|
return {
|
|
8163
8852
|
accountBudgetName: accountBudget?.name,
|
|
8164
8853
|
alertTopicArn: alertTopic.arn,
|
|
8165
|
-
capabilities:
|
|
8166
|
-
clusterId: cluster.id,
|
|
8167
|
-
contractVersion: '
|
|
8168
|
-
|
|
8854
|
+
capabilities: ${JSON.stringify(foundationPlan.descriptor.capabilities)},
|
|
8855
|
+
${foundationPlan.resources.containerCompute ? ' clusterId: cluster.id,' : ''}
|
|
8856
|
+
contractVersion: '${foundationPlan.descriptor.contractVersion}',
|
|
8857
|
+
costAnomalySubscriptionArn: accountCostAnomalySubscription?.arn,
|
|
8858
|
+
${foundationPlan.resources.postgres ? ' databaseCapacityAlarmName: databaseCapacityAlarm.name,' : ''}
|
|
8169
8859
|
dashboardName: dashboard.dashboardName,
|
|
8170
|
-
|
|
8171
|
-
|
|
8860
|
+
${foundationPlan.resources.postgres
|
|
8861
|
+
? ` databaseClusterId: database.id,
|
|
8862
|
+
databaseEventSubscriptionId: databaseEvents?.id,`
|
|
8863
|
+
: ''}
|
|
8172
8864
|
eventBusName: eventBus.name,
|
|
8173
8865
|
identityUserPoolId: userPool.id,
|
|
8174
8866
|
identityTokenEndpoint,
|
|
@@ -8176,33 +8868,68 @@ export function foundation(
|
|
|
8176
8868
|
productionFoundationProfile: productionConfiguration?.profile,
|
|
8177
8869
|
rootDomain,
|
|
8178
8870
|
routerUrl: router.url,
|
|
8179
|
-
vpcId: vpc.id,
|
|
8871
|
+
${foundationPlan.resources.network ? ' vpcId: vpc.id,' : ''}
|
|
8180
8872
|
};
|
|
8181
8873
|
}
|
|
8182
8874
|
`,
|
|
8183
8875
|
'packages/foundation/infra/index.ts': `import { foundation } from './foundation.js';
|
|
8184
|
-
import type { FoundationWorkloadRolePreflight } from './workload-roles/preflight.js';
|
|
8185
8876
|
|
|
8186
|
-
export function app(
|
|
8187
|
-
|
|
8188
|
-
return {
|
|
8189
|
-
...resources,
|
|
8190
|
-
workloadRoleManifestDigest:
|
|
8191
|
-
workloadRoles?.registry.manifestDigest ?? 'local-dev-ephemeral',
|
|
8192
|
-
};
|
|
8877
|
+
export function app() {
|
|
8878
|
+
return foundation();
|
|
8193
8879
|
}
|
|
8194
8880
|
`,
|
|
8195
8881
|
'packages/foundation/performance/account-cost-policy.json': json({
|
|
8196
8882
|
actualNotificationThresholdPercent: 80,
|
|
8197
8883
|
forecastNotificationThresholdPercent: 100,
|
|
8198
8884
|
kind: 'ebk-foundation-account-cost-policy',
|
|
8199
|
-
monthlyMaximumUsd: 1_000,
|
|
8885
|
+
monthlyMaximumUsd: deploymentProfile === 'starter' ? 250 : 1_000,
|
|
8200
8886
|
owner: 'replace-with-owner',
|
|
8201
8887
|
reviewAt: null,
|
|
8202
8888
|
schemaVersion: 1,
|
|
8203
8889
|
status: 'example',
|
|
8204
8890
|
workspace: name,
|
|
8205
8891
|
}),
|
|
8892
|
+
'packages/foundation/security/router-waf.json': json({
|
|
8893
|
+
deploymentProfile,
|
|
8894
|
+
kind: 'ebk-router-waf-policy',
|
|
8895
|
+
logging: {
|
|
8896
|
+
enabled: false,
|
|
8897
|
+
retention: '1 week',
|
|
8898
|
+
},
|
|
8899
|
+
rateLimitPerIp: 2_000,
|
|
8900
|
+
schemaVersion: 1,
|
|
8901
|
+
stages: {
|
|
8902
|
+
development: {
|
|
8903
|
+
access: 'allowlist',
|
|
8904
|
+
allowedCidrs: [],
|
|
8905
|
+
mode: 'block',
|
|
8906
|
+
},
|
|
8907
|
+
production: {
|
|
8908
|
+
access: 'public',
|
|
8909
|
+
allowedCidrs: [],
|
|
8910
|
+
mode: 'block',
|
|
8911
|
+
},
|
|
8912
|
+
...(deploymentProfile === 'starter'
|
|
8913
|
+
? {}
|
|
8914
|
+
: {
|
|
8915
|
+
staging: {
|
|
8916
|
+
access: 'allowlist',
|
|
8917
|
+
allowedCidrs: [],
|
|
8918
|
+
mode: 'block',
|
|
8919
|
+
},
|
|
8920
|
+
}),
|
|
8921
|
+
},
|
|
8922
|
+
}),
|
|
8923
|
+
'packages/foundation/security/router-waf-blocking-rehearsal.json': json({
|
|
8924
|
+
kind: 'ebk-router-waf-blocking-rehearsal',
|
|
8925
|
+
schemaVersion: 4,
|
|
8926
|
+
status: 'pending',
|
|
8927
|
+
}),
|
|
8928
|
+
'packages/foundation/security/router-waf-live-evidence.json': json({
|
|
8929
|
+
kind: 'ebk-router-waf-live-evidence',
|
|
8930
|
+
schemaVersion: 4,
|
|
8931
|
+
status: 'pending',
|
|
8932
|
+
}),
|
|
8206
8933
|
'packages/foundation/configuration/declarations.json': json({
|
|
8207
8934
|
project: 'foundation',
|
|
8208
8935
|
schemaVersion: 1,
|
|
@@ -8235,29 +8962,87 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
|
|
|
8235
8962
|
version: '4.17.1',
|
|
8236
8963
|
},
|
|
8237
8964
|
policy: {
|
|
8965
|
+
adoptedImports: [],
|
|
8238
8966
|
additiveLowRiskTypes: [
|
|
8239
8967
|
'aws:cloudwatch/dashboard:Dashboard',
|
|
8240
8968
|
'aws:cloudwatch/logGroup:LogGroup',
|
|
8241
8969
|
'aws:cloudwatch/metricAlarm:MetricAlarm',
|
|
8242
8970
|
],
|
|
8971
|
+
additiveReviewedTypes: [
|
|
8972
|
+
'aws:cloudwatch/eventArchive:EventArchive',
|
|
8973
|
+
'aws:cloudwatch/eventBus:EventBus',
|
|
8974
|
+
'aws:cognito/userPoolDomain:UserPoolDomain',
|
|
8975
|
+
'aws:ec2/defaultSecurityGroup:DefaultSecurityGroup',
|
|
8976
|
+
'aws:ec2/internetGateway:InternetGateway',
|
|
8977
|
+
'aws:ec2/routeTable:RouteTable',
|
|
8978
|
+
'aws:ec2/routeTableAssociation:RouteTableAssociation',
|
|
8979
|
+
'aws:ec2/securityGroup:SecurityGroup',
|
|
8980
|
+
'aws:ec2/subnet:Subnet',
|
|
8981
|
+
'aws:ec2/vpc:Vpc',
|
|
8982
|
+
'aws:ecs/cluster:Cluster',
|
|
8983
|
+
'aws:ecs/clusterCapacityProviders:ClusterCapacityProviders',
|
|
8984
|
+
'aws:rds/clusterParameterGroup:ClusterParameterGroup',
|
|
8985
|
+
'aws:rds/eventSubscription:EventSubscription',
|
|
8986
|
+
'aws:rds/parameterGroup:ParameterGroup',
|
|
8987
|
+
'aws:rds/subnetGroup:SubnetGroup',
|
|
8988
|
+
'aws:servicediscovery/privateDnsNamespace:PrivateDnsNamespace',
|
|
8989
|
+
'aws:sns/topic:Topic',
|
|
8990
|
+
'aws:sns/topicPolicy:TopicPolicy',
|
|
8991
|
+
'aws:wafv2/webAclLoggingConfiguration:WebAclLoggingConfiguration',
|
|
8992
|
+
'pulumi-nodejs:dynamic:Resource',
|
|
8993
|
+
'pulumi:providers:aws',
|
|
8994
|
+
'pulumi:providers:pulumi-nodejs',
|
|
8995
|
+
'pulumi:providers:random',
|
|
8996
|
+
'pulumi:pulumi:Stack',
|
|
8997
|
+
'sst:aws:Aurora',
|
|
8998
|
+
'sst:aws:Bus',
|
|
8999
|
+
'sst:aws:CDN',
|
|
9000
|
+
'sst:aws:Cluster',
|
|
9001
|
+
'sst:aws:CognitoUserPool',
|
|
9002
|
+
'sst:aws:Router',
|
|
9003
|
+
'sst:aws:SnsTopic',
|
|
9004
|
+
'sst:aws:Vpc',
|
|
9005
|
+
'sst:sst:LinkRef',
|
|
9006
|
+
'sst:sst:Version',
|
|
9007
|
+
],
|
|
8243
9008
|
costOrQuotaTypes: [
|
|
8244
9009
|
'aws:apigatewayv2/api:Api',
|
|
8245
9010
|
'aws:budgets/budget:Budget',
|
|
9011
|
+
'aws:cloudfront/cachePolicy:CachePolicy',
|
|
8246
9012
|
'aws:cloudfront/distribution:Distribution',
|
|
9013
|
+
'aws:cloudfront/function:Function',
|
|
9014
|
+
'aws:cloudfront/keyValueStore:KeyValueStore',
|
|
9015
|
+
'aws:costexplorer/anomalyMonitor:AnomalyMonitor',
|
|
9016
|
+
'aws:costexplorer/anomalySubscription:AnomalySubscription',
|
|
8247
9017
|
'aws:cognito/userPool:UserPool',
|
|
9018
|
+
'aws:ec2/eip:Eip',
|
|
9019
|
+
'aws:ec2/eipAssociation:EipAssociation',
|
|
9020
|
+
'aws:ec2/instance:Instance',
|
|
8248
9021
|
'aws:ec2/natGateway:NatGateway',
|
|
8249
9022
|
'aws:rds/cluster:Cluster',
|
|
9023
|
+
'aws:rds/clusterInstance:ClusterInstance',
|
|
9024
|
+
'aws:wafv2/ipSet:IpSet',
|
|
9025
|
+
'aws:wafv2/webAcl:WebAcl',
|
|
8250
9026
|
],
|
|
8251
9027
|
failClosedOnUnknown: true,
|
|
8252
9028
|
protectedPlanAuthority: 'deploy-role-residual',
|
|
8253
9029
|
standingPolicyRiskClasses: ['no-change', 'additive-low-risk'],
|
|
8254
9030
|
statefulTypes: [
|
|
9031
|
+
'aws:cloudwatch/eventArchive:EventArchive',
|
|
9032
|
+
'aws:cloudwatch/eventBus:EventBus',
|
|
8255
9033
|
'aws:cognito/userPool:UserPool',
|
|
8256
9034
|
'aws:dynamodb/table:Table',
|
|
8257
9035
|
'aws:events/eventBus:EventBus',
|
|
8258
9036
|
'aws:kms/key:Key',
|
|
8259
9037
|
'aws:rds/cluster:Cluster',
|
|
8260
9038
|
'aws:s3/bucket:Bucket',
|
|
9039
|
+
'aws:secretsmanager/secret:Secret',
|
|
9040
|
+
'aws:secretsmanager/secretVersion:SecretVersion',
|
|
9041
|
+
'aws:sns/topic:Topic',
|
|
9042
|
+
'aws:ssm/parameter:Parameter',
|
|
9043
|
+
'aws:wafv2/ipSet:IpSet',
|
|
9044
|
+
'aws:wafv2/webAcl:WebAcl',
|
|
9045
|
+
'random:index/randomPassword:RandomPassword',
|
|
8261
9046
|
],
|
|
8262
9047
|
},
|
|
8263
9048
|
project: 'foundation',
|
|
@@ -8287,10 +9072,6 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
|
|
|
8287
9072
|
'verify',
|
|
8288
9073
|
{ target: 'profile-preflight', params: 'forward' },
|
|
8289
9074
|
],
|
|
8290
|
-
outputs: [
|
|
8291
|
-
`{projectRoot}/.ebk/evidence/infrastructure-convergence.json`,
|
|
8292
|
-
`{projectRoot}/.ebk/evidence/infrastructure-mutation-binding.json`,
|
|
8293
|
-
],
|
|
8294
9075
|
options: {
|
|
8295
9076
|
cwd: 'packages/foundation',
|
|
8296
9077
|
stage: 'development',
|
|
@@ -8307,15 +9088,7 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
|
|
|
8307
9088
|
'infrastructure-plan': {
|
|
8308
9089
|
cache: false,
|
|
8309
9090
|
executor: '@empire-builder-kit/nx:infrastructure-plan',
|
|
8310
|
-
outputs: [
|
|
8311
|
-
`{projectRoot}/.ebk/evidence/infrastructure-plan-authorization.json`,
|
|
8312
|
-
`{projectRoot}/.ebk/evidence/infrastructure-plan-command.json`,
|
|
8313
|
-
`{projectRoot}/.ebk/evidence/infrastructure-plan.private-stderr.txt`,
|
|
8314
|
-
`{projectRoot}/.ebk/evidence/infrastructure-plan.raw.json`,
|
|
8315
|
-
`{projectRoot}/.ebk/evidence/infrastructure-plan.json`,
|
|
8316
|
-
],
|
|
8317
9091
|
options: {
|
|
8318
|
-
action: 'deploy',
|
|
8319
9092
|
cwd: 'packages/foundation',
|
|
8320
9093
|
stage: 'development',
|
|
8321
9094
|
stageMode: 'foundation',
|
|
@@ -8324,10 +9097,6 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
|
|
|
8324
9097
|
refresh: {
|
|
8325
9098
|
executor: '@empire-builder-kit/nx:refresh',
|
|
8326
9099
|
dependsOn: [{ target: 'profile-preflight', params: 'forward' }],
|
|
8327
|
-
outputs: [
|
|
8328
|
-
`{projectRoot}/.ebk/evidence/infrastructure-convergence.json`,
|
|
8329
|
-
`{projectRoot}/.ebk/evidence/infrastructure-mutation-binding.json`,
|
|
8330
|
-
],
|
|
8331
9100
|
options: {
|
|
8332
9101
|
cwd: 'packages/foundation',
|
|
8333
9102
|
stage: 'development',
|
|
@@ -8337,10 +9106,6 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
|
|
|
8337
9106
|
remove: {
|
|
8338
9107
|
executor: '@empire-builder-kit/nx:remove',
|
|
8339
9108
|
dependsOn: [{ target: 'remove-preflight', params: 'forward' }],
|
|
8340
|
-
outputs: [
|
|
8341
|
-
`{projectRoot}/.ebk/evidence/infrastructure-convergence.json`,
|
|
8342
|
-
`{projectRoot}/.ebk/evidence/infrastructure-mutation-binding.json`,
|
|
8343
|
-
],
|
|
8344
9109
|
options: {
|
|
8345
9110
|
cwd: 'packages/foundation',
|
|
8346
9111
|
stage: 'development',
|
|
@@ -8355,6 +9120,13 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
|
|
|
8355
9120
|
executor: '@empire-builder-kit/nx:foundation-profile-preflight',
|
|
8356
9121
|
options: { stage: 'development' },
|
|
8357
9122
|
},
|
|
9123
|
+
'router-waf-rehearsal': {
|
|
9124
|
+
cache: false,
|
|
9125
|
+
executor: '@empire-builder-kit/nx:router-waf-rehearsal',
|
|
9126
|
+
outputs: [
|
|
9127
|
+
`{projectRoot}/security/router-waf-blocking-rehearsal.json`,
|
|
9128
|
+
],
|
|
9129
|
+
},
|
|
8358
9130
|
typecheck: {
|
|
8359
9131
|
executor: 'nx:run-commands',
|
|
8360
9132
|
options: {
|
|
@@ -8382,6 +9154,19 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
|
|
|
8382
9154
|
}),
|
|
8383
9155
|
'packages/foundation/scripts/verify-foundation.mjs': `import { readFileSync } from 'node:fs';
|
|
8384
9156
|
|
|
9157
|
+
import {
|
|
9158
|
+
normalizeRouterWafBlockingRehearsal,
|
|
9159
|
+
normalizeRouterWafPolicy,
|
|
9160
|
+
reconcileRouterWafBlockingRehearsal,
|
|
9161
|
+
routerWafContractDigest,
|
|
9162
|
+
} from '@empire-builder-kit/nx/foundation';
|
|
9163
|
+
import { createStageAuthorityIdentity } from '@empire-builder-kit/nx/aws-authority';
|
|
9164
|
+
import {
|
|
9165
|
+
configuredDeploymentAccountForStage,
|
|
9166
|
+
configuredDeploymentRegion,
|
|
9167
|
+
productionRehearsalStage,
|
|
9168
|
+
} from '@empire-builder-kit/nx/repository-policy';
|
|
9169
|
+
|
|
8385
9170
|
const descriptor = JSON.parse(
|
|
8386
9171
|
readFileSync(new URL('../contracts/foundation.json', import.meta.url), 'utf8'),
|
|
8387
9172
|
);
|
|
@@ -8397,35 +9182,92 @@ const frontendSecurityHeaders = JSON.parse(
|
|
|
8397
9182
|
'utf8',
|
|
8398
9183
|
),
|
|
8399
9184
|
);
|
|
8400
|
-
const
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
'
|
|
8410
|
-
|
|
9185
|
+
const routerWafPolicy = normalizeRouterWafPolicy(
|
|
9186
|
+
JSON.parse(
|
|
9187
|
+
readFileSync(
|
|
9188
|
+
new URL('../security/router-waf.json', import.meta.url),
|
|
9189
|
+
'utf8',
|
|
9190
|
+
),
|
|
9191
|
+
),
|
|
9192
|
+
);
|
|
9193
|
+
const repositoryPolicy = JSON.parse(
|
|
9194
|
+
readFileSync(new URL('../../../ebk.repository-policy.json', import.meta.url), 'utf8'),
|
|
9195
|
+
);
|
|
9196
|
+
const workspaceManifest = JSON.parse(
|
|
9197
|
+
readFileSync(new URL('../../../package.json', import.meta.url), 'utf8'),
|
|
9198
|
+
);
|
|
9199
|
+
const rehearsalReceipt = JSON.parse(
|
|
9200
|
+
readFileSync(
|
|
9201
|
+
new URL(
|
|
9202
|
+
'../security/router-waf-blocking-rehearsal.json',
|
|
9203
|
+
import.meta.url,
|
|
9204
|
+
),
|
|
9205
|
+
'utf8',
|
|
9206
|
+
),
|
|
9207
|
+
);
|
|
9208
|
+
const normalizedRehearsal = normalizeRouterWafBlockingRehearsal(rehearsalReceipt);
|
|
9209
|
+
if (normalizedRehearsal) {
|
|
9210
|
+
const rehearsalStage = productionRehearsalStage(repositoryPolicy.deploymentProfile);
|
|
9211
|
+
const rehearsalAccount = configuredDeploymentAccountForStage(
|
|
9212
|
+
repositoryPolicy.deploymentProfile,
|
|
9213
|
+
repositoryPolicy.deploymentAccounts,
|
|
9214
|
+
rehearsalStage,
|
|
9215
|
+
);
|
|
9216
|
+
const rehearsalRegion = configuredDeploymentRegion(repositoryPolicy.deploymentRegion);
|
|
9217
|
+
reconcileRouterWafBlockingRehearsal({
|
|
9218
|
+
evidence: JSON.parse(
|
|
9219
|
+
readFileSync(
|
|
9220
|
+
new URL('../security/router-waf-live-evidence.json', import.meta.url),
|
|
9221
|
+
'utf8',
|
|
9222
|
+
),
|
|
9223
|
+
),
|
|
9224
|
+
expected: {
|
|
9225
|
+
accountId: rehearsalAccount,
|
|
9226
|
+
deploymentRoleArn: createStageAuthorityIdentity({
|
|
9227
|
+
accountId: rehearsalAccount,
|
|
9228
|
+
partition: rehearsalRegion.startsWith('us-gov-') ? 'aws-us-gov' : 'aws',
|
|
9229
|
+
region: rehearsalRegion,
|
|
9230
|
+
stage: rehearsalStage,
|
|
9231
|
+
}).deploymentRoleArn,
|
|
9232
|
+
region: rehearsalRegion,
|
|
9233
|
+
stage: rehearsalStage,
|
|
9234
|
+
wafContractDigest: routerWafContractDigest(routerWafPolicy),
|
|
9235
|
+
workspace: workspaceManifest.name,
|
|
9236
|
+
},
|
|
9237
|
+
policy: routerWafPolicy,
|
|
9238
|
+
receipt: normalizedRehearsal,
|
|
9239
|
+
});
|
|
9240
|
+
}
|
|
9241
|
+
const expectedCapabilities = ${JSON.stringify(foundationPlan.descriptor.capabilities)};
|
|
8411
9242
|
|
|
8412
|
-
if (descriptor.contractVersion !== '
|
|
8413
|
-
throw new Error('Foundation contractVersion must be
|
|
9243
|
+
if (descriptor.contractVersion !== '2.0.0') {
|
|
9244
|
+
throw new Error('Foundation contractVersion must be 2.0.0.');
|
|
9245
|
+
}
|
|
9246
|
+
if (
|
|
9247
|
+
JSON.stringify(Object.keys(routerWafPolicy.stages).sort()) !==
|
|
9248
|
+
JSON.stringify(${JSON.stringify([...activeStages].sort())}) ||
|
|
9249
|
+
routerWafPolicy.stages.development.access !== 'allowlist' ||
|
|
9250
|
+
${deploymentProfile !== 'starter' ? "routerWafPolicy.stages.staging?.access !== 'allowlist' ||" : ''}
|
|
9251
|
+
routerWafPolicy.stages.production.mode !== 'block'
|
|
9252
|
+
) {
|
|
9253
|
+
throw new Error('Foundation Router WAF stage defaults are invalid.');
|
|
8414
9254
|
}
|
|
8415
9255
|
if (JSON.stringify(descriptor.capabilities) !== JSON.stringify(expectedCapabilities)) {
|
|
8416
|
-
throw new Error('Foundation capability descriptor does not match
|
|
9256
|
+
throw new Error('Foundation capability descriptor does not match its generated resource declaration.');
|
|
8417
9257
|
}
|
|
8418
9258
|
if (
|
|
8419
|
-
costs.schemaVersion !==
|
|
9259
|
+
costs.schemaVersion !== 3 ||
|
|
9260
|
+
costs.aurora?.declared !== ${foundationPlan.resources.postgres} ||
|
|
8420
9261
|
costs.aurora?.engineVersion !== '${index_js_4.FOUNDATION_AURORA_ENGINE_VERSION}' ||
|
|
8421
9262
|
costs.productionProfileSource !== 'ebk.repository-policy.json' ||
|
|
8422
9263
|
costs.productionProfiles?.standard?.aurora?.min !== '2 ACU' ||
|
|
8423
|
-
costs.productionProfiles
|
|
9264
|
+
JSON.stringify(costs.productionProfiles) !== JSON.stringify(${JSON.stringify(index_js_4.PRODUCTION_FOUNDATION_PROFILE_CONTRACTS)}) ||
|
|
8424
9265
|
!costs.stages?.development ||
|
|
8425
|
-
|
|
9266
|
+
JSON.stringify(Object.keys(costs.stages ?? {}).sort()) !==
|
|
9267
|
+
JSON.stringify(${JSON.stringify([...activeStages].sort())}) ||
|
|
8426
9268
|
costs.stages?.production?.dataRetention !== 'retained-and-protected'
|
|
8427
9269
|
) {
|
|
8428
|
-
throw new Error('Foundation cost manifest does not match the
|
|
9270
|
+
throw new Error('Foundation cost manifest does not match the declared capability and stage contract.');
|
|
8429
9271
|
}
|
|
8430
9272
|
const localHeaders =
|
|
8431
9273
|
frontendSecurityHeaders.profiles?.['local-development']?.headers ?? [];
|
|
@@ -8462,9 +9304,9 @@ export {};
|
|
|
8462
9304
|
export default $config({
|
|
8463
9305
|
app(input: { stage?: string } | undefined) {
|
|
8464
9306
|
const stage = input?.stage ?? 'development';
|
|
8465
|
-
if (!
|
|
9307
|
+
if (!new Set<string>(${JSON.stringify(activeStages)}).has(stage)) {
|
|
8466
9308
|
throw new Error(
|
|
8467
|
-
'Foundation stage ' + stage + ' is
|
|
9309
|
+
'Foundation stage ' + stage + ' is inactive for the ${deploymentProfile} deployment profile.',
|
|
8468
9310
|
);
|
|
8469
9311
|
}
|
|
8470
9312
|
const production = stage === 'production';
|
|
@@ -8476,16 +9318,15 @@ export default $config({
|
|
|
8476
9318
|
providers: {
|
|
8477
9319
|
aws: {
|
|
8478
9320
|
defaultTags: {
|
|
9321
|
+
// SST contributes sst:app and sst:stage. Keep the provider baseline
|
|
9322
|
+
// at eight tags so generated S3 asset objects remain within AWS's
|
|
9323
|
+
// hard ten-tag object limit. Richer performance tags stay explicit.
|
|
8479
9324
|
tags: {
|
|
8480
9325
|
'ebk:cost-allocation': 'foundation',
|
|
8481
9326
|
'ebk:cost-scope': '${name}/foundation/' + stage,
|
|
8482
9327
|
'ebk:framework-version': '${frameworkVersion}',
|
|
8483
9328
|
'ebk:managed-by': 'empire-builder-kit',
|
|
8484
9329
|
'ebk:owner': 'foundation',
|
|
8485
|
-
'ebk:production-profile':
|
|
8486
|
-
process.env.EBK_PRODUCTION_FOUNDATION_PROFILE ?? 'standard',
|
|
8487
|
-
'ebk:repository':
|
|
8488
|
-
process.env.GITHUB_REPOSITORY ?? '${name}',
|
|
8489
9330
|
'ebk:slice': 'foundation',
|
|
8490
9331
|
'ebk:stage': stage,
|
|
8491
9332
|
'ebk:workspace': '${name}',
|
|
@@ -8497,15 +9338,8 @@ export default $config({
|
|
|
8497
9338
|
};
|
|
8498
9339
|
},
|
|
8499
9340
|
async run() {
|
|
8500
|
-
const workloadRoles = $dev
|
|
8501
|
-
? undefined
|
|
8502
|
-
: await import(
|
|
8503
|
-
'./infra/workload-roles/preflight.js'
|
|
8504
|
-
).then(({ preflightFoundationWorkloadRoles }) =>
|
|
8505
|
-
preflightFoundationWorkloadRoles($app.stage),
|
|
8506
|
-
);
|
|
8507
9341
|
const { app } = await import('./infra/index.js');
|
|
8508
|
-
return app(
|
|
9342
|
+
return app();
|
|
8509
9343
|
},
|
|
8510
9344
|
});
|
|
8511
9345
|
`,
|
|
@@ -8520,6 +9354,7 @@ export default $config({
|
|
|
8520
9354
|
],
|
|
8521
9355
|
exclude: ['.sst', 'dist', 'node_modules'],
|
|
8522
9356
|
}),
|
|
9357
|
+
[dependency_overrides_js_1.DEPENDENCY_OVERRIDES_PATH]: json(dependencyOverrides),
|
|
8523
9358
|
'pnpm-workspace.yaml': `packages:
|
|
8524
9359
|
- 'packages/*'
|
|
8525
9360
|
- 'packages/*/apps/*'
|
|
@@ -8554,6 +9389,7 @@ supportedArchitectures:
|
|
|
8554
9389
|
strictDepBuilds: true
|
|
8555
9390
|
allowBuilds:
|
|
8556
9391
|
${index_js_6.FRAMEWORK_REVIEWED_BUILD_PACKAGES.map((packageName) => ` '${packageName}': true`).join('\n')}
|
|
9392
|
+
${(0, dependency_overrides_js_1.renderDependencyOverridesYaml)(dependencyOverrides)}
|
|
8557
9393
|
`,
|
|
8558
9394
|
'tsconfig.base.json': json({
|
|
8559
9395
|
compilerOptions: {
|
|
@@ -9376,7 +10212,9 @@ process.stdout.write(
|
|
|
9376
10212
|
);
|
|
9377
10213
|
`,
|
|
9378
10214
|
'tools/ebk-git-policy.mjs': `import { evaluateGitPolicy } from '@empire-builder-kit/nx/git-policy';
|
|
10215
|
+
import { deliveryLaneOf, readRepositoryOperatingPolicy } from '@empire-builder-kit/nx/repository-policy';
|
|
9379
10216
|
import { spawnSync } from 'node:child_process';
|
|
10217
|
+
const deliveryLane = deliveryLaneOf(readRepositoryOperatingPolicy(process.cwd()));
|
|
9380
10218
|
|
|
9381
10219
|
const git = (...args) => {
|
|
9382
10220
|
const result = spawnSync('git', args, { encoding: 'utf8', shell: false });
|
|
@@ -9396,32 +10234,40 @@ if (!['pull_request', 'push', 'workflow_dispatch'].includes(eventName)) {
|
|
|
9396
10234
|
throw new Error('Unsupported or missing GITHUB_EVENT_NAME; Git policy fails closed.');
|
|
9397
10235
|
}
|
|
9398
10236
|
|
|
9399
|
-
const request = { eventName, ref: process.env.GITHUB_REF };
|
|
10237
|
+
const request = { deliveryLane, eventName, ref: process.env.GITHUB_REF };
|
|
9400
10238
|
if (eventName === 'pull_request' || eventName === 'push') {
|
|
9401
10239
|
git(
|
|
9402
10240
|
'fetch',
|
|
9403
10241
|
'--no-tags',
|
|
9404
10242
|
'origin',
|
|
9405
10243
|
'main:refs/remotes/origin/main',
|
|
9406
|
-
'develop:refs/remotes/origin/develop',
|
|
10244
|
+
...(deliveryLane === 'trunk' ? [] : ['develop:refs/remotes/origin/develop']),
|
|
9407
10245
|
);
|
|
9408
10246
|
}
|
|
9409
10247
|
if (eventName === 'pull_request') {
|
|
9410
10248
|
const baseRef = process.env.GITHUB_BASE_REF;
|
|
10249
|
+
const headRepository = process.env.EBK_HEAD_REPOSITORY;
|
|
9411
10250
|
const headRef = process.env.GITHUB_HEAD_REF;
|
|
9412
|
-
|
|
9413
|
-
|
|
10251
|
+
const repository = process.env.GITHUB_REPOSITORY;
|
|
10252
|
+
if (!baseRef || !headRepository || !headRef || !repository) {
|
|
10253
|
+
throw new Error('Pull-request branch and repository metadata is missing.');
|
|
9414
10254
|
}
|
|
9415
10255
|
Object.assign(request, {
|
|
9416
10256
|
baseIsAncestorOfHead: isAncestor('refs/remotes/origin/' + baseRef, 'HEAD'),
|
|
9417
10257
|
baseRef,
|
|
10258
|
+
headRepository,
|
|
9418
10259
|
headRef,
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
|
|
9422
|
-
|
|
10260
|
+
...(deliveryLane === 'trunk'
|
|
10261
|
+
? {}
|
|
10262
|
+
: {
|
|
10263
|
+
mainIsAncestorOfDevelop: isAncestor(
|
|
10264
|
+
'refs/remotes/origin/main',
|
|
10265
|
+
'refs/remotes/origin/develop',
|
|
10266
|
+
),
|
|
10267
|
+
}),
|
|
10268
|
+
repository,
|
|
9423
10269
|
});
|
|
9424
|
-
} else if (eventName === 'push') {
|
|
10270
|
+
} else if (eventName === 'push' && deliveryLane !== 'trunk') {
|
|
9425
10271
|
Object.assign(request, {
|
|
9426
10272
|
baseIsAncestorOfHead: isAncestor('refs/remotes/origin/develop', 'HEAD'),
|
|
9427
10273
|
mainIsAncestorOfDevelop: isAncestor(
|
|
@@ -9435,7 +10281,7 @@ const decision = evaluateGitPolicy(request);
|
|
|
9435
10281
|
process.stdout.write(JSON.stringify(decision, null, 2) + '\\n');
|
|
9436
10282
|
if (!decision.allowed) process.exitCode = 1;
|
|
9437
10283
|
`,
|
|
9438
|
-
'tools/workspace-health.mjs': `import {
|
|
10284
|
+
'tools/workspace-health.mjs': `import { readRepositoryOperatingPolicy } from '@empire-builder-kit/nx/repository-policy';
|
|
9439
10285
|
import { assertWorkMarkerReport, formatWorkMarkerReport, scanWorkMarkers } from '@empire-builder-kit/nx/work-markers';
|
|
9440
10286
|
import { createHash } from 'node:crypto';
|
|
9441
10287
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
@@ -9498,13 +10344,7 @@ if (
|
|
|
9498
10344
|
validateOwnership('.', workspace, 'Workspace');
|
|
9499
10345
|
validateOwnership('packages/foundation', foundation, 'Foundation');
|
|
9500
10346
|
|
|
9501
|
-
|
|
9502
|
-
if (!policy.valid) {
|
|
9503
|
-
throw new Error(
|
|
9504
|
-
'Repository Operating Policy is invalid:\\n' +
|
|
9505
|
-
policy.issues.map(({ message, path }) => '- ' + path + ': ' + message).join('\\n'),
|
|
9506
|
-
);
|
|
9507
|
-
}
|
|
10347
|
+
readRepositoryOperatingPolicy(process.cwd());
|
|
9508
10348
|
|
|
9509
10349
|
for (const name of readdirSync('packages')) {
|
|
9510
10350
|
if (name === 'foundation') continue;
|
|
@@ -9522,7 +10362,63 @@ assertWorkMarkerReport(markerReport);
|
|
|
9522
10362
|
process.stdout.write('Empire Builder Kit workspace source health is green.\\n');
|
|
9523
10363
|
`,
|
|
9524
10364
|
};
|
|
9525
|
-
Object.assign(files, SUPPLY_CHAIN_WORKSPACE_FILE_PLAN.files,
|
|
10365
|
+
Object.assign(files, SUPPLY_CHAIN_WORKSPACE_FILE_PLAN.files, developerAgentFilePlan.files, (0, workspace_files_adopter_skills_js_1.renderAdopterSkills)({ name, deploymentProfile }), (0, workspace_files_stage_authority_js_1.renderStageAuthorityWorkspaceFiles)({
|
|
10366
|
+
deploymentProfile,
|
|
10367
|
+
name,
|
|
10368
|
+
workspaceDeploymentId,
|
|
10369
|
+
}));
|
|
10370
|
+
Object.assign(files, (0, workspace_files_ecr_inspector_js_1.renderThinEcrInspectorWorkflow)({
|
|
10371
|
+
actionPins: {
|
|
10372
|
+
checkout: checkoutAction,
|
|
10373
|
+
configureAwsCredentials: configureAwsAction,
|
|
10374
|
+
downloadArtifact: downloadArtifactAction,
|
|
10375
|
+
pnpmSetup: pnpmAction,
|
|
10376
|
+
setupNode: setupNodeAction,
|
|
10377
|
+
uploadArtifact: uploadArtifactAction,
|
|
10378
|
+
},
|
|
10379
|
+
deploymentProfile,
|
|
10380
|
+
name,
|
|
10381
|
+
nodeVersion: tool_versions_js_1.WORKSPACE_NODE_VERSION,
|
|
10382
|
+
}), (0, workspace_files_thin_deploy_js_1.renderThinDeployWorkflowFilePlan)({
|
|
10383
|
+
deliveryLane,
|
|
10384
|
+
productionApprovalMode,
|
|
10385
|
+
actionPins: {
|
|
10386
|
+
cache: cacheAction,
|
|
10387
|
+
checkout: checkoutAction,
|
|
10388
|
+
configureAwsCredentials: configureAwsAction,
|
|
10389
|
+
downloadArtifact: downloadArtifactAction,
|
|
10390
|
+
pnpmSetup: pnpmAction,
|
|
10391
|
+
setupNode: setupNodeAction,
|
|
10392
|
+
uploadArtifact: uploadArtifactAction,
|
|
10393
|
+
},
|
|
10394
|
+
deploymentProfile,
|
|
10395
|
+
name,
|
|
10396
|
+
toolVersions: {
|
|
10397
|
+
node: tool_versions_js_1.WORKSPACE_NODE_VERSION,
|
|
10398
|
+
sst: tool_versions_js_1.WORKSPACE_SST_VERSION,
|
|
10399
|
+
},
|
|
10400
|
+
}).files, (0, workspace_files_thin_docs_js_1.renderThinLifecycleDocumentation)({
|
|
10401
|
+
deliveryLane,
|
|
10402
|
+
deploymentProfile,
|
|
10403
|
+
name,
|
|
10404
|
+
productionApprovalMode,
|
|
10405
|
+
}));
|
|
10406
|
+
const thinReleaseWorkflowPlan = (0, workspace_files_thin_release_js_1.renderThinReleaseWorkflowPlan)();
|
|
10407
|
+
Object.assign(files, thinReleaseWorkflowPlan.files);
|
|
10408
|
+
for (const obsoletePath of [
|
|
10409
|
+
...thinReleaseWorkflowPlan.obsoletePaths,
|
|
10410
|
+
// The trunk lane has no develop branch to sync main back into; trunk
|
|
10411
|
+
// promotion only ever fast-forwards main from develop, so there is
|
|
10412
|
+
// nothing to sync back either.
|
|
10413
|
+
...(trunkLane || trunkPromotion ? ['.github/workflows/sync-back.yml'] : []),
|
|
10414
|
+
'.github/workflows/performance-aggregate-counts.yml',
|
|
10415
|
+
'.github/workflows/performance-cost-actual.yml',
|
|
10416
|
+
'.github/workflows/performance-load.yml',
|
|
10417
|
+
'packages/foundation/operations/infrastructure-change.json',
|
|
10418
|
+
'tools/ebk-infrastructure-plan-summary.mjs',
|
|
10419
|
+
]) {
|
|
10420
|
+
delete files[obsoletePath];
|
|
10421
|
+
}
|
|
9526
10422
|
for (const [path, source] of Object.entries(files)) {
|
|
9527
10423
|
if (/^\.github\/workflows\/[^/]+\.ya?ml$/u.test(path)) {
|
|
9528
10424
|
files[path] = isolateOidcTokenBeforeAwsAuthority(source, configureAwsAction);
|