@empire-builder-kit/nx 1.0.0-rc.4 → 1.0.0-rc.41
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 +44 -10
- 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.d.ts +1 -1
- 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 +30 -0
- package/src/cli/deploy-runtime.js +174 -0
- package/src/cli/dev-command.d.ts +18 -4
- package/src/cli/dev-command.js +71 -0
- package/src/cli/main.d.ts +9 -1
- package/src/cli/main.js +157 -24
- package/src/cli/output.d.ts +1 -1
- package/src/cli/output.js +1 -1
- package/src/cli/personal-secret-runtime.d.ts +12 -0
- package/src/cli/personal-secret-runtime.js +18 -3
- package/src/cli/secret-terminal.js +1 -1
- package/src/container-images.d.ts +1 -1
- package/src/container-images.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 +15 -0
- package/src/developer-agent/operation-registry.js +342 -49
- package/src/developer-agent/planning.d.ts +8 -1
- package/src/developer-agent/planning.js +39 -16
- 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 +163 -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 +46 -6
- package/src/executors/sst-lifecycle/run-sst-command.js +750 -71
- 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/cost-estimate.d.ts +170 -0
- package/src/foundation/cost-estimate.js +683 -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/workspace-files-adopter-skills.d.ts +7 -0
- package/src/generators/preset/workspace-files-adopter-skills.js +324 -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 +162 -44
- 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-shared-operation-claims.d.ts +10 -0
- package/src/generators/preset/workspace-files-shared-operation-claims.js +59 -0
- package/src/generators/preset/workspace-files-stage-authority.d.ts +6 -0
- package/src/generators/preset/workspace-files-stage-authority.js +529 -0
- package/src/generators/preset/workspace-files-thin-deploy.d.ts +65 -0
- package/src/generators/preset/workspace-files-thin-deploy.js +2507 -0
- package/src/generators/preset/workspace-files-thin-docs.d.ts +7 -0
- package/src/generators/preset/workspace-files-thin-docs.js +370 -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 +1525 -609
- 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 +49 -11
- 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 +102 -334
- package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js +975 -233
- 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 +176 -1200
- 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 +25 -2
- 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 +14 -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 -730
- package/src/generators/slice/built-in-plan-infrastructure-safety-files.js +75 -14
- 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 +159 -29
- 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 +283 -44
- 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-event-files.d.ts +7 -0
- package/src/generators/slice/built-in-plan-testing-persistence-event-files.js +487 -0
- 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 +180 -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 +83 -30
- 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/frozen-container.d.ts +1 -1
- 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 +48 -8
- package/src/git-policy/github-bootstrap.js +344 -176
- 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 +58 -22
- 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 +16 -4
- 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/personal-command.d.ts +18 -2
- package/src/secrets/personal-command.js +66 -12
- package/src/secrets/registry.d.ts +2 -1
- package/src/secrets/registry.js +18 -1
- package/src/secrets/verification.js +15 -2
- 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,19 +7,27 @@ 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");
|
|
14
|
+
const cost_estimate_js_1 = require("../../foundation/cost-estimate.js");
|
|
12
15
|
const index_js_3 = require("../../git-policy/index.js");
|
|
13
16
|
const index_js_4 = require("../../repository-policy/index.js");
|
|
14
17
|
const index_js_5 = require("../../release/index.js");
|
|
15
18
|
const index_js_6 = require("../../supply-chain/index.js");
|
|
16
19
|
const deterministic_order_js_1 = require("../../deterministic-order.js");
|
|
17
20
|
const tool_versions_js_1 = require("./tool-versions.js");
|
|
21
|
+
const workspace_files_shared_operation_claims_js_1 = require("./workspace-files-shared-operation-claims.js");
|
|
18
22
|
const workspace_files_developer_agent_js_1 = require("./workspace-files-developer-agent.js");
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
+
const workspace_files_ci_acceptance_js_1 = require("./workspace-files-ci-acceptance.js");
|
|
24
|
+
const workspace_files_ci_install_js_1 = require("./workspace-files-ci-install.js");
|
|
25
|
+
const workspace_files_adopter_skills_js_1 = require("./workspace-files-adopter-skills.js");
|
|
26
|
+
const workspace_files_ecr_inspector_js_1 = require("./workspace-files-ecr-inspector.js");
|
|
27
|
+
const workspace_files_thin_deploy_js_1 = require("./workspace-files-thin-deploy.js");
|
|
28
|
+
const workspace_files_thin_docs_js_1 = require("./workspace-files-thin-docs.js");
|
|
29
|
+
const workspace_files_thin_release_js_1 = require("./workspace-files-thin-release.js");
|
|
30
|
+
const workspace_files_stage_authority_js_1 = require("./workspace-files-stage-authority.js");
|
|
23
31
|
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
32
|
const GENERATED_RELEASE_ROLLOUT_PLAN_ID_PATTERN = JSON.stringify(index_js_5.RELEASE_ROLLOUT_PLAN_ID_PATTERN_SOURCE);
|
|
25
33
|
exports.BLUEPRINT_TRUST_AUTHORITY_PATH = 'tools/blueprint-trust-check.mjs';
|
|
@@ -60,11 +68,12 @@ exports.PLAN_PROVIDER_REFRESH_GLOBAL_READ_ACTIONS = [
|
|
|
60
68
|
'rds:DescribeDBProxies',
|
|
61
69
|
'rds:DescribeDBSubnetGroups',
|
|
62
70
|
];
|
|
63
|
-
function validatePlanProviderRefreshPolicy(value, region, account) {
|
|
71
|
+
function validatePlanProviderRefreshPolicy(value, region, account, stage) {
|
|
64
72
|
if (!Array.isArray(value) ||
|
|
65
73
|
value.length === 0 ||
|
|
66
74
|
!/^[a-z]{2}(?:-gov)?-[a-z]+-\d$/.test(region) ||
|
|
67
|
-
!/^\d{12}$/.test(account)
|
|
75
|
+
!/^\d{12}$/.test(account) ||
|
|
76
|
+
!['production', 'staging'].includes(stage)) {
|
|
68
77
|
throw new Error('Provider refresh policy must be a non-empty exact statement array for one account and region.');
|
|
69
78
|
}
|
|
70
79
|
const allowed = new Set(exports.PLAN_PROVIDER_REFRESH_READ_ACTIONS);
|
|
@@ -117,6 +126,14 @@ function validatePlanProviderRefreshPolicy(value, region, account) {
|
|
|
117
126
|
if (resources.some((resource) => {
|
|
118
127
|
const arn = resource.split(':');
|
|
119
128
|
const target = arn.slice(5).join(':');
|
|
129
|
+
const boundedPathWildcard = target.endsWith('/*') && !/[*?]/.test(target.slice(0, -2));
|
|
130
|
+
const boundedLambdaStagePrefix = arn[2] === 'lambda' &&
|
|
131
|
+
actions.every((action) => action.startsWith('lambda:') && !global.has(action)) &&
|
|
132
|
+
new RegExp('^function:(?:' +
|
|
133
|
+
stage +
|
|
134
|
+
'-|[A-Za-z0-9][A-Za-z0-9_-]*-' +
|
|
135
|
+
stage +
|
|
136
|
+
'-)[A-Za-z0-9_-]*\\*$').test(target);
|
|
120
137
|
return (arn[0] !== 'arn' ||
|
|
121
138
|
arn[1] !== 'aws' ||
|
|
122
139
|
!/^[a-z0-9-]+$/.test(arn[2] ?? '') ||
|
|
@@ -124,7 +141,8 @@ function validatePlanProviderRefreshPolicy(value, region, account) {
|
|
|
124
141
|
!['', account].includes(arn[4] ?? '') ||
|
|
125
142
|
!target ||
|
|
126
143
|
(/[*?]/.test(target) &&
|
|
127
|
-
|
|
144
|
+
!boundedPathWildcard &&
|
|
145
|
+
!boundedLambdaStagePrefix));
|
|
128
146
|
})) {
|
|
129
147
|
throw new Error(`Provider refresh statement ${index} is not bounded to exact targets in the selected account and region.`);
|
|
130
148
|
}
|
|
@@ -161,6 +179,51 @@ const TOOL_VERSIONS = {
|
|
|
161
179
|
function json(value) {
|
|
162
180
|
return `${JSON.stringify(value, null, 2)}\n`;
|
|
163
181
|
}
|
|
182
|
+
function indentWorkflowScript(source, spaces) {
|
|
183
|
+
const prefix = ' '.repeat(spaces);
|
|
184
|
+
return source
|
|
185
|
+
.trim()
|
|
186
|
+
.split('\n')
|
|
187
|
+
.map((line) => `${prefix}${line}`)
|
|
188
|
+
.join('\n');
|
|
189
|
+
}
|
|
190
|
+
function protectedSecretPolicyDerivationScript() {
|
|
191
|
+
return `node --input-type=module <<'NODE'
|
|
192
|
+
import { appendFileSync } from 'node:fs';
|
|
193
|
+
import {
|
|
194
|
+
configuredDeploymentAccountForStage,
|
|
195
|
+
configuredDeploymentRegion,
|
|
196
|
+
isActiveDeploymentStage,
|
|
197
|
+
readRepositoryOperatingPolicy,
|
|
198
|
+
} from '@empire-builder-kit/nx/repository-policy';
|
|
199
|
+
|
|
200
|
+
const output = process.env.GITHUB_OUTPUT?.trim() ?? '';
|
|
201
|
+
const renderedProfile = process.env.EBK_RENDERED_PROFILE?.trim() ?? '';
|
|
202
|
+
const stage = process.env.EBK_STAGE?.trim() ?? '';
|
|
203
|
+
if (!output) throw new Error('Secret workflow authority output is unavailable.');
|
|
204
|
+
const policy = readRepositoryOperatingPolicy(process.cwd());
|
|
205
|
+
if (policy.deploymentProfile !== renderedProfile) {
|
|
206
|
+
throw new Error('Generated and committed deployment profiles differ.');
|
|
207
|
+
}
|
|
208
|
+
if (!isActiveDeploymentStage(policy.deploymentProfile, stage)) {
|
|
209
|
+
throw new Error('Secret workflow stage is inactive for the committed deployment profile.');
|
|
210
|
+
}
|
|
211
|
+
configuredDeploymentAccountForStage(
|
|
212
|
+
policy.deploymentProfile,
|
|
213
|
+
policy.deploymentAccounts,
|
|
214
|
+
stage,
|
|
215
|
+
);
|
|
216
|
+
const accounts = policy.deploymentAccounts;
|
|
217
|
+
const region = configuredDeploymentRegion(policy.deploymentRegion);
|
|
218
|
+
appendFileSync(output, [
|
|
219
|
+
'deployment_region=' + region,
|
|
220
|
+
'development_account_id=' + (accounts.development ?? ''),
|
|
221
|
+
'staging_account_id=' + (accounts.staging ?? ''),
|
|
222
|
+
'production_account_id=' + (accounts.production ?? ''),
|
|
223
|
+
'',
|
|
224
|
+
].join('\\n'));
|
|
225
|
+
NODE`;
|
|
226
|
+
}
|
|
164
227
|
function planApplicationBindingCheck() {
|
|
165
228
|
return ` node - <<'NODE'
|
|
166
229
|
const declared = JSON.parse(process.env.AWS_PLAN_SST_APPS_JSON ?? '');
|
|
@@ -474,6 +537,8 @@ function planRoleAuditStep(branch, mode = 'produce') {
|
|
|
474
537
|
EBK_PLAN_ENVIRONMENT: infrastructure-plan-${stage}
|
|
475
538
|
EBK_PLAN_ROLE_ARN: \${{ env.AWS_PLAN_ROLE_ARN }}
|
|
476
539
|
EBK_PLAN_REPOSITORY: \${{ github.repository }}
|
|
540
|
+
EBK_PLAN_REPOSITORY_ID: \${{ github.repository_id }}
|
|
541
|
+
EBK_PLAN_REPOSITORY_OWNER_ID: \${{ github.repository_owner_id }}
|
|
477
542
|
run: |
|
|
478
543
|
role_name="\${EBK_PLAN_ROLE_ARN##*/}"
|
|
479
544
|
test "$(aws sts get-caller-identity --query Account --output text)" = "$AWS_EXPECTED_ACCOUNT_ID" ||
|
|
@@ -506,8 +571,23 @@ function planRoleAuditStep(branch, mode = 'produce') {
|
|
|
506
571
|
const role = JSON.parse(readFileSync(process.argv[2], 'utf8')).Role;
|
|
507
572
|
const policy = JSON.parse(readFileSync(process.argv[3], 'utf8')).PolicyDocument;
|
|
508
573
|
const account = process.env.AWS_EXPECTED_ACCOUNT_ID;
|
|
574
|
+
const repository = process.env.EBK_PLAN_REPOSITORY ?? '';
|
|
575
|
+
const repositoryId = process.env.EBK_PLAN_REPOSITORY_ID ?? '';
|
|
576
|
+
const repositoryOwnerId = process.env.EBK_PLAN_REPOSITORY_OWNER_ID ?? '';
|
|
509
577
|
const stateBucketArn = process.env.AWS_PLAN_STATE_BUCKET_ARN;
|
|
510
578
|
const stateKmsKeyArn = process.env.AWS_PLAN_STATE_KMS_KEY_ARN;
|
|
579
|
+
if (
|
|
580
|
+
!/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\\/[A-Za-z0-9_.-]{1,100}$/.test(
|
|
581
|
+
repository,
|
|
582
|
+
) ||
|
|
583
|
+
!/^[1-9][0-9]*$/.test(repositoryOwnerId) ||
|
|
584
|
+
!/^[1-9][0-9]*$/.test(repositoryId)
|
|
585
|
+
) throw new Error('Exact immutable GitHub repository identity is required.');
|
|
586
|
+
const [repositoryOwner, repositoryName] = repository.split('/');
|
|
587
|
+
const oidcSubject =
|
|
588
|
+
'repo:' + repositoryOwner + '@' + repositoryOwnerId + '/' +
|
|
589
|
+
repositoryName + '@' + repositoryId + ':environment:' +
|
|
590
|
+
process.env.EBK_PLAN_ENVIRONMENT;
|
|
511
591
|
const parseExactArnArray = (name, pattern) => {
|
|
512
592
|
let value;
|
|
513
593
|
try {
|
|
@@ -551,6 +631,7 @@ function planRoleAuditStep(branch, mode = 'produce') {
|
|
|
551
631
|
'AWS_PLAN_GENERATED_SECRET_ARNS_JSON',
|
|
552
632
|
new RegExp('^arn:aws:secretsmanager:' + process.env.AWS_REGION + ':' + account + ':secret:ebk/'),
|
|
553
633
|
);
|
|
634
|
+
const stateStage = process.env.EBK_PLAN_BRANCH === 'main' ? 'production' : 'staging';
|
|
554
635
|
let providerRefreshPolicy;
|
|
555
636
|
try {
|
|
556
637
|
providerRefreshPolicy = JSON.parse(
|
|
@@ -614,6 +695,23 @@ function planRoleAuditStep(branch, mode = 'produce') {
|
|
|
614
695
|
for (const resource of resources) {
|
|
615
696
|
const arn = resource.split(':');
|
|
616
697
|
const target = arn.slice(5).join(':');
|
|
698
|
+
const boundedPathWildcard =
|
|
699
|
+
target.endsWith('/*') &&
|
|
700
|
+
!/[*?]/.test(target.slice(0, -2));
|
|
701
|
+
const boundedLambdaStagePrefix =
|
|
702
|
+
arn[2] === 'lambda' &&
|
|
703
|
+
actions.every(
|
|
704
|
+
(action) =>
|
|
705
|
+
action.startsWith('lambda:') &&
|
|
706
|
+
!globalProviderReadActions.has(action),
|
|
707
|
+
) &&
|
|
708
|
+
new RegExp(
|
|
709
|
+
'^function:(?:' +
|
|
710
|
+
stateStage +
|
|
711
|
+
'-|[A-Za-z0-9][A-Za-z0-9_-]*-' +
|
|
712
|
+
stateStage +
|
|
713
|
+
'-)[A-Za-z0-9_-]*\\\\*$',
|
|
714
|
+
).test(target);
|
|
617
715
|
if (
|
|
618
716
|
arn[0] !== 'arn' ||
|
|
619
717
|
arn[1] !== 'aws' ||
|
|
@@ -622,7 +720,8 @@ function planRoleAuditStep(branch, mode = 'produce') {
|
|
|
622
720
|
!['', account].includes(arn[4] ?? '') ||
|
|
623
721
|
!target ||
|
|
624
722
|
(/[*?]/.test(target) &&
|
|
625
|
-
|
|
723
|
+
!boundedPathWildcard &&
|
|
724
|
+
!boundedLambdaStagePrefix)
|
|
626
725
|
) throw new Error('Provider refresh statement ' + index + ' has a target outside the selected account and region.');
|
|
627
726
|
}
|
|
628
727
|
}
|
|
@@ -637,7 +736,6 @@ function planRoleAuditStep(branch, mode = 'produce') {
|
|
|
637
736
|
).sort((left, right) =>
|
|
638
737
|
left.Sid < right.Sid ? -1 : left.Sid > right.Sid ? 1 : 0,
|
|
639
738
|
);
|
|
640
|
-
const stateStage = process.env.EBK_PLAN_BRANCH === 'main' ? 'production' : 'staging';
|
|
641
739
|
if (
|
|
642
740
|
stateKmsKeyArn &&
|
|
643
741
|
stateKmsKeyArn !==
|
|
@@ -681,7 +779,7 @@ function planRoleAuditStep(branch, mode = 'produce') {
|
|
|
681
779
|
Action: 'sts:AssumeRoleWithWebIdentity',
|
|
682
780
|
Condition: { StringEquals: {
|
|
683
781
|
'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com',
|
|
684
|
-
'token.actions.githubusercontent.com:sub':
|
|
782
|
+
'token.actions.githubusercontent.com:sub': oidcSubject,
|
|
685
783
|
}},
|
|
686
784
|
}],
|
|
687
785
|
};
|
|
@@ -703,11 +801,24 @@ function planRoleAuditStep(branch, mode = 'produce') {
|
|
|
703
801
|
Sid: 'ObserveInfrastructureIdentity',
|
|
704
802
|
Effect: 'Allow',
|
|
705
803
|
Action: [
|
|
706
|
-
'
|
|
804
|
+
'tag:GetResources',
|
|
707
805
|
'sts:GetCallerIdentity',
|
|
708
806
|
],
|
|
709
807
|
Resource: '*',
|
|
710
808
|
},
|
|
809
|
+
{
|
|
810
|
+
Sid: 'ObserveStageSecretVersionIdentity',
|
|
811
|
+
Effect: 'Allow',
|
|
812
|
+
Action: 'secretsmanager:ListSecretVersionIds',
|
|
813
|
+
Resource:
|
|
814
|
+
'arn:aws:secretsmanager:' + process.env.AWS_REGION + ':' +
|
|
815
|
+
account + ':secret:*',
|
|
816
|
+
Condition: {
|
|
817
|
+
StringEquals: {
|
|
818
|
+
'aws:ResourceTag/ebk:stage': stateStage,
|
|
819
|
+
},
|
|
820
|
+
},
|
|
821
|
+
},
|
|
711
822
|
{
|
|
712
823
|
Sid: 'ValidatePlannedIamPolicies',
|
|
713
824
|
Effect: 'Allow',
|
|
@@ -782,7 +893,10 @@ function planRoleAuditStep(branch, mode = 'produce') {
|
|
|
782
893
|
generatedSecretArns,
|
|
783
894
|
providerRefreshStatements,
|
|
784
895
|
region: process.env.AWS_REGION,
|
|
785
|
-
repository
|
|
896
|
+
repository,
|
|
897
|
+
repositoryId,
|
|
898
|
+
repositoryOwnerId,
|
|
899
|
+
oidcSubject,
|
|
786
900
|
role: {
|
|
787
901
|
arn: role.Arn,
|
|
788
902
|
policy: canonical(policy),
|
|
@@ -826,11 +940,17 @@ function planRoleAuditJob(stage, configureAwsAction) {
|
|
|
826
940
|
const production = stage === 'production';
|
|
827
941
|
const branch = production ? 'main' : 'develop';
|
|
828
942
|
const prefix = production ? 'AWS_PRODUCTION_PLAN' : 'AWS_STAGING_PLAN';
|
|
943
|
+
const profileAdmission = production
|
|
944
|
+
? ''
|
|
945
|
+
: ` (github.event_name == 'workflow_dispatch' ||\n needs.deployment-mode.outputs.profile != 'starter') &&\n`;
|
|
829
946
|
return ` infrastructure-plan-role-audit-${stage}:
|
|
830
947
|
name: audit ${stage} infrastructure plan role
|
|
831
|
-
needs: route
|
|
948
|
+
needs: [route, deployment-mode]
|
|
832
949
|
if: |
|
|
833
|
-
|
|
950
|
+
always() &&
|
|
951
|
+
needs.route.result == 'success' &&
|
|
952
|
+
needs.deployment-mode.result == 'success' &&
|
|
953
|
+
${profileAdmission} github.ref == 'refs/heads/${branch}' &&
|
|
834
954
|
(github.event_name == 'push' && 'deploy' || inputs.action) != 'unlock'
|
|
835
955
|
runs-on: ubuntu-latest
|
|
836
956
|
environment: infrastructure-plan-${stage}
|
|
@@ -938,6 +1058,7 @@ const SUPPLY_CHAIN_WORKSPACE_FILE_PLAN = (0, index_js_6.renderSupplyChainWorkspa
|
|
|
938
1058
|
generatedAt: '1970-01-01T00:00:00.000Z',
|
|
939
1059
|
});
|
|
940
1060
|
const WORKSPACE_SEEDED_PATHS = new Set([
|
|
1061
|
+
dependency_overrides_js_1.DEPENDENCY_OVERRIDES_PATH,
|
|
941
1062
|
...SUPPLY_CHAIN_WORKSPACE_FILE_PLAN.seeded,
|
|
942
1063
|
...workspace_files_developer_agent_js_1.DEVELOPER_AGENT_WORKSPACE_SEEDED_PATHS,
|
|
943
1064
|
'.github/CODEOWNERS',
|
|
@@ -947,7 +1068,10 @@ const WORKSPACE_SEEDED_PATHS = new Set([
|
|
|
947
1068
|
'docs/architecture.md',
|
|
948
1069
|
'docs/git-workflow.md',
|
|
949
1070
|
'docs/repository-operating-policy.md',
|
|
1071
|
+
'docs/stage-deployment-authority.md',
|
|
1072
|
+
'docs/workspace-deployment-ownership.md',
|
|
950
1073
|
'docs/work-markers.md',
|
|
1074
|
+
workspace_files_shared_operation_claims_js_1.SHARED_OPERATION_CLAIMS_PATH,
|
|
951
1075
|
'ebk.repository-policy.json',
|
|
952
1076
|
]);
|
|
953
1077
|
exports.WORKSPACE_PACKAGE_MANAGED_JSON_POINTERS = [
|
|
@@ -964,21 +1088,23 @@ exports.WORKSPACE_PROJECT_MANAGED_JSON_POINTERS = [
|
|
|
964
1088
|
'/root',
|
|
965
1089
|
'/tags',
|
|
966
1090
|
'/targets/blueprint-trust-check',
|
|
1091
|
+
'/targets/blueprint-trust-check-main',
|
|
967
1092
|
'/targets/blueprint-trust-check-protected',
|
|
968
1093
|
'/targets/developer-context',
|
|
969
1094
|
'/targets/deployment-plan',
|
|
1095
|
+
'/targets/deploy-local',
|
|
970
1096
|
'/targets/dev',
|
|
971
1097
|
'/targets/dev-doctor',
|
|
972
1098
|
'/targets/dev-setup',
|
|
973
1099
|
'/targets/github-bootstrap',
|
|
974
1100
|
'/targets/install-git-hooks',
|
|
1101
|
+
'/targets/stage-authority-render',
|
|
975
1102
|
'/targets/supply-chain-check',
|
|
976
1103
|
'/targets/supply-chain-check-protected',
|
|
977
1104
|
'/targets/supply-chain-scan',
|
|
978
1105
|
'/targets/verify',
|
|
1106
|
+
'/targets/verify-proposed',
|
|
979
1107
|
'/targets/work-markers',
|
|
980
|
-
'/targets/workload-role-candidate-report',
|
|
981
|
-
'/targets/workload-role-provisioner-build',
|
|
982
1108
|
'/targets/workspace-health',
|
|
983
1109
|
];
|
|
984
1110
|
const FOUNDATION_SEEDED_PATHS = new Set([
|
|
@@ -986,6 +1112,10 @@ const FOUNDATION_SEEDED_PATHS = new Set([
|
|
|
986
1112
|
'docs/costs.md',
|
|
987
1113
|
'docs/runbooks.md',
|
|
988
1114
|
'docs/security.md',
|
|
1115
|
+
'performance/account-cost-policy.json',
|
|
1116
|
+
'security/router-waf-blocking-rehearsal.json',
|
|
1117
|
+
'security/router-waf-live-evidence.json',
|
|
1118
|
+
'security/router-waf.json',
|
|
989
1119
|
]);
|
|
990
1120
|
function createFoundationOwnership(files, frameworkVersion) {
|
|
991
1121
|
const paths = Object.keys(files)
|
|
@@ -1048,7 +1178,48 @@ function createWorkspaceOwnership(files, frameworkVersion) {
|
|
|
1048
1178
|
}
|
|
1049
1179
|
function renderWorkspaceFiles(options) {
|
|
1050
1180
|
const { frameworkVersion, name } = options;
|
|
1181
|
+
const foundationPlan = (0, capability_plan_js_1.planFoundationCapabilities)({
|
|
1182
|
+
current: options.foundationDescriptor,
|
|
1183
|
+
requirements: options.foundationRequirements ?? [],
|
|
1184
|
+
});
|
|
1185
|
+
const dependencyOverrides = (0, dependency_overrides_js_1.parseDependencyOverrides)(options.dependencyOverrides === undefined
|
|
1186
|
+
? { schemaVersion: 1, overrides: {} }
|
|
1187
|
+
: options.dependencyOverrides);
|
|
1188
|
+
const deploymentProfile = options.deploymentProfile ?? 'starter';
|
|
1189
|
+
const deliveryLane = options.deliveryLane ?? 'protected-pair';
|
|
1190
|
+
if (deliveryLane === 'trunk' && deploymentProfile !== 'starter') {
|
|
1191
|
+
throw new Error('The trunk delivery lane requires the starter deployment profile.');
|
|
1192
|
+
}
|
|
1193
|
+
const trunkLane = deliveryLane === 'trunk';
|
|
1194
|
+
const productionApprovalMode = options.productionApprovalMode ?? 'promotion-pr';
|
|
1195
|
+
if (productionApprovalMode === 'trunk-promotion' &&
|
|
1196
|
+
(deploymentProfile === 'starter' || trunkLane)) {
|
|
1197
|
+
throw new Error('trunk-promotion requires an isolated deployment profile on the protected pair.');
|
|
1198
|
+
}
|
|
1199
|
+
const trunkPromotion = productionApprovalMode === 'trunk-promotion';
|
|
1200
|
+
const protectedBranches = trunkLane ? '[main]' : '[develop, main]';
|
|
1201
|
+
const activeStages = (0, index_js_4.activeDeploymentStages)(deploymentProfile);
|
|
1202
|
+
const protectedWorkflowStageChoices = deploymentProfile === 'starter'
|
|
1203
|
+
? 'development, production'
|
|
1204
|
+
: deploymentProfile === 'production-isolated'
|
|
1205
|
+
? 'production'
|
|
1206
|
+
: 'staging, production';
|
|
1207
|
+
const ecrInspectorWorkflowStageChoices = deploymentProfile === 'starter'
|
|
1208
|
+
? 'production'
|
|
1209
|
+
: protectedWorkflowStageChoices;
|
|
1210
|
+
const rehearsalStage = (0, index_js_4.productionRehearsalStage)(deploymentProfile);
|
|
1211
|
+
const protectedWorkflowStageBranchCases = `${rehearsalStage}:refs/heads/develop|production:refs/heads/main`;
|
|
1212
|
+
const workspaceDeploymentId = options.workspaceDeploymentId ??
|
|
1213
|
+
`ebkws-${(0, index_js_1.hashGeneratedContent)(`workspace-deployment-id:${name}`).slice(0, 32)}`;
|
|
1214
|
+
const initialRepositoryPolicy = (0, index_js_4.createIncubationRepositoryPolicy)(deploymentProfile, workspaceDeploymentId);
|
|
1215
|
+
initialRepositoryPolicy.deploymentAccounts = options.deploymentAccounts ?? {
|
|
1216
|
+
development: null,
|
|
1217
|
+
production: null,
|
|
1218
|
+
staging: null,
|
|
1219
|
+
};
|
|
1220
|
+
initialRepositoryPolicy.deploymentRegion = options.deploymentRegion ?? null;
|
|
1051
1221
|
const developerAgentFilePlan = (0, workspace_files_developer_agent_js_1.renderDeveloperAgentWorkspaceFilePlan)({
|
|
1222
|
+
deploymentProfile,
|
|
1052
1223
|
frameworkVersion,
|
|
1053
1224
|
name,
|
|
1054
1225
|
});
|
|
@@ -1059,7 +1230,38 @@ function renderWorkspaceFiles(options) {
|
|
|
1059
1230
|
const pnpmAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.pnpmSetup);
|
|
1060
1231
|
const setupNodeAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.setupNode);
|
|
1061
1232
|
const uploadArtifactAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.uploadArtifact);
|
|
1233
|
+
const cacheAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.cache);
|
|
1234
|
+
const nxSetShasAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.nxSetShas);
|
|
1235
|
+
// GitHub scopes cache reads to the current branch and its base, so protected
|
|
1236
|
+
// pushes never restore entries written by pull-request runs.
|
|
1237
|
+
const nxCacheSteps = ` - name: Restore the Nx task cache
|
|
1238
|
+
uses: ${cacheAction}
|
|
1239
|
+
with:
|
|
1240
|
+
path: .nx/cache
|
|
1241
|
+
key: nx-\${{ runner.os }}-\${{ hashFiles('pnpm-lock.yaml') }}-\${{ github.sha }}
|
|
1242
|
+
restore-keys: |
|
|
1243
|
+
nx-\${{ runner.os }}-\${{ hashFiles('pnpm-lock.yaml') }}-`;
|
|
1244
|
+
// Isolated profiles bind the staging release candidate when promoting to
|
|
1245
|
+
// production, so a main push never needs a rebuilt candidate.
|
|
1246
|
+
const releaseCandidateBranchCondition = deploymentProfile === 'starter'
|
|
1247
|
+
? ''
|
|
1248
|
+
: " &&\n github.ref != 'refs/heads/main'";
|
|
1249
|
+
// In the trunk lane no pull request is a protected route: main is the only
|
|
1250
|
+
// protected branch and every feature pull request targets it.
|
|
1251
|
+
const protectedDevelopPromotion = trunkLane
|
|
1252
|
+
? 'false'
|
|
1253
|
+
: "github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.head.ref == 'develop' && github.event.pull_request.base.ref == 'main'";
|
|
1254
|
+
const protectedMainSync = trunkLane
|
|
1255
|
+
? 'false'
|
|
1256
|
+
: "github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.head.ref == 'main' && github.event.pull_request.base.ref == 'develop'";
|
|
1257
|
+
const protectedPush = trunkLane
|
|
1258
|
+
? "github.event_name == 'push' && github.ref == 'refs/heads/main'"
|
|
1259
|
+
: "github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop')";
|
|
1260
|
+
const protectedDeploymentRouteJobName = 'protected deployment route';
|
|
1261
|
+
const stagingApplyJobPrefix = 'apply staging plan ';
|
|
1062
1262
|
const files = {
|
|
1263
|
+
...(0, workspace_files_ci_acceptance_js_1.renderWorkspaceCiAcceptanceFiles)(),
|
|
1264
|
+
...(0, workspace_files_ci_install_js_1.renderWorkspaceCiInstallFiles)(),
|
|
1063
1265
|
'.node-version': `${tool_versions_js_1.WORKSPACE_NODE_VERSION}\n`,
|
|
1064
1266
|
'.github/CODEOWNERS': `# Replace this placeholder with the GitHub user or team that owns protected source.
|
|
1065
1267
|
# GitHub bootstrap remains blocked until a non-placeholder catch-all owner is committed.
|
|
@@ -1069,21 +1271,83 @@ function renderWorkspaceFiles(options) {
|
|
|
1069
1271
|
|
|
1070
1272
|
on:
|
|
1071
1273
|
pull_request:
|
|
1072
|
-
branches:
|
|
1274
|
+
branches: ${protectedBranches}
|
|
1073
1275
|
push:
|
|
1074
|
-
branches:
|
|
1276
|
+
branches: ${protectedBranches}
|
|
1075
1277
|
workflow_dispatch:
|
|
1076
1278
|
|
|
1077
1279
|
concurrency:
|
|
1078
|
-
group: verify-\${{ github.ref }}
|
|
1079
|
-
cancel-in-progress:
|
|
1280
|
+
group: verify-\${{ github.event_name == 'push' && github.sha || github.ref }}
|
|
1281
|
+
cancel-in-progress: \${{ github.event_name != 'push' }}
|
|
1080
1282
|
|
|
1081
1283
|
permissions:
|
|
1284
|
+
actions: read
|
|
1082
1285
|
contents: read
|
|
1286
|
+
pull-requests: read
|
|
1287
|
+
|
|
1288
|
+
env:
|
|
1289
|
+
EBK_CI_CANDIDATE_SHA: \${{ github.event.pull_request.head.sha || github.sha }}
|
|
1083
1290
|
|
|
1084
1291
|
jobs:
|
|
1292
|
+
acceptance:
|
|
1293
|
+
name: accepted merge evidence
|
|
1294
|
+
if: ${protectedPush}
|
|
1295
|
+
runs-on: ubuntu-latest
|
|
1296
|
+
outputs:
|
|
1297
|
+
history_only: \${{ steps.resolve.outputs.history_only == 'true' && steps.restore.outcome == 'success' && steps.verify.outcome == 'success' }}
|
|
1298
|
+
reused: \${{ steps.resolve.outputs.reused == 'true' && steps.restore.outcome == 'success' && steps.verify.outcome == 'success' }}
|
|
1299
|
+
route: \${{ steps.resolve.outputs.reused == 'true' && steps.restore.outcome == 'success' && steps.verify.outcome == 'success' && steps.resolve.outputs.route || '' }}
|
|
1300
|
+
steps:
|
|
1301
|
+
- name: Check out immutable merged candidate
|
|
1302
|
+
if: ${protectedPush}
|
|
1303
|
+
uses: ${checkoutAction}
|
|
1304
|
+
with:
|
|
1305
|
+
fetch-depth: 0
|
|
1306
|
+
ref: \${{ github.sha }}
|
|
1307
|
+
- name: Resolve exact protected-route acceptance
|
|
1308
|
+
if: ${protectedPush}
|
|
1309
|
+
id: resolve
|
|
1310
|
+
env:
|
|
1311
|
+
GH_TOKEN: \${{ github.token }}
|
|
1312
|
+
run: node tools/ci/workspace-acceptance.mjs resolve
|
|
1313
|
+
- name: Restore exact protected-route acceptance
|
|
1314
|
+
if: steps.resolve.outputs.reused == 'true'
|
|
1315
|
+
id: restore
|
|
1316
|
+
continue-on-error: true
|
|
1317
|
+
env:
|
|
1318
|
+
EBK_ACCEPTANCE_ARTIFACT_DIGEST: \${{ steps.resolve.outputs.artifact_digest }}
|
|
1319
|
+
EBK_ACCEPTANCE_ARTIFACT_ID: \${{ steps.resolve.outputs.artifact_id }}
|
|
1320
|
+
EBK_ACCEPTANCE_ARTIFACT_SIZE: \${{ steps.resolve.outputs.artifact_size }}
|
|
1321
|
+
GH_TOKEN: \${{ github.token }}
|
|
1322
|
+
run: >-
|
|
1323
|
+
node tools/ci/workspace-acceptance.mjs download
|
|
1324
|
+
.ebk/evidence/reused-acceptance
|
|
1325
|
+
"$EBK_ACCEPTANCE_ARTIFACT_ID"
|
|
1326
|
+
"$EBK_ACCEPTANCE_ARTIFACT_DIGEST"
|
|
1327
|
+
"$EBK_ACCEPTANCE_ARTIFACT_SIZE"
|
|
1328
|
+
- name: Verify exact merged-tree acceptance
|
|
1329
|
+
if: steps.resolve.outputs.reused == 'true' && steps.restore.outcome == 'success'
|
|
1330
|
+
id: verify
|
|
1331
|
+
continue-on-error: true
|
|
1332
|
+
env:
|
|
1333
|
+
EBK_ACCEPTANCE_BASE: \${{ steps.resolve.outputs.base_commit }}
|
|
1334
|
+
EBK_ACCEPTANCE_HEAD: \${{ steps.resolve.outputs.head_commit }}
|
|
1335
|
+
EBK_ACCEPTANCE_ROUTE: \${{ steps.resolve.outputs.route }}
|
|
1336
|
+
EBK_ACCEPTANCE_RUN_ATTEMPT: \${{ steps.resolve.outputs.run_attempt }}
|
|
1337
|
+
EBK_ACCEPTANCE_RUN_ID: \${{ steps.resolve.outputs.run_id }}
|
|
1338
|
+
run: >-
|
|
1339
|
+
node tools/ci/workspace-acceptance.mjs verify
|
|
1340
|
+
.ebk/evidence/reused-acceptance/manifest.json
|
|
1341
|
+
"$EBK_ACCEPTANCE_HEAD"
|
|
1342
|
+
"$EBK_ACCEPTANCE_BASE"
|
|
1343
|
+
"$EBK_ACCEPTANCE_ROUTE"
|
|
1344
|
+
"$EBK_ACCEPTANCE_RUN_ID"
|
|
1345
|
+
"$EBK_ACCEPTANCE_RUN_ATTEMPT"
|
|
1346
|
+
|
|
1085
1347
|
policy:
|
|
1086
1348
|
name: policy
|
|
1349
|
+
needs: acceptance
|
|
1350
|
+
if: always() && needs.acceptance.outputs.reused != 'true'
|
|
1087
1351
|
runs-on: ubuntu-latest
|
|
1088
1352
|
steps:
|
|
1089
1353
|
- uses: ${checkoutAction}
|
|
@@ -1095,31 +1359,118 @@ jobs:
|
|
|
1095
1359
|
with:
|
|
1096
1360
|
node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
|
|
1097
1361
|
cache: pnpm
|
|
1098
|
-
- run:
|
|
1362
|
+
- run: node tools/ci/install.mjs
|
|
1099
1363
|
- run: node tools/ebk-git-policy.mjs
|
|
1364
|
+
env:
|
|
1365
|
+
EBK_HEAD_REPOSITORY: \${{ github.event.pull_request.head.repo.full_name }}
|
|
1100
1366
|
|
|
1101
1367
|
verify:
|
|
1102
1368
|
name: verify
|
|
1369
|
+
needs: acceptance
|
|
1370
|
+
if: always() && needs.acceptance.outputs.reused != 'true'
|
|
1103
1371
|
runs-on: ubuntu-latest
|
|
1104
1372
|
steps:
|
|
1105
1373
|
- uses: ${checkoutAction}
|
|
1374
|
+
with:
|
|
1375
|
+
fetch-depth: 0
|
|
1376
|
+
ref: \${{ github.event.pull_request.head.sha || github.sha }}
|
|
1106
1377
|
- uses: ${pnpmAction}
|
|
1107
1378
|
- uses: ${setupNodeAction}
|
|
1108
1379
|
with:
|
|
1109
1380
|
node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
|
|
1110
1381
|
cache: pnpm
|
|
1111
|
-
- run:
|
|
1112
|
-
|
|
1113
|
-
- run: pnpm
|
|
1382
|
+
- run: node tools/ci/install.mjs
|
|
1383
|
+
${nxCacheSteps}
|
|
1384
|
+
- run: pnpm format:check
|
|
1385
|
+
- name: Resolve the affected base and head for proposed work
|
|
1386
|
+
if: github.event_name == 'pull_request' && !(${protectedDevelopPromotion} || ${protectedMainSync})
|
|
1387
|
+
uses: ${nxSetShasAction}
|
|
1388
|
+
- name: Verify proposed workspace
|
|
1389
|
+
if: github.event_name == 'pull_request' && !(${protectedDevelopPromotion} || ${protectedMainSync})
|
|
1390
|
+
run: |
|
|
1391
|
+
pnpm nx run ${name}:verify-proposed
|
|
1392
|
+
pnpm nx affected -t verify --exclude=${name} --parallel=3
|
|
1393
|
+
- name: Verify protected develop promotion
|
|
1394
|
+
if: github.event_name == 'pull_request' && ${protectedDevelopPromotion}
|
|
1395
|
+
run: pnpm nx run-many -t verify --parallel=3
|
|
1396
|
+
- name: Verify protected main sync
|
|
1397
|
+
if: github.event_name == 'pull_request' && ${protectedMainSync}
|
|
1398
|
+
run: |
|
|
1399
|
+
pnpm nx run ${name}:blueprint-trust-check-main
|
|
1400
|
+
pnpm nx run ${name}:verify-proposed
|
|
1401
|
+
pnpm nx run-many -t verify --exclude=${name} --parallel=3
|
|
1402
|
+
- name: Verify adopted workspace
|
|
1403
|
+
if: github.event_name != 'pull_request'
|
|
1404
|
+
run: pnpm nx run-many -t verify --parallel=3
|
|
1405
|
+
|
|
1406
|
+
seal-acceptance:
|
|
1407
|
+
name: seal protected route acceptance
|
|
1408
|
+
needs: [policy, verify]
|
|
1409
|
+
if: >-
|
|
1410
|
+
always() &&
|
|
1411
|
+
needs.policy.result == 'success' &&
|
|
1412
|
+
needs.verify.result == 'success' &&
|
|
1413
|
+
github.event_name == 'pull_request' &&
|
|
1414
|
+
(${protectedDevelopPromotion} || ${protectedMainSync})
|
|
1415
|
+
runs-on: ubuntu-latest
|
|
1416
|
+
steps:
|
|
1417
|
+
- name: Check out immutable accepted candidate
|
|
1418
|
+
uses: ${checkoutAction}
|
|
1419
|
+
with:
|
|
1420
|
+
fetch-depth: 0
|
|
1421
|
+
ref: \${{ env.EBK_CI_CANDIDATE_SHA }}
|
|
1422
|
+
- name: Seal exact protected-route acceptance
|
|
1423
|
+
id: seal
|
|
1424
|
+
env:
|
|
1425
|
+
EBK_BASE_COMMIT: \${{ github.event.pull_request.base.sha }}
|
|
1426
|
+
EBK_HEAD_REPOSITORY: \${{ github.event.pull_request.head.repo.full_name }}
|
|
1427
|
+
run: >-
|
|
1428
|
+
node tools/ci/workspace-acceptance.mjs seal
|
|
1429
|
+
.ebk/evidence/ci/workspace-acceptance/manifest.json
|
|
1430
|
+
- name: Retain exact protected-route acceptance
|
|
1431
|
+
if: steps.seal.outputs.sealed == 'true'
|
|
1432
|
+
uses: ${uploadArtifactAction}
|
|
1433
|
+
with:
|
|
1434
|
+
name: workspace-acceptance-\${{ env.EBK_CI_CANDIDATE_SHA }}-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
1435
|
+
path: .ebk/evidence/ci/workspace-acceptance
|
|
1436
|
+
if-no-files-found: error
|
|
1437
|
+
include-hidden-files: true
|
|
1438
|
+
retention-days: 30
|
|
1439
|
+
|
|
1440
|
+
release-candidate:
|
|
1441
|
+
name: build immutable release candidates
|
|
1442
|
+
needs: [acceptance, policy, verify]
|
|
1443
|
+
if: >-
|
|
1444
|
+
always() &&
|
|
1445
|
+
github.event_name == 'push' &&
|
|
1446
|
+
needs.acceptance.outputs.history_only != 'true' &&
|
|
1447
|
+
(needs.acceptance.outputs.reused == 'true' ||
|
|
1448
|
+
(needs.policy.result == 'success' && needs.verify.result == 'success'))${releaseCandidateBranchCondition}
|
|
1449
|
+
runs-on: ubuntu-latest
|
|
1450
|
+
steps:
|
|
1451
|
+
- uses: ${checkoutAction}
|
|
1452
|
+
with:
|
|
1453
|
+
fetch-depth: 0
|
|
1454
|
+
ref: \${{ github.sha }}
|
|
1455
|
+
- uses: ${pnpmAction}
|
|
1456
|
+
- uses: ${setupNodeAction}
|
|
1457
|
+
with:
|
|
1458
|
+
node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
|
|
1459
|
+
cache: pnpm
|
|
1460
|
+
- run: node tools/ci/install.mjs
|
|
1461
|
+
${nxCacheSteps}
|
|
1114
1462
|
- name: Build immutable release candidates
|
|
1115
|
-
|
|
1463
|
+
id: candidates
|
|
1116
1464
|
env:
|
|
1117
1465
|
EBK_SOURCE_COMMIT: \${{ github.sha }}
|
|
1118
1466
|
run: |
|
|
1119
|
-
slice_projects="$(
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1467
|
+
slice_projects="$(node tools/ci/release-candidates.mjs)"
|
|
1468
|
+
if [ -z "$slice_projects" ]; then
|
|
1469
|
+
echo 'No application release producers; application artifacts are not applicable.'
|
|
1470
|
+
echo 'available=false' >> "$GITHUB_OUTPUT"
|
|
1471
|
+
exit 0
|
|
1472
|
+
fi
|
|
1473
|
+
pnpm nx run-many -t release-candidate --projects="$slice_projects" --parallel=3
|
|
1123
1474
|
mkdir -p .ebk/evidence/release-candidate/packages
|
|
1124
1475
|
IFS=',' read -r -a projects <<< "$slice_projects"
|
|
1125
1476
|
for project in "\${projects[@]}"; do
|
|
@@ -1129,8 +1480,9 @@ jobs:
|
|
|
1129
1480
|
cp packages/"$project"/.ebk/evidence/release-candidate/*.json \
|
|
1130
1481
|
".ebk/evidence/release-candidate/packages/$project/"
|
|
1131
1482
|
done
|
|
1483
|
+
echo 'available=true' >> "$GITHUB_OUTPUT"
|
|
1132
1484
|
- name: Retain exact immutable release candidate
|
|
1133
|
-
if:
|
|
1485
|
+
if: steps.candidates.outputs.available == 'true'
|
|
1134
1486
|
uses: ${uploadArtifactAction}
|
|
1135
1487
|
with:
|
|
1136
1488
|
name: release-candidate-\${{ github.sha }}
|
|
@@ -1138,6 +1490,7 @@ jobs:
|
|
|
1138
1490
|
include-hidden-files: true
|
|
1139
1491
|
retention-days: 30
|
|
1140
1492
|
path: .ebk/evidence/release-candidate
|
|
1493
|
+
${(0, workspace_files_thin_deploy_js_1.renderThinDeployVerifyCaller)(deploymentProfile, deliveryLane, productionApprovalMode)}
|
|
1141
1494
|
`,
|
|
1142
1495
|
'.github/workflows/deploy.yml': `name: Deploy
|
|
1143
1496
|
|
|
@@ -1165,7 +1518,97 @@ permissions:
|
|
|
1165
1518
|
contents: read
|
|
1166
1519
|
|
|
1167
1520
|
jobs:
|
|
1521
|
+
history-classification:
|
|
1522
|
+
name: classify protected history-only sync
|
|
1523
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/develop'
|
|
1524
|
+
runs-on: ubuntu-latest
|
|
1525
|
+
permissions:
|
|
1526
|
+
actions: read
|
|
1527
|
+
contents: read
|
|
1528
|
+
pull-requests: read
|
|
1529
|
+
outputs:
|
|
1530
|
+
history_only: \${{ steps.resolve.outputs.history_only == 'true' && steps.restore.outcome == 'success' && steps.verify.outcome == 'success' }}
|
|
1531
|
+
steps:
|
|
1532
|
+
- name: Check out immutable protected candidate
|
|
1533
|
+
uses: ${checkoutAction}
|
|
1534
|
+
with:
|
|
1535
|
+
fetch-depth: 0
|
|
1536
|
+
ref: \${{ github.sha }}
|
|
1537
|
+
- uses: ${setupNodeAction}
|
|
1538
|
+
with:
|
|
1539
|
+
node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
|
|
1540
|
+
- name: Resolve exact protected-route acceptance
|
|
1541
|
+
id: resolve
|
|
1542
|
+
env:
|
|
1543
|
+
GH_TOKEN: \${{ github.token }}
|
|
1544
|
+
run: node tools/ci/workspace-acceptance.mjs resolve
|
|
1545
|
+
- name: Restore exact protected-route acceptance
|
|
1546
|
+
if: steps.resolve.outputs.reused == 'true'
|
|
1547
|
+
id: restore
|
|
1548
|
+
continue-on-error: true
|
|
1549
|
+
env:
|
|
1550
|
+
EBK_ACCEPTANCE_ARTIFACT_DIGEST: \${{ steps.resolve.outputs.artifact_digest }}
|
|
1551
|
+
EBK_ACCEPTANCE_ARTIFACT_ID: \${{ steps.resolve.outputs.artifact_id }}
|
|
1552
|
+
EBK_ACCEPTANCE_ARTIFACT_SIZE: \${{ steps.resolve.outputs.artifact_size }}
|
|
1553
|
+
GH_TOKEN: \${{ github.token }}
|
|
1554
|
+
run: >-
|
|
1555
|
+
node tools/ci/workspace-acceptance.mjs download
|
|
1556
|
+
.ebk/evidence/reused-history-acceptance
|
|
1557
|
+
"$EBK_ACCEPTANCE_ARTIFACT_ID"
|
|
1558
|
+
"$EBK_ACCEPTANCE_ARTIFACT_DIGEST"
|
|
1559
|
+
"$EBK_ACCEPTANCE_ARTIFACT_SIZE"
|
|
1560
|
+
- name: Verify exact history-only acceptance
|
|
1561
|
+
if: steps.resolve.outputs.reused == 'true' && steps.restore.outcome == 'success'
|
|
1562
|
+
id: verify
|
|
1563
|
+
continue-on-error: true
|
|
1564
|
+
env:
|
|
1565
|
+
EBK_ACCEPTANCE_BASE: \${{ steps.resolve.outputs.base_commit }}
|
|
1566
|
+
EBK_ACCEPTANCE_HEAD: \${{ steps.resolve.outputs.head_commit }}
|
|
1567
|
+
EBK_ACCEPTANCE_ROUTE: \${{ steps.resolve.outputs.route }}
|
|
1568
|
+
EBK_ACCEPTANCE_RUN_ATTEMPT: \${{ steps.resolve.outputs.run_attempt }}
|
|
1569
|
+
EBK_ACCEPTANCE_RUN_ID: \${{ steps.resolve.outputs.run_id }}
|
|
1570
|
+
run: >-
|
|
1571
|
+
node tools/ci/workspace-acceptance.mjs verify
|
|
1572
|
+
.ebk/evidence/reused-history-acceptance/manifest.json
|
|
1573
|
+
"$EBK_ACCEPTANCE_HEAD"
|
|
1574
|
+
"$EBK_ACCEPTANCE_BASE"
|
|
1575
|
+
"$EBK_ACCEPTANCE_ROUTE"
|
|
1576
|
+
"$EBK_ACCEPTANCE_RUN_ID"
|
|
1577
|
+
"$EBK_ACCEPTANCE_RUN_ATTEMPT"
|
|
1578
|
+
|
|
1579
|
+
deployment-mode:
|
|
1580
|
+
name: classify deployment profile
|
|
1581
|
+
runs-on: ubuntu-latest
|
|
1582
|
+
outputs:
|
|
1583
|
+
profile: \${{ steps.profile.outputs.profile }}
|
|
1584
|
+
steps:
|
|
1585
|
+
- name: Check out immutable deployment policy
|
|
1586
|
+
uses: ${checkoutAction}
|
|
1587
|
+
with:
|
|
1588
|
+
ref: \${{ github.sha }}
|
|
1589
|
+
- id: profile
|
|
1590
|
+
name: Read exact committed deployment profile
|
|
1591
|
+
run: |
|
|
1592
|
+
node - <<'NODE'
|
|
1593
|
+
const { readFileSync } = require('node:fs');
|
|
1594
|
+
const policy = JSON.parse(readFileSync('ebk.repository-policy.json', 'utf8'));
|
|
1595
|
+
const profiles = new Set(['starter', 'production-isolated', 'fully-isolated']);
|
|
1596
|
+
if (!profiles.has(policy.deploymentProfile)) {
|
|
1597
|
+
throw new Error('Committed deployment profile is absent or invalid.');
|
|
1598
|
+
}
|
|
1599
|
+
require('node:fs').appendFileSync(
|
|
1600
|
+
process.env.GITHUB_OUTPUT,
|
|
1601
|
+
'profile=' + policy.deploymentProfile + '\\n',
|
|
1602
|
+
);
|
|
1603
|
+
NODE
|
|
1604
|
+
|
|
1168
1605
|
route:
|
|
1606
|
+
name: ${protectedDeploymentRouteJobName}
|
|
1607
|
+
needs: [history-classification, deployment-mode]
|
|
1608
|
+
if: >-
|
|
1609
|
+
always() &&
|
|
1610
|
+
needs.history-classification.outputs.history_only != 'true' &&
|
|
1611
|
+
needs.deployment-mode.result == 'success'
|
|
1169
1612
|
runs-on: ubuntu-latest
|
|
1170
1613
|
steps:
|
|
1171
1614
|
- name: Validate protected ref
|
|
@@ -1178,14 +1621,25 @@ jobs:
|
|
|
1178
1621
|
esac
|
|
1179
1622
|
|
|
1180
1623
|
plan-staging:
|
|
1181
|
-
needs: route
|
|
1182
|
-
if:
|
|
1624
|
+
needs: [route, deployment-mode]
|
|
1625
|
+
if: >-
|
|
1626
|
+
always() &&
|
|
1627
|
+
needs.route.result == 'success' &&
|
|
1628
|
+
needs.deployment-mode.result == 'success' &&
|
|
1629
|
+
github.ref == 'refs/heads/develop' &&
|
|
1630
|
+
(github.event_name == 'workflow_dispatch' ||
|
|
1631
|
+
needs.deployment-mode.outputs.profile != 'starter')
|
|
1183
1632
|
runs-on: ubuntu-latest
|
|
1184
1633
|
outputs:
|
|
1634
|
+
deployment_profile: \${{ steps.plan.outputs.deployment_profile }}
|
|
1635
|
+
deployment_region: \${{ steps.plan.outputs.deployment_region }}
|
|
1636
|
+
development_account_id: \${{ steps.plan.outputs.development_account_id }}
|
|
1185
1637
|
foundation_project: \${{ steps.plan.outputs.foundation_project }}
|
|
1186
1638
|
has_slices: \${{ steps.plan.outputs.has_slices }}
|
|
1187
1639
|
ordered_projects: \${{ steps.plan.outputs.ordered_projects }}
|
|
1640
|
+
production_account_id: \${{ steps.plan.outputs.production_account_id }}
|
|
1188
1641
|
slice_projects: \${{ steps.plan.outputs.slice_projects }}
|
|
1642
|
+
staging_account_id: \${{ steps.plan.outputs.staging_account_id }}
|
|
1189
1643
|
steps:
|
|
1190
1644
|
- uses: ${checkoutAction}
|
|
1191
1645
|
with:
|
|
@@ -1208,7 +1662,7 @@ jobs:
|
|
|
1208
1662
|
release-source-staging:
|
|
1209
1663
|
name: select and seal staging release source
|
|
1210
1664
|
needs: plan-staging
|
|
1211
|
-
if: \${{ needs.plan-staging.result == 'success' && (github.event_name == 'push' && 'deploy' || inputs.action) != 'unlock' }}
|
|
1665
|
+
if: \${{ always() && needs.plan-staging.result == 'success' && (github.event_name == 'push' && 'deploy' || inputs.action) != 'unlock' }}
|
|
1212
1666
|
runs-on: ubuntu-latest
|
|
1213
1667
|
permissions:
|
|
1214
1668
|
actions: read
|
|
@@ -1396,16 +1850,25 @@ jobs:
|
|
|
1396
1850
|
path: .ebk/evidence/staging-release-source/**
|
|
1397
1851
|
|
|
1398
1852
|
plan-production:
|
|
1399
|
-
needs: route
|
|
1400
|
-
if:
|
|
1853
|
+
needs: [route, deployment-mode]
|
|
1854
|
+
if: >-
|
|
1855
|
+
always() &&
|
|
1856
|
+
needs.route.result == 'success' &&
|
|
1857
|
+
needs.deployment-mode.result == 'success' &&
|
|
1858
|
+
github.ref == 'refs/heads/main'
|
|
1401
1859
|
runs-on: ubuntu-latest
|
|
1402
1860
|
outputs:
|
|
1861
|
+
deployment_profile: \${{ steps.plan.outputs.deployment_profile }}
|
|
1862
|
+
deployment_region: \${{ steps.plan.outputs.deployment_region }}
|
|
1863
|
+
development_account_id: \${{ steps.plan.outputs.development_account_id }}
|
|
1403
1864
|
foundation_project: \${{ steps.plan.outputs.foundation_project }}
|
|
1404
1865
|
foundation_profile: \${{ steps.plan.outputs.foundation_profile }}
|
|
1405
1866
|
has_slices: \${{ steps.plan.outputs.has_slices }}
|
|
1406
1867
|
ordered_projects: \${{ steps.plan.outputs.ordered_projects }}
|
|
1407
1868
|
production_approval_mode: \${{ steps.plan.outputs.production_approval_mode }}
|
|
1869
|
+
production_account_id: \${{ steps.plan.outputs.production_account_id }}
|
|
1408
1870
|
slice_projects: \${{ steps.plan.outputs.slice_projects }}
|
|
1871
|
+
staging_account_id: \${{ steps.plan.outputs.staging_account_id }}
|
|
1409
1872
|
steps:
|
|
1410
1873
|
- uses: ${checkoutAction}
|
|
1411
1874
|
with:
|
|
@@ -1699,7 +2162,7 @@ jobs:
|
|
|
1699
2162
|
container-platform-staging:
|
|
1700
2163
|
name: native staging OCI \${{ matrix.platform }}
|
|
1701
2164
|
needs: [plan-staging, release-source-staging]
|
|
1702
|
-
if: \${{ (github.event_name == 'push' && 'deploy' || inputs.action) == 'deploy' && needs.plan-staging.outputs.
|
|
2165
|
+
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
2166
|
strategy:
|
|
1704
2167
|
fail-fast: false
|
|
1705
2168
|
matrix:
|
|
@@ -1726,6 +2189,8 @@ jobs:
|
|
|
1726
2189
|
EBK_RELEASE_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
|
|
1727
2190
|
EBK_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
|
|
1728
2191
|
EBK_STAGE: staging
|
|
2192
|
+
EBK_POLICY_ACCOUNT_ID: \${{ needs.plan-staging.outputs.staging_account_id }}
|
|
2193
|
+
EBK_POLICY_REGION: \${{ needs.plan-staging.outputs.deployment_region }}
|
|
1729
2194
|
SLICE_PROJECTS: \${{ needs.plan-staging.outputs.slice_projects }}
|
|
1730
2195
|
steps:
|
|
1731
2196
|
- uses: ${checkoutAction}
|
|
@@ -1741,6 +2206,10 @@ jobs:
|
|
|
1741
2206
|
- run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile
|
|
1742
2207
|
- name: Verify managed workspace integrity before AWS authority
|
|
1743
2208
|
run: pnpm nx run ${name}:workspace-health
|
|
2209
|
+
- name: Bind staging account to committed deployment topology
|
|
2210
|
+
run: |
|
|
2211
|
+
test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_ACCOUNT_ID"
|
|
2212
|
+
test "$AWS_REGION" = "$EBK_POLICY_REGION"
|
|
1744
2213
|
${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - uses: ${configureAwsAction}
|
|
1745
2214
|
with:
|
|
1746
2215
|
role-to-assume: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
|
|
@@ -1780,6 +2249,8 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - uses: ${c
|
|
|
1780
2249
|
EBK_RELEASE_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
|
|
1781
2250
|
EBK_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
|
|
1782
2251
|
EBK_STAGE: staging
|
|
2252
|
+
EBK_POLICY_ACCOUNT_ID: \${{ needs.plan-staging.outputs.staging_account_id }}
|
|
2253
|
+
EBK_POLICY_REGION: \${{ needs.plan-staging.outputs.deployment_region }}
|
|
1783
2254
|
SLICE_PROJECTS: \${{ needs.plan-staging.outputs.slice_projects }}
|
|
1784
2255
|
steps:
|
|
1785
2256
|
- uses: ${checkoutAction}
|
|
@@ -1795,6 +2266,10 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - uses: ${c
|
|
|
1795
2266
|
- run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile
|
|
1796
2267
|
- name: Verify managed workspace integrity before AWS authority
|
|
1797
2268
|
run: pnpm nx run ${name}:workspace-health
|
|
2269
|
+
- name: Bind staging account to committed deployment topology
|
|
2270
|
+
run: |
|
|
2271
|
+
test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_ACCOUNT_ID"
|
|
2272
|
+
test "$AWS_REGION" = "$EBK_POLICY_REGION"
|
|
1798
2273
|
${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Restore both native platform records
|
|
1799
2274
|
uses: ${downloadArtifactAction}
|
|
1800
2275
|
with:
|
|
@@ -1825,7 +2300,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
1825
2300
|
container-promote-production:
|
|
1826
2301
|
name: promote reviewed OCI indexes to production
|
|
1827
2302
|
needs: [plan-production, release-source-production]
|
|
1828
|
-
if: \${{ always() && needs.plan-production.result == 'success' && needs.release-source-production.result == 'success' && needs.plan-production.outputs.
|
|
2303
|
+
if: \${{ always() && needs.plan-production.result == 'success' && needs.release-source-production.result == 'success' && needs.plan-production.outputs.slice_projects != '' }}
|
|
1829
2304
|
runs-on: ubuntu-latest
|
|
1830
2305
|
environment: production
|
|
1831
2306
|
permissions:
|
|
@@ -1836,6 +2311,10 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
1836
2311
|
AWS_DEPLOY_ROLE_ARN: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
|
|
1837
2312
|
AWS_EXPECTED_ACCOUNT_ID: \${{ vars.AWS_EXPECTED_ACCOUNT_ID }}
|
|
1838
2313
|
AWS_REGION: \${{ vars.AWS_REGION }}
|
|
2314
|
+
EBK_DEPLOYMENT_PROFILE: \${{ needs.plan-production.outputs.deployment_profile }}
|
|
2315
|
+
EBK_POLICY_PRODUCTION_ACCOUNT_ID: \${{ needs.plan-production.outputs.production_account_id }}
|
|
2316
|
+
EBK_POLICY_REGION: \${{ needs.plan-production.outputs.deployment_region }}
|
|
2317
|
+
EBK_POLICY_STAGING_ACCOUNT_ID: \${{ needs.plan-production.outputs.staging_account_id }}
|
|
1839
2318
|
EBK_CANDIDATE_RUN_ATTEMPT: \${{ needs.release-source-production.outputs.candidate_run_attempt }}
|
|
1840
2319
|
EBK_CANDIDATE_RUN_ID: \${{ needs.release-source-production.outputs.candidate_run_id }}
|
|
1841
2320
|
EBK_RELEASE_SOURCE_COMMIT: \${{ needs.release-source-production.outputs.source_commit }}
|
|
@@ -1936,11 +2415,13 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
1936
2415
|
cp "$source_sidecar" "$target"
|
|
1937
2416
|
done
|
|
1938
2417
|
- uses: ${configureAwsAction}
|
|
2418
|
+
if: env.EBK_DEPLOYMENT_PROFILE != 'starter'
|
|
1939
2419
|
with:
|
|
1940
2420
|
role-to-assume: \${{ secrets.AWS_STAGING_PROMOTION_PULL_ROLE_ARN }}
|
|
1941
2421
|
role-session-name: ebk-staging-container-pull-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
1942
2422
|
aws-region: \${{ vars.AWS_STAGING_PROMOTION_REGION }}
|
|
1943
2423
|
- name: Authenticate exact staging source registry
|
|
2424
|
+
if: env.EBK_DEPLOYMENT_PROFILE != 'starter'
|
|
1944
2425
|
env:
|
|
1945
2426
|
EBK_STAGING_ACCOUNT_ID: \${{ vars.AWS_STAGING_PROMOTION_EXPECTED_ACCOUNT_ID }}
|
|
1946
2427
|
EBK_STAGING_REGION: \${{ vars.AWS_STAGING_PROMOTION_REGION }}
|
|
@@ -1959,10 +2440,27 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
1959
2440
|
role-to-assume: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
|
|
1960
2441
|
role-session-name: ebk-production-container-push-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
1961
2442
|
aws-region: \${{ vars.AWS_REGION }}
|
|
2443
|
+
- name: Bind production authority to committed deployment topology
|
|
2444
|
+
run: |
|
|
2445
|
+
test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_PRODUCTION_ACCOUNT_ID" ||
|
|
2446
|
+
{ echo 'Production environment account differs from committed deployment topology'; exit 1; }
|
|
2447
|
+
test "$AWS_REGION" = "$EBK_POLICY_REGION" ||
|
|
2448
|
+
{ echo 'Production environment Region differs from committed deployment topology'; exit 1; }
|
|
2449
|
+
- name: Authenticate shared-account staging source registry
|
|
2450
|
+
if: env.EBK_DEPLOYMENT_PROFILE == 'starter'
|
|
2451
|
+
run: |
|
|
2452
|
+
test "$EBK_POLICY_STAGING_ACCOUNT_ID" = "$EBK_POLICY_PRODUCTION_ACCOUNT_ID" ||
|
|
2453
|
+
{ echo 'Starter promotion requires one shared AWS account'; exit 1; }
|
|
2454
|
+
registry_suffix=.com
|
|
2455
|
+
case "$EBK_POLICY_REGION" in cn-*) registry_suffix=.com.cn ;; esac
|
|
2456
|
+
aws ecr get-login-password --region "$EBK_POLICY_REGION" |
|
|
2457
|
+
docker login --username AWS --password-stdin \
|
|
2458
|
+
"$EBK_POLICY_STAGING_ACCOUNT_ID.dkr.ecr.$EBK_POLICY_REGION.amazonaws$registry_suffix"
|
|
1962
2459
|
- name: Promote exact staging indexes without rebuilding
|
|
1963
2460
|
env:
|
|
1964
|
-
|
|
1965
|
-
|
|
2461
|
+
EBK_DEPLOYMENT_PROFILE: \${{ needs.plan-production.outputs.deployment_profile }}
|
|
2462
|
+
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 }}
|
|
2463
|
+
EBK_STAGING_PROMOTION_REGION: \${{ needs.plan-production.outputs.deployment_profile == 'starter' && needs.plan-production.outputs.deployment_region || vars.AWS_STAGING_PROMOTION_REGION }}
|
|
1966
2464
|
run: |
|
|
1967
2465
|
test "$(aws sts get-caller-identity --query Account --output text)" = "$AWS_EXPECTED_ACCOUNT_ID" ||
|
|
1968
2466
|
{ echo 'Production promotion role account differs from policy'; exit 1; }
|
|
@@ -1995,7 +2493,7 @@ ${planRoleAuditJob('production', configureAwsAction)}${planRoleAuditJob('staging
|
|
|
1995
2493
|
(
|
|
1996
2494
|
needs.release-source-production.result == 'success' &&
|
|
1997
2495
|
(
|
|
1998
|
-
needs.plan-production.outputs.
|
|
2496
|
+
needs.plan-production.outputs.slice_projects == '' ||
|
|
1999
2497
|
needs.container-promote-production.result == 'success'
|
|
2000
2498
|
)
|
|
2001
2499
|
)
|
|
@@ -2089,7 +2587,7 @@ ${infrastructureCandidateDownloadSteps(downloadArtifactAction, {
|
|
|
2089
2587
|
runId: '${{ needs.release-source-production.outputs.candidate_run_id }}',
|
|
2090
2588
|
verifiedStagingRunId: '${{ needs.release-source-production.outputs.verified_staging_run_id }}',
|
|
2091
2589
|
})} - name: Restore exact production container bindings
|
|
2092
|
-
if: env.EBK_ACTION == 'deploy' && needs.plan-production.outputs.
|
|
2590
|
+
if: env.EBK_ACTION == 'deploy' && needs.plan-production.outputs.slice_projects != ''
|
|
2093
2591
|
env:
|
|
2094
2592
|
ACTIONS_ID_TOKEN_REQUEST_TOKEN: ''
|
|
2095
2593
|
ACTIONS_ID_TOKEN_REQUEST_URL: ''
|
|
@@ -2150,7 +2648,7 @@ ${planRoleAuditStep('main', 'verify')}
|
|
|
2150
2648
|
(github.event_name == 'push' && 'deploy' || inputs.action) != 'unlock' &&
|
|
2151
2649
|
(
|
|
2152
2650
|
(github.event_name == 'push' && 'deploy' || inputs.action) != 'deploy' ||
|
|
2153
|
-
needs.plan-staging.outputs.
|
|
2651
|
+
needs.plan-staging.outputs.slice_projects == '' ||
|
|
2154
2652
|
needs.container-assemble-staging.result == 'success'
|
|
2155
2653
|
)
|
|
2156
2654
|
runs-on: ubuntu-latest
|
|
@@ -2181,6 +2679,8 @@ ${planRoleAuditStep('main', 'verify')}
|
|
|
2181
2679
|
EBK_RELEASE_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
|
|
2182
2680
|
EBK_SOURCE_COMMIT: \${{ github.sha }}
|
|
2183
2681
|
EBK_STAGE: staging
|
|
2682
|
+
EBK_POLICY_ACCOUNT_ID: \${{ needs.plan-staging.outputs.staging_account_id }}
|
|
2683
|
+
EBK_POLICY_REGION: \${{ needs.plan-staging.outputs.deployment_region }}
|
|
2184
2684
|
FOUNDATION_PROJECT: \${{ needs.plan-staging.outputs.foundation_project }}
|
|
2185
2685
|
ORDERED_PROJECTS: \${{ needs.plan-staging.outputs.ordered_projects }}
|
|
2186
2686
|
SLICE_PROJECTS: \${{ needs.plan-staging.outputs.slice_projects }}
|
|
@@ -2238,7 +2738,7 @@ ${planApplicationBindingCheck()} case "$AWS_PLAN_ROLE_ARN" in
|
|
|
2238
2738
|
ACTIONS_ID_TOKEN_REQUEST_URL: ''
|
|
2239
2739
|
run: pnpm nx run ${name}:workspace-health
|
|
2240
2740
|
${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Restore exact staging container bindings
|
|
2241
|
-
if: env.EBK_ACTION == 'deploy' && needs.plan-staging.outputs.
|
|
2741
|
+
if: env.EBK_ACTION == 'deploy' && needs.plan-staging.outputs.slice_projects != ''
|
|
2242
2742
|
env:
|
|
2243
2743
|
ACTIONS_ID_TOKEN_REQUEST_TOKEN: ''
|
|
2244
2744
|
ACTIONS_ID_TOKEN_REQUEST_URL: ''
|
|
@@ -2288,14 +2788,14 @@ ${planRoleAuditStep('develop', 'verify')}
|
|
|
2288
2788
|
packages/*/.ebk/evidence/infrastructure-plan-command.json
|
|
2289
2789
|
|
|
2290
2790
|
deploy-staging:
|
|
2291
|
-
name:
|
|
2791
|
+
name: ${stagingApplyJobPrefix}\${{ needs.infrastructure-plan-staging.outputs.plan_digest || 'recovery-without-resource-plan' }}
|
|
2292
2792
|
needs: [plan-staging, release-source-staging, container-assemble-staging, infrastructure-plan-staging]
|
|
2293
2793
|
if: |
|
|
2294
2794
|
always() &&
|
|
2295
2795
|
needs.plan-staging.result == 'success' &&
|
|
2296
2796
|
(
|
|
2297
2797
|
(github.event_name == 'push' && 'deploy' || inputs.action) != 'deploy' ||
|
|
2298
|
-
needs.plan-staging.outputs.
|
|
2798
|
+
needs.plan-staging.outputs.slice_projects == '' ||
|
|
2299
2799
|
needs.container-assemble-staging.result == 'success'
|
|
2300
2800
|
) &&
|
|
2301
2801
|
(
|
|
@@ -2357,7 +2857,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
2357
2857
|
name: infrastructure-plan-staging-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
2358
2858
|
path: .
|
|
2359
2859
|
- name: Restore exact staging container bindings
|
|
2360
|
-
if: env.EBK_ACTION == 'deploy' && needs.plan-staging.outputs.
|
|
2860
|
+
if: env.EBK_ACTION == 'deploy' && needs.plan-staging.outputs.slice_projects != ''
|
|
2361
2861
|
uses: ${downloadArtifactAction}
|
|
2362
2862
|
with:
|
|
2363
2863
|
name: container-release-staging-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
@@ -2367,6 +2867,10 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
2367
2867
|
test -n "$AWS_DEPLOY_ROLE_ARN" || { echo 'staging AWS_DEPLOY_ROLE_ARN is required'; exit 1; }
|
|
2368
2868
|
test -n "$AWS_EXPECTED_ACCOUNT_ID" || { echo 'staging AWS_EXPECTED_ACCOUNT_ID is required'; exit 1; }
|
|
2369
2869
|
test -n "$AWS_REGION" || { echo 'staging AWS_REGION is required'; exit 1; }
|
|
2870
|
+
test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_ACCOUNT_ID" ||
|
|
2871
|
+
{ echo 'staging environment account differs from committed deployment topology'; exit 1; }
|
|
2872
|
+
test "$AWS_REGION" = "$EBK_POLICY_REGION" ||
|
|
2873
|
+
{ echo 'staging environment Region differs from committed deployment topology'; exit 1; }
|
|
2370
2874
|
test -n "$EBK_FLEET_ORIGIN" ||
|
|
2371
2875
|
{ echo 'staging environment EBK_FLEET_ORIGIN is required'; exit 1; }
|
|
2372
2876
|
test "$EBK_FLEET_ORIGIN" = "$EBK_PLANNED_FLEET_ORIGIN" ||
|
|
@@ -2378,8 +2882,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
2378
2882
|
test -n "$AWS_PLAN_GENERATED_SECRET_ARNS_JSON" &&
|
|
2379
2883
|
test -n "$AWS_PLAN_PROVIDER_REFRESH_POLICY_JSON" ||
|
|
2380
2884
|
{ 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" ||
|
|
2885
|
+
test -n "$AWS_PLAN_ROLE_ARN" ||
|
|
2383
2886
|
{ echo 'staging exact plan-authority inputs are required'; exit 1; }
|
|
2384
2887
|
case "$AWS_DEPLOY_ROLE_ARN" in
|
|
2385
2888
|
arn:aws:iam::$AWS_EXPECTED_ACCOUNT_ID:role/*) ;;
|
|
@@ -2946,7 +3449,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
2946
3449
|
needs.plan-production.result == 'success' &&
|
|
2947
3450
|
(
|
|
2948
3451
|
(github.event_name == 'push' && 'deploy' || inputs.action) != 'deploy' ||
|
|
2949
|
-
needs.plan-production.outputs.
|
|
3452
|
+
needs.plan-production.outputs.slice_projects == '' ||
|
|
2950
3453
|
needs.container-promote-production.result == 'success'
|
|
2951
3454
|
) &&
|
|
2952
3455
|
(
|
|
@@ -3003,6 +3506,8 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
3003
3506
|
EBK_APPROVED_MAIN_COMMIT: \${{ github.sha }}
|
|
3004
3507
|
EBK_SOURCE_COMMIT: \${{ github.sha }}
|
|
3005
3508
|
EBK_STAGE: production
|
|
3509
|
+
EBK_POLICY_ACCOUNT_ID: \${{ needs.plan-production.outputs.production_account_id }}
|
|
3510
|
+
EBK_POLICY_REGION: \${{ needs.plan-production.outputs.deployment_region }}
|
|
3006
3511
|
EBK_VERIFIED_STAGING_RUN_ID: \${{ needs.release-source-production.outputs.verified_staging_run_id }}
|
|
3007
3512
|
FOUNDATION_PROJECT: \${{ needs.plan-production.outputs.foundation_project }}
|
|
3008
3513
|
ORDERED_PROJECTS: \${{ needs.plan-production.outputs.ordered_projects }}
|
|
@@ -3089,7 +3594,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
3089
3594
|
name: infrastructure-plan-production-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
3090
3595
|
path: .
|
|
3091
3596
|
- name: Restore exact production container bindings
|
|
3092
|
-
if: env.EBK_ACTION == 'deploy' && needs.plan-production.outputs.
|
|
3597
|
+
if: env.EBK_ACTION == 'deploy' && needs.plan-production.outputs.slice_projects != ''
|
|
3093
3598
|
uses: ${downloadArtifactAction}
|
|
3094
3599
|
with:
|
|
3095
3600
|
name: container-release-production-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
@@ -3099,6 +3604,10 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
3099
3604
|
test -n "$AWS_DEPLOY_ROLE_ARN" || { echo 'production AWS_DEPLOY_ROLE_ARN is required'; exit 1; }
|
|
3100
3605
|
test -n "$AWS_EXPECTED_ACCOUNT_ID" || { echo 'production AWS_EXPECTED_ACCOUNT_ID is required'; exit 1; }
|
|
3101
3606
|
test -n "$AWS_REGION" || { echo 'production AWS_REGION is required'; exit 1; }
|
|
3607
|
+
test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_ACCOUNT_ID" ||
|
|
3608
|
+
{ echo 'production environment account differs from committed deployment topology'; exit 1; }
|
|
3609
|
+
test "$AWS_REGION" = "$EBK_POLICY_REGION" ||
|
|
3610
|
+
{ echo 'production environment Region differs from committed deployment topology'; exit 1; }
|
|
3102
3611
|
test -n "$EBK_FLEET_ORIGIN" ||
|
|
3103
3612
|
{ echo 'production environment EBK_FLEET_ORIGIN is required'; exit 1; }
|
|
3104
3613
|
test "$EBK_FLEET_ORIGIN" = "$EBK_PLANNED_FLEET_ORIGIN" ||
|
|
@@ -3110,8 +3619,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
|
|
|
3110
3619
|
test -n "$AWS_PLAN_GENERATED_SECRET_ARNS_JSON" &&
|
|
3111
3620
|
test -n "$AWS_PLAN_PROVIDER_REFRESH_POLICY_JSON" ||
|
|
3112
3621
|
{ 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" ||
|
|
3622
|
+
test -n "$AWS_PLAN_ROLE_ARN" ||
|
|
3115
3623
|
{ echo 'production exact plan-authority inputs are required'; exit 1; }
|
|
3116
3624
|
case "$AWS_DEPLOY_ROLE_ARN" in
|
|
3117
3625
|
arn:aws:iam::$AWS_EXPECTED_ACCOUNT_ID:role/*) ;;
|
|
@@ -3492,7 +4000,7 @@ on:
|
|
|
3492
4000
|
description: Exact protected deployment stage
|
|
3493
4001
|
required: true
|
|
3494
4002
|
type: choice
|
|
3495
|
-
options: [
|
|
4003
|
+
options: [${ecrInspectorWorkflowStageChoices}]
|
|
3496
4004
|
deployment_commit:
|
|
3497
4005
|
description: Exact successful protected deployment commit
|
|
3498
4006
|
required: true
|
|
@@ -3501,8 +4009,8 @@ on:
|
|
|
3501
4009
|
description: Exact successful Deploy workflow run ID
|
|
3502
4010
|
required: true
|
|
3503
4011
|
type: string
|
|
3504
|
-
|
|
3505
|
-
description: Exact
|
|
4012
|
+
deployment_receipt_artifact_name:
|
|
4013
|
+
description: Exact deployment-receipt artifact from that run
|
|
3506
4014
|
required: true
|
|
3507
4015
|
type: string
|
|
3508
4016
|
slice:
|
|
@@ -3556,7 +4064,7 @@ jobs:
|
|
|
3556
4064
|
EBK_DEPLOYMENT_ARN: \${{ inputs.deployment_arn }}
|
|
3557
4065
|
EBK_DEPLOYMENT_KIND: \${{ inputs.deployment_kind }}
|
|
3558
4066
|
EBK_DEPLOY_RUN_ID: \${{ inputs.deploy_run_id }}
|
|
3559
|
-
|
|
4067
|
+
EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME: \${{ inputs.deployment_receipt_artifact_name }}
|
|
3560
4068
|
EBK_SLICE: \${{ inputs.slice }}
|
|
3561
4069
|
EBK_DEPLOYMENT_COMMIT: \${{ inputs.deployment_commit }}
|
|
3562
4070
|
EBK_STAGE: \${{ inputs.stage }}
|
|
@@ -3579,10 +4087,10 @@ jobs:
|
|
|
3579
4087
|
esac
|
|
3580
4088
|
expected_branch=develop
|
|
3581
4089
|
if [ "$EBK_STAGE" = production ]; then expected_branch=main; fi
|
|
3582
|
-
expected_artifact="
|
|
3583
|
-
case "$
|
|
3584
|
-
"$expected_artifact"*) attempt="\${
|
|
3585
|
-
*) echo '
|
|
4090
|
+
expected_artifact="deployment-receipt-$EBK_STAGE-$EBK_DEPLOY_RUN_ID-"
|
|
4091
|
+
case "$EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME" in
|
|
4092
|
+
"$expected_artifact"*) attempt="\${EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME#"$expected_artifact"}" ;;
|
|
4093
|
+
*) echo 'Deployment receipt artifact does not bind the exact deploy run'; exit 1 ;;
|
|
3586
4094
|
esac
|
|
3587
4095
|
case "$attempt" in
|
|
3588
4096
|
''|0*|*[!0-9]*) echo 'Release evidence artifact has an invalid run attempt'; exit 1 ;;
|
|
@@ -3595,7 +4103,7 @@ jobs:
|
|
|
3595
4103
|
test "$(printf '%s' "$run" | jq -r .path)" = ".github/workflows/deploy.yml" ||
|
|
3596
4104
|
{ echo 'Deploy run is not the exact successful protected source authority'; exit 1; }
|
|
3597
4105
|
artifacts="$(gh api "/repos/$GH_REPO/actions/runs/$EBK_DEPLOY_RUN_ID/artifacts?per_page=100")"
|
|
3598
|
-
count="$(printf '%s' "$artifacts" | jq --arg name "$
|
|
4106
|
+
count="$(printf '%s' "$artifacts" | jq --arg name "$EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME" \
|
|
3599
4107
|
'[.artifacts[] | select(.name == $name and .expired == false)] | length')"
|
|
3600
4108
|
test "$count" = 1 ||
|
|
3601
4109
|
{ echo 'Exact unexpired release evidence artifact is absent or ambiguous'; exit 1; }
|
|
@@ -3612,7 +4120,7 @@ jobs:
|
|
|
3612
4120
|
- uses: ${downloadArtifactAction}
|
|
3613
4121
|
with:
|
|
3614
4122
|
github-token: \${{ github.token }}
|
|
3615
|
-
name: \${{ inputs.
|
|
4123
|
+
name: \${{ inputs.deployment_receipt_artifact_name }}
|
|
3616
4124
|
path: .
|
|
3617
4125
|
run-id: \${{ inputs.deploy_run_id }}
|
|
3618
4126
|
- uses: ${pnpmAction}
|
|
@@ -3719,7 +4227,7 @@ jobs:
|
|
|
3719
4227
|
{ encoding: "utf8" },
|
|
3720
4228
|
).trim()
|
|
3721
4229
|
) fail("Production deployment tree differs from its reviewed release source.");
|
|
3722
|
-
} else if (stage !== "
|
|
4230
|
+
} else if (stage !== "${rehearsalStage}") {
|
|
3723
4231
|
fail("Protected release stage is invalid.");
|
|
3724
4232
|
}
|
|
3725
4233
|
const productionKeys = [
|
|
@@ -3775,7 +4283,7 @@ jobs:
|
|
|
3775
4283
|
!digest.test(binding.candidateDigest ?? "") ||
|
|
3776
4284
|
(stage === "production" &&
|
|
3777
4285
|
!digest.test(binding.lifecycleDigest ?? "")) ||
|
|
3778
|
-
(stage === "
|
|
4286
|
+
(stage === "${rehearsalStage}" && binding.stage !== stage) ||
|
|
3779
4287
|
binding.runId !== process.env.EBK_DEPLOY_RUN_ID ||
|
|
3780
4288
|
binding.runAttempt !== process.env.EBK_DEPLOY_RUN_ATTEMPT ||
|
|
3781
4289
|
binding.workflow !== expectedWorkflow
|
|
@@ -3854,7 +4362,7 @@ on:
|
|
|
3854
4362
|
description: Protected deployed stage
|
|
3855
4363
|
required: true
|
|
3856
4364
|
type: choice
|
|
3857
|
-
options: [
|
|
4365
|
+
options: [${protectedWorkflowStageChoices}]
|
|
3858
4366
|
project:
|
|
3859
4367
|
description: Exact Product Slice project
|
|
3860
4368
|
required: true
|
|
@@ -3954,7 +4462,7 @@ jobs:
|
|
|
3954
4462
|
EBK_REPORT_RUN_ID: \${{ inputs.report_run_id }}
|
|
3955
4463
|
run: |
|
|
3956
4464
|
case "$EBK_STAGE:$EBK_PROTECTED_REF" in
|
|
3957
|
-
|
|
4465
|
+
${protectedWorkflowStageBranchCases}) ;;
|
|
3958
4466
|
*) echo 'Async recovery stage does not match its protected branch'; exit 1 ;;
|
|
3959
4467
|
esac
|
|
3960
4468
|
case "$EBK_PROJECT" in
|
|
@@ -4765,13 +5273,78 @@ permissions:
|
|
|
4765
5273
|
contents: read
|
|
4766
5274
|
|
|
4767
5275
|
jobs:
|
|
5276
|
+
deployment-classification:
|
|
5277
|
+
name: classify exact staging deployment
|
|
5278
|
+
if: >-
|
|
5279
|
+
github.event_name == 'workflow_run' &&
|
|
5280
|
+
github.event.workflow_run.event == 'push' &&
|
|
5281
|
+
github.event.workflow_run.head_branch == 'develop' &&
|
|
5282
|
+
github.event.workflow_run.conclusion == 'success'
|
|
5283
|
+
permissions:
|
|
5284
|
+
actions: read
|
|
5285
|
+
contents: read
|
|
5286
|
+
outputs:
|
|
5287
|
+
deployed: \${{ steps.classify.outputs.deployed }}
|
|
5288
|
+
runs-on: ubuntu-latest
|
|
5289
|
+
steps:
|
|
5290
|
+
- name: Check out immutable deployment policy
|
|
5291
|
+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
5292
|
+
with:
|
|
5293
|
+
persist-credentials: false
|
|
5294
|
+
ref: \${{ github.event.workflow_run.head_sha }}
|
|
5295
|
+
- id: profile
|
|
5296
|
+
name: Read exact committed deployment profile
|
|
5297
|
+
run: |
|
|
5298
|
+
node - <<'NODE'
|
|
5299
|
+
const { appendFileSync, readFileSync } = require('node:fs');
|
|
5300
|
+
const policy = JSON.parse(
|
|
5301
|
+
readFileSync('ebk.repository-policy.json', 'utf8'),
|
|
5302
|
+
);
|
|
5303
|
+
const profiles = new Set([
|
|
5304
|
+
'starter',
|
|
5305
|
+
'production-isolated',
|
|
5306
|
+
'fully-isolated',
|
|
5307
|
+
]);
|
|
5308
|
+
if (!profiles.has(policy.deploymentProfile)) {
|
|
5309
|
+
throw new Error('Committed deployment profile is absent or invalid.');
|
|
5310
|
+
}
|
|
5311
|
+
appendFileSync(
|
|
5312
|
+
process.env.GITHUB_OUTPUT,
|
|
5313
|
+
'profile=' + policy.deploymentProfile + '\\n',
|
|
5314
|
+
);
|
|
5315
|
+
NODE
|
|
5316
|
+
- id: classify
|
|
5317
|
+
name: Require one successful thin staging deployment job
|
|
5318
|
+
env:
|
|
5319
|
+
EBK_DEPLOYMENT_PROFILE: \${{ steps.profile.outputs.profile }}
|
|
5320
|
+
EBK_DEPLOYMENT_RUN_ID: \${{ github.event.workflow_run.id }}
|
|
5321
|
+
GH_TOKEN: \${{ github.token }}
|
|
5322
|
+
GH_REPO: \${{ github.repository }}
|
|
5323
|
+
run: |
|
|
5324
|
+
if [ "$EBK_DEPLOYMENT_PROFILE" = starter ]; then
|
|
5325
|
+
echo 'deployed=false' >> "$GITHUB_OUTPUT"
|
|
5326
|
+
exit 0
|
|
5327
|
+
fi
|
|
5328
|
+
jobs="$(gh api \
|
|
5329
|
+
"/repos/$GH_REPO/actions/runs/$EBK_DEPLOYMENT_RUN_ID/jobs?filter=latest&per_page=100")"
|
|
5330
|
+
execute_count="$(printf '%s' "$jobs" | jq \
|
|
5331
|
+
'[.jobs[] | select(.name == "native deploy staging" and .conclusion == "success")] | length')"
|
|
5332
|
+
[ "$execute_count" = 1 ] || {
|
|
5333
|
+
echo 'Deploy must contain exactly one successful thin staging execution'
|
|
5334
|
+
exit 1
|
|
5335
|
+
}
|
|
5336
|
+
echo 'deployed=true' >> "$GITHUB_OUTPUT"
|
|
5337
|
+
|
|
4768
5338
|
plan:
|
|
5339
|
+
needs: deployment-classification
|
|
4769
5340
|
if: >-
|
|
4770
|
-
(
|
|
5341
|
+
always() &&
|
|
5342
|
+
((github.event_name == 'workflow_run' &&
|
|
4771
5343
|
github.event.workflow_run.conclusion == 'success' &&
|
|
4772
5344
|
github.event.workflow_run.event == 'push' &&
|
|
4773
|
-
github.event.workflow_run.head_branch == 'develop'
|
|
4774
|
-
|
|
5345
|
+
github.event.workflow_run.head_branch == 'develop' &&
|
|
5346
|
+
needs.deployment-classification.outputs.deployed == 'true') ||
|
|
5347
|
+
github.event_name == 'workflow_dispatch')
|
|
4775
5348
|
outputs:
|
|
4776
5349
|
deployment_run_attempt: \${{ steps.identity.outputs.deployment_run_attempt }}
|
|
4777
5350
|
deployment_run_id: \${{ steps.identity.outputs.deployment_run_id }}
|
|
@@ -4878,9 +5451,10 @@ jobs:
|
|
|
4878
5451
|
EBK_SMOKE_AUTHORITY_VERSION: \${{ vars.EBK_SMOKE_AUTHORITY_VERSION }}
|
|
4879
5452
|
EBK_SMOKE_TARGETS: \${{ vars.EBK_SMOKE_TARGETS }}
|
|
4880
5453
|
EBK_SOURCE_COMMIT: \${{ needs.plan.outputs.source_commit }}
|
|
4881
|
-
PLAYWRIGHT_BROWSERS_PATH: \${{ runner.temp }}/ebk-playwright-browsers
|
|
4882
5454
|
runs-on: ubuntu-latest
|
|
4883
5455
|
steps:
|
|
5456
|
+
- name: Initialize private browser cache path
|
|
5457
|
+
run: echo "PLAYWRIGHT_BROWSERS_PATH=$RUNNER_TEMP/ebk-playwright-browsers" >> "$GITHUB_ENV"
|
|
4884
5458
|
- name: Validate credential-free preparation
|
|
4885
5459
|
run: |
|
|
4886
5460
|
test -n "$AWS_EXPECTED_ACCOUNT_ID" || { echo 'AWS_EXPECTED_ACCOUNT_ID is required'; exit 1; }
|
|
@@ -5264,14 +5838,14 @@ jobs:
|
|
|
5264
5838
|
test "$(node --version | cut -d. -f1)" = v22
|
|
5265
5839
|
- name: Run exact deployed smoke
|
|
5266
5840
|
working-directory: \${{ runner.temp }}/ebk-prepared-workspace
|
|
5267
|
-
run: node node_modules/nx/bin/nx.js run "$EBK_PROJECT:smoke-deployed"
|
|
5841
|
+
run: node node_modules/nx/dist/bin/nx.js run "$EBK_PROJECT:smoke-deployed"
|
|
5268
5842
|
- name: Verify reviewed frontend visual baseline
|
|
5269
5843
|
working-directory: \${{ runner.temp }}/ebk-prepared-workspace
|
|
5270
|
-
run: node node_modules/nx/bin/nx.js run "$EBK_PROJECT:frontend-visual"
|
|
5844
|
+
run: node node_modules/nx/dist/bin/nx.js run "$EBK_PROJECT:frontend-visual"
|
|
5271
5845
|
- name: Verify deployed frontend security headers
|
|
5272
5846
|
working-directory: \${{ runner.temp }}/ebk-prepared-workspace
|
|
5273
5847
|
run: |
|
|
5274
|
-
node node_modules/nx/bin/nx.js run \
|
|
5848
|
+
node node_modules/nx/dist/bin/nx.js run \
|
|
5275
5849
|
"$EBK_PROJECT:frontend-security-headers" -- \
|
|
5276
5850
|
--url="$EBK_SMOKE_URL"
|
|
5277
5851
|
- name: Bind exact alarm and data-integrity evidence
|
|
@@ -6642,7 +7216,7 @@ on:
|
|
|
6642
7216
|
description: Protected stage
|
|
6643
7217
|
required: true
|
|
6644
7218
|
type: choice
|
|
6645
|
-
options: [
|
|
7219
|
+
options: [${protectedWorkflowStageChoices}]
|
|
6646
7220
|
project:
|
|
6647
7221
|
description: Exact Product Slice project
|
|
6648
7222
|
required: true
|
|
@@ -6668,11 +7242,9 @@ jobs:
|
|
|
6668
7242
|
contents: read
|
|
6669
7243
|
id-token: write
|
|
6670
7244
|
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
7245
|
EBK_ACTION: \${{ inputs.action }}
|
|
6675
7246
|
EBK_PROJECT: \${{ inputs.project }}
|
|
7247
|
+
EBK_RENDERED_PROFILE: ${deploymentProfile}
|
|
6676
7248
|
EBK_STAGE: \${{ inputs.stage }}
|
|
6677
7249
|
runs-on: ubuntu-latest
|
|
6678
7250
|
steps:
|
|
@@ -6681,19 +7253,12 @@ jobs:
|
|
|
6681
7253
|
EBK_PROTECTED_REF: \${{ github.ref }}
|
|
6682
7254
|
run: |
|
|
6683
7255
|
case "$EBK_STAGE:$EBK_PROTECTED_REF" in
|
|
6684
|
-
|
|
7256
|
+
${protectedWorkflowStageBranchCases}) ;;
|
|
6685
7257
|
*) echo 'Secret metadata stage does not match the protected branch'; exit 1 ;;
|
|
6686
7258
|
esac
|
|
6687
7259
|
case "$EBK_PROJECT" in
|
|
6688
7260
|
''|-*|*-|*[!a-z0-9-]*) echo 'Secret metadata project must be an exact normalized Slice name'; exit 1 ;;
|
|
6689
7261
|
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
7262
|
- uses: ${checkoutAction}
|
|
6698
7263
|
with:
|
|
6699
7264
|
persist-credentials: false
|
|
@@ -6705,24 +7270,153 @@ jobs:
|
|
|
6705
7270
|
- run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile
|
|
6706
7271
|
- name: Verify managed workspace integrity before AWS authority
|
|
6707
7272
|
run: pnpm nx run ${name}:workspace-health
|
|
7273
|
+
- id: policy
|
|
7274
|
+
name: Derive committed secret metadata stage policy
|
|
7275
|
+
run: |
|
|
7276
|
+
${indentWorkflowScript(protectedSecretPolicyDerivationScript(), 10)}
|
|
7277
|
+
- id: authority
|
|
7278
|
+
name: Derive canonical committed stage authority
|
|
7279
|
+
env:
|
|
7280
|
+
EBK_DEVELOPMENT_ACCOUNT: \${{ steps.policy.outputs.development_account_id }}
|
|
7281
|
+
EBK_PRODUCTION_ACCOUNT: \${{ steps.policy.outputs.production_account_id }}
|
|
7282
|
+
EBK_REGION: \${{ steps.policy.outputs.deployment_region }}
|
|
7283
|
+
EBK_STAGING_ACCOUNT: \${{ steps.policy.outputs.staging_account_id }}
|
|
7284
|
+
run: |
|
|
7285
|
+
${indentWorkflowScript(`set -euo pipefail\n${(0, workspace_files_thin_deploy_js_1.canonicalStageAuthorityDerivationScript)()}`, 10)}
|
|
6708
7286
|
- uses: ${configureAwsAction}
|
|
6709
7287
|
with:
|
|
6710
|
-
|
|
7288
|
+
audience: \${{ steps.authority.outputs.audience }}
|
|
7289
|
+
role-to-assume: \${{ steps.authority.outputs.role_arn }}
|
|
6711
7290
|
role-session-name: ebk-secret-metadata-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
6712
|
-
aws-region: \${{
|
|
6713
|
-
- name: Verify
|
|
6714
|
-
|
|
7291
|
+
aws-region: \${{ steps.policy.outputs.deployment_region }}
|
|
7292
|
+
- name: Verify exact stage authority
|
|
7293
|
+
env:
|
|
7294
|
+
AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
|
|
7295
|
+
AWS_PARTITION: \${{ steps.authority.outputs.partition }}
|
|
7296
|
+
EBK_POLICY_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
|
|
7297
|
+
run: |
|
|
7298
|
+
${indentWorkflowScript((0, workspace_files_thin_deploy_js_1.exactStageDeployerGuardScript)('Secret metadata', '$EBK_POLICY_ACCOUNT_ID'), 10)}
|
|
6715
7299
|
- name: Reconcile value-blind metadata
|
|
7300
|
+
env:
|
|
7301
|
+
AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
|
|
7302
|
+
AWS_EXPECTED_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
|
|
7303
|
+
AWS_REGION: \${{ steps.policy.outputs.deployment_region }}
|
|
6716
7304
|
run: |
|
|
6717
7305
|
args=("--stage=$EBK_STAGE" "--region=$AWS_REGION")
|
|
6718
7306
|
if [ "$EBK_ACTION" = apply ]; then args+=("--apply"); fi
|
|
6719
7307
|
pnpm nx run "$EBK_PROJECT:secret-metadata" -- "\${args[@]}"
|
|
6720
7308
|
`,
|
|
6721
|
-
'.github/workflows/
|
|
7309
|
+
'.github/workflows/secret-initialize.yml': `name: Secret initialize
|
|
7310
|
+
|
|
7311
|
+
on:
|
|
7312
|
+
workflow_dispatch:
|
|
7313
|
+
inputs:
|
|
7314
|
+
stage:
|
|
7315
|
+
description: Protected stage
|
|
7316
|
+
required: true
|
|
7317
|
+
type: choice
|
|
7318
|
+
options: [${protectedWorkflowStageChoices}]
|
|
7319
|
+
project:
|
|
7320
|
+
description: Exact Product Slice project
|
|
7321
|
+
required: true
|
|
7322
|
+
type: string
|
|
7323
|
+
logical_name:
|
|
7324
|
+
description: Exact machine-generated secret logical name
|
|
7325
|
+
required: true
|
|
7326
|
+
type: string
|
|
7327
|
+
action:
|
|
7328
|
+
description: Value-blind initialization action
|
|
7329
|
+
required: true
|
|
7330
|
+
default: report
|
|
7331
|
+
type: choice
|
|
7332
|
+
options: [report, apply]
|
|
7333
|
+
|
|
7334
|
+
concurrency:
|
|
7335
|
+
group: secret-initialize-\${{ inputs.stage }}-\${{ inputs.project }}-\${{ inputs.logical_name }}
|
|
7336
|
+
cancel-in-progress: false
|
|
7337
|
+
|
|
7338
|
+
permissions:
|
|
7339
|
+
contents: read
|
|
7340
|
+
|
|
7341
|
+
jobs:
|
|
7342
|
+
initialize:
|
|
7343
|
+
environment: \${{ inputs.stage }}
|
|
7344
|
+
permissions:
|
|
7345
|
+
contents: read
|
|
7346
|
+
id-token: write
|
|
7347
|
+
env:
|
|
7348
|
+
EBK_ACTION: \${{ inputs.action }}
|
|
7349
|
+
EBK_LOGICAL_NAME: \${{ inputs.logical_name }}
|
|
7350
|
+
EBK_PROJECT: \${{ inputs.project }}
|
|
7351
|
+
EBK_RENDERED_PROFILE: ${deploymentProfile}
|
|
7352
|
+
EBK_STAGE: \${{ inputs.stage }}
|
|
7353
|
+
runs-on: ubuntu-latest
|
|
7354
|
+
steps:
|
|
7355
|
+
- name: Validate protected request
|
|
7356
|
+
env:
|
|
7357
|
+
EBK_PROTECTED_REF: \${{ github.ref }}
|
|
7358
|
+
run: |
|
|
7359
|
+
case "$EBK_STAGE:$EBK_PROTECTED_REF" in
|
|
7360
|
+
${protectedWorkflowStageBranchCases}) ;;
|
|
7361
|
+
*) echo 'Secret initialize stage does not match the protected branch'; exit 1 ;;
|
|
7362
|
+
esac
|
|
7363
|
+
case "$EBK_PROJECT" in
|
|
7364
|
+
''|-*|*-|*[!a-z0-9-]*) echo 'Secret initialize project must be an exact normalized Slice name'; exit 1 ;;
|
|
7365
|
+
esac
|
|
7366
|
+
[[ "$EBK_LOGICAL_NAME" =~ ^[A-Za-z][A-Za-z0-9]{0,63}$ ]] ||
|
|
7367
|
+
{ echo 'Secret initialize logical name is invalid'; exit 1; }
|
|
7368
|
+
- uses: ${checkoutAction}
|
|
7369
|
+
with:
|
|
7370
|
+
persist-credentials: false
|
|
7371
|
+
- uses: ${pnpmAction}
|
|
7372
|
+
- uses: ${setupNodeAction}
|
|
7373
|
+
with:
|
|
7374
|
+
node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
|
|
7375
|
+
cache: pnpm
|
|
7376
|
+
- run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile
|
|
7377
|
+
- name: Verify managed workspace integrity before AWS authority
|
|
7378
|
+
run: pnpm nx run ${name}:workspace-health
|
|
7379
|
+
- id: policy
|
|
7380
|
+
name: Derive committed secret initialize stage policy
|
|
7381
|
+
run: |
|
|
7382
|
+
${indentWorkflowScript(protectedSecretPolicyDerivationScript(), 10)}
|
|
7383
|
+
- id: authority
|
|
7384
|
+
name: Derive canonical committed stage authority
|
|
7385
|
+
env:
|
|
7386
|
+
EBK_DEVELOPMENT_ACCOUNT: \${{ steps.policy.outputs.development_account_id }}
|
|
7387
|
+
EBK_PRODUCTION_ACCOUNT: \${{ steps.policy.outputs.production_account_id }}
|
|
7388
|
+
EBK_REGION: \${{ steps.policy.outputs.deployment_region }}
|
|
7389
|
+
EBK_STAGING_ACCOUNT: \${{ steps.policy.outputs.staging_account_id }}
|
|
7390
|
+
run: |
|
|
7391
|
+
${indentWorkflowScript(`set -euo pipefail\n${(0, workspace_files_thin_deploy_js_1.canonicalStageAuthorityDerivationScript)()}`, 10)}
|
|
7392
|
+
- uses: ${configureAwsAction}
|
|
7393
|
+
with:
|
|
7394
|
+
audience: \${{ steps.authority.outputs.audience }}
|
|
7395
|
+
role-to-assume: \${{ steps.authority.outputs.role_arn }}
|
|
7396
|
+
role-session-name: ebk-secret-initialize-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
7397
|
+
aws-region: \${{ steps.policy.outputs.deployment_region }}
|
|
7398
|
+
- name: Verify exact stage authority
|
|
7399
|
+
env:
|
|
7400
|
+
AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
|
|
7401
|
+
AWS_PARTITION: \${{ steps.authority.outputs.partition }}
|
|
7402
|
+
EBK_POLICY_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
|
|
7403
|
+
run: |
|
|
7404
|
+
${indentWorkflowScript((0, workspace_files_thin_deploy_js_1.exactStageDeployerGuardScript)('Secret initialize', '$EBK_POLICY_ACCOUNT_ID'), 10)}
|
|
7405
|
+
- name: Initialize machine-generated secret value-blind
|
|
7406
|
+
env:
|
|
7407
|
+
AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
|
|
7408
|
+
AWS_EXPECTED_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
|
|
7409
|
+
AWS_REGION: \${{ steps.policy.outputs.deployment_region }}
|
|
7410
|
+
run: |
|
|
7411
|
+
args=("--logicalName=$EBK_LOGICAL_NAME" "--stage=$EBK_STAGE" "--region=$AWS_REGION")
|
|
7412
|
+
if [ "$EBK_ACTION" = apply ]; then args+=("--apply"); fi
|
|
7413
|
+
pnpm nx run "$EBK_PROJECT:secret-initialize" -- "\${args[@]}"
|
|
7414
|
+
`,
|
|
7415
|
+
'.github/workflows/sync-back.yml': `name: Sync protected main history
|
|
6722
7416
|
|
|
6723
7417
|
on:
|
|
6724
7418
|
workflow_run:
|
|
6725
|
-
workflows: [
|
|
7419
|
+
workflows: [Verify]
|
|
6726
7420
|
types: [completed]
|
|
6727
7421
|
|
|
6728
7422
|
permissions:
|
|
@@ -6731,8 +7425,12 @@ permissions:
|
|
|
6731
7425
|
|
|
6732
7426
|
jobs:
|
|
6733
7427
|
sync-back:
|
|
7428
|
+
# The sync carries history, not deployment evidence: open it after any
|
|
7429
|
+
# completed main run so a failed production deploy cannot lock develop
|
|
7430
|
+
# behind a sync that never opens.
|
|
6734
7431
|
if: >-
|
|
6735
|
-
github.event.workflow_run.conclusion
|
|
7432
|
+
github.event.workflow_run.conclusion != 'cancelled' &&
|
|
7433
|
+
github.event.workflow_run.event == 'push' &&
|
|
6736
7434
|
github.event.workflow_run.head_branch == 'main'
|
|
6737
7435
|
runs-on: ubuntu-latest
|
|
6738
7436
|
steps:
|
|
@@ -6746,8 +7444,8 @@ jobs:
|
|
|
6746
7444
|
gh pr create \
|
|
6747
7445
|
--head main \
|
|
6748
7446
|
--base develop \
|
|
6749
|
-
--title 'chore: sync
|
|
6750
|
-
--body 'Managed EBK sync
|
|
7447
|
+
--title 'chore(git): sync protected main to develop' \
|
|
7448
|
+
--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
7449
|
number=$(gh pr list --state open --head main --base develop --json number --jq '.[0].number')
|
|
6752
7450
|
fi
|
|
6753
7451
|
echo "Sync-back pull request #$number is ready for review."
|
|
@@ -6764,7 +7462,7 @@ jobs:
|
|
|
6764
7462
|
fi
|
|
6765
7463
|
sleep 5
|
|
6766
7464
|
done
|
|
6767
|
-
echo
|
|
7465
|
+
echo "No pull-request verification run materialized; events created with GITHUB_TOKEN may not start workflows. Close and reopen sync pull request #$number with a user credential (a write-access user, or an agent in that user's own gh session: gh pr close $number && gh pr reopen $number), then approve its workflows if GitHub marks them approval-required."
|
|
6768
7466
|
exit 1
|
|
6769
7467
|
`,
|
|
6770
7468
|
'tools/ebk-infrastructure-plan-summary.mjs': `import {
|
|
@@ -7005,9 +7703,15 @@ infrastructure or protected deployment policy. Preserve the ownership boundary r
|
|
|
7005
7703
|
\`.ebk/ownership.json\`: managed files belong to EBK, seeded files are generated starting
|
|
7006
7704
|
points that adopters may change, and unrecorded files are adopter-owned.
|
|
7007
7705
|
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7706
|
+
Ordinary feature work uses affected tests, typecheck, lint, and ownership checks.
|
|
7707
|
+
At a local milestone, \`pnpm verify\` checks current source through
|
|
7708
|
+
\`${name}:verify-proposed\`, including before the first Git commit. This is not
|
|
7709
|
+
protected Blueprint acceptance. The separate \`${name}:verify\` target and
|
|
7710
|
+
protected GitHub workflows retain their committed-history and trust gates.
|
|
7711
|
+
|
|
7712
|
+
Human-supplied secret values are entered only by a human. The Developer-Agent workflow
|
|
7713
|
+
below describes the hand-off for \`ebk secret set\` and \`ebk secret remove\` and the
|
|
7714
|
+
value-blind paths agents and CI use.
|
|
7011
7715
|
|
|
7012
7716
|
## Work markers
|
|
7013
7717
|
|
|
@@ -7067,40 +7771,6 @@ export default tseslint.config(
|
|
|
7067
7771
|
rules: { 'no-console': 'error' },
|
|
7068
7772
|
},
|
|
7069
7773
|
);
|
|
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
7774
|
`,
|
|
7105
7775
|
'docs/architecture.md': `# Workspace architecture
|
|
7106
7776
|
|
|
@@ -7114,248 +7784,18 @@ The Standard Foundation lives at \`packages/foundation\`. Product slices live be
|
|
|
7114
7784
|
\`packages/<slice-name>\` and consume versioned Foundation contracts instead of importing
|
|
7115
7785
|
Foundation implementation files.
|
|
7116
7786
|
|
|
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.
|
|
7787
|
+
This workspace's \`${deploymentProfile}\` profile activates only:
|
|
7788
|
+
${activeStages.join(', ')}. Each active canonical consumer stage uses the matching Foundation.
|
|
7789
|
+
Inactive canonical stages are rejected. Framework-derived personal stages may use only the
|
|
7790
|
+
\`development\` Foundation; generic preview stages are unsupported.
|
|
7791
|
+
|
|
7792
|
+
Every active canonical stage owns a separate Router/domain, Cognito user pool,
|
|
7793
|
+
event bus, secret namespace, IAM boundary, logs, queues, ECR namespace, and
|
|
7794
|
+
stage-qualified SSM discovery contract. Discovery rejects a record for an inactive or different
|
|
7795
|
+
canonical stage. DynamoDB tables belong to individual Slices/tasks, not one global application
|
|
7796
|
+
table. Declared relational features add an independent Aurora cluster per canonical stage,
|
|
7797
|
+
with separate logical databases and migration histories per Slice. Existing relational resources
|
|
7798
|
+
remain declared on upgrade and after removing their last consumer; that is not database retirement.
|
|
7359
7799
|
`,
|
|
7360
7800
|
'docs/work-markers.md': `# Agent work markers
|
|
7361
7801
|
|
|
@@ -7415,8 +7855,22 @@ deployment by themselves. Open \`SECURITY\` and \`BLOCKER\` markers fail staging
|
|
|
7415
7855
|
deploy or refresh before AWS access. Protected remove and unlock remain usable for recovery.
|
|
7416
7856
|
There is no local or silent bypass in this prerelease contract; any future waiver mechanism
|
|
7417
7857
|
must be committed, reviewable, and auditable.
|
|
7858
|
+
|
|
7859
|
+
## Shared-operation claims
|
|
7860
|
+
|
|
7861
|
+
\`${workspace_files_shared_operation_claims_js_1.SHARED_OPERATION_CLAIMS_PATH}\` is a seeded claim ledger for operations that only one
|
|
7862
|
+
actor at a time may run: gateway setup, package release, candidate adoption, database
|
|
7863
|
+
replacement, and development deployment. Before starting one, an agent or human reads the
|
|
7864
|
+
current ledger where the other actors record claims; if another open claim names the same
|
|
7865
|
+
operation, it stops and reports the conflict instead of running the operation. Otherwise it
|
|
7866
|
+
records one \`EBK-TODO\` claim with the reference
|
|
7867
|
+
\`PLAN:${workspace_files_shared_operation_claims_js_1.SHARED_OPERATION_CLAIM_PHASE}/<operation>\`, commits it where the others read the
|
|
7868
|
+
ledger, and deletes the claim when the operation and its verification are complete. The
|
|
7869
|
+
work-markers target above lists open claims. Nothing enforces a claim; the ledger is a
|
|
7870
|
+
convention, current only as far as the copy each actor reads.
|
|
7418
7871
|
`,
|
|
7419
|
-
|
|
7872
|
+
[workspace_files_shared_operation_claims_js_1.SHARED_OPERATION_CLAIMS_PATH]: (0, workspace_files_shared_operation_claims_js_1.renderSharedOperationClaims)(name),
|
|
7873
|
+
'ebk.repository-policy.json': json(initialRepositoryPolicy),
|
|
7420
7874
|
'nx.json': json({
|
|
7421
7875
|
$schema: './node_modules/nx/schemas/nx-schema.json',
|
|
7422
7876
|
defaultBase: 'develop',
|
|
@@ -7456,6 +7910,7 @@ must be committed, reviewable, and auditable.
|
|
|
7456
7910
|
inputs: ['default', '^production'],
|
|
7457
7911
|
},
|
|
7458
7912
|
},
|
|
7913
|
+
analytics: false,
|
|
7459
7914
|
neverConnectToCloud: true,
|
|
7460
7915
|
useDaemonProcess: false,
|
|
7461
7916
|
}),
|
|
@@ -7471,23 +7926,25 @@ must be committed, reviewable, and auditable.
|
|
|
7471
7926
|
scripts: {
|
|
7472
7927
|
build: `nx run-many -t build --exclude=${name}`,
|
|
7473
7928
|
format: 'nx format:write',
|
|
7474
|
-
'format:check': 'nx format:check',
|
|
7929
|
+
'format:check': 'nx format:check --all',
|
|
7475
7930
|
graph: 'nx graph',
|
|
7476
7931
|
health: `nx run ${name}:workspace-health`,
|
|
7477
7932
|
lint: `nx run-many -t lint --exclude=${name}`,
|
|
7478
7933
|
test: `nx run-many -t test --exclude=${name}`,
|
|
7479
7934
|
typecheck: `nx run-many -t typecheck --exclude=${name}`,
|
|
7480
|
-
verify: `nx run ${name}:verify && nx run-many -t verify --exclude=${name}`,
|
|
7935
|
+
verify: `nx format:check --all && nx run ${name}:verify-proposed && nx run-many -t verify --exclude=${name}`,
|
|
7481
7936
|
},
|
|
7482
7937
|
dependencies: {
|
|
7483
7938
|
'@empire-builder-kit/runtime': frameworkVersion,
|
|
7484
7939
|
},
|
|
7485
7940
|
devDependencies: {
|
|
7486
7941
|
'@aws-sdk/client-iam': TOOL_VERSIONS.awsSdk,
|
|
7942
|
+
'@aws-sdk/client-rds': TOOL_VERSIONS.awsSdk,
|
|
7487
7943
|
'@aws-sdk/client-s3': TOOL_VERSIONS.awsSdk,
|
|
7488
7944
|
'@aws-sdk/client-secrets-manager': TOOL_VERSIONS.awsSdk,
|
|
7489
7945
|
'@aws-sdk/client-ssm': TOOL_VERSIONS.awsSdk,
|
|
7490
7946
|
'@aws-sdk/client-sts': TOOL_VERSIONS.awsSdk,
|
|
7947
|
+
'@aws-sdk/credential-providers': TOOL_VERSIONS.awsSdk,
|
|
7491
7948
|
'@eslint/js': TOOL_VERSIONS.eslintJs,
|
|
7492
7949
|
'@empire-builder-kit/nx': frameworkVersion,
|
|
7493
7950
|
'@types/node': TOOL_VERSIONS.nodeTypes,
|
|
@@ -7520,6 +7977,15 @@ must be committed, reviewable, and auditable.
|
|
|
7520
7977
|
protectedRef: 'refs/remotes/origin/develop',
|
|
7521
7978
|
},
|
|
7522
7979
|
},
|
|
7980
|
+
'blueprint-trust-check-main': {
|
|
7981
|
+
executor: '@empire-builder-kit/nx:blueprint-trust',
|
|
7982
|
+
options: {
|
|
7983
|
+
operation: 'adopt',
|
|
7984
|
+
output: '.ebk/evidence/supply-chain/blueprint-trust.json',
|
|
7985
|
+
protectedBranch: 'main',
|
|
7986
|
+
protectedRef: 'refs/remotes/origin/main',
|
|
7987
|
+
},
|
|
7988
|
+
},
|
|
7523
7989
|
'blueprint-trust-check-protected': {
|
|
7524
7990
|
executor: '@empire-builder-kit/nx:blueprint-trust',
|
|
7525
7991
|
options: {
|
|
@@ -7562,6 +8028,23 @@ must be committed, reviewable, and auditable.
|
|
|
7562
8028
|
executor: 'nx:run-commands',
|
|
7563
8029
|
options: { command: 'node tools/install-ebk-hooks.mjs' },
|
|
7564
8030
|
},
|
|
8031
|
+
'stage-authority-render': {
|
|
8032
|
+
cache: false,
|
|
8033
|
+
executor: 'nx:run-commands',
|
|
8034
|
+
outputs: ['{workspaceRoot}/.ebk/authority/{args.stage}/*'],
|
|
8035
|
+
options: {
|
|
8036
|
+
command: 'node tools/stage-authority-render.mjs',
|
|
8037
|
+
forwardAllArgs: true,
|
|
8038
|
+
},
|
|
8039
|
+
},
|
|
8040
|
+
'deploy-local': {
|
|
8041
|
+
cache: false,
|
|
8042
|
+
dependsOn: ['workspace-health'],
|
|
8043
|
+
executor: '@empire-builder-kit/nx:local-deploy',
|
|
8044
|
+
options: {
|
|
8045
|
+
stage: 'development',
|
|
8046
|
+
},
|
|
8047
|
+
},
|
|
7565
8048
|
'supply-chain-check': {
|
|
7566
8049
|
executor: 'nx:run-commands',
|
|
7567
8050
|
options: {
|
|
@@ -7585,64 +8068,27 @@ must be committed, reviewable, and auditable.
|
|
|
7585
8068
|
executor: '@empire-builder-kit/nx:work-markers',
|
|
7586
8069
|
options: {},
|
|
7587
8070
|
},
|
|
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
8071
|
verify: {
|
|
7602
8072
|
dependsOn: ['blueprint-trust-check', 'supply-chain-check'],
|
|
7603
8073
|
executor: 'nx:run-commands',
|
|
7604
8074
|
options: { command: 'node tools/workspace-health.mjs' },
|
|
7605
8075
|
},
|
|
8076
|
+
'verify-proposed': {
|
|
8077
|
+
dependsOn: ['supply-chain-check'],
|
|
8078
|
+
executor: 'nx:run-commands',
|
|
8079
|
+
options: { command: 'node tools/workspace-health.mjs' },
|
|
8080
|
+
},
|
|
7606
8081
|
'workspace-health': {
|
|
7607
8082
|
executor: 'nx:run-commands',
|
|
7608
8083
|
options: { command: 'node tools/workspace-health.mjs' },
|
|
7609
8084
|
},
|
|
7610
8085
|
},
|
|
7611
8086
|
}),
|
|
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
|
-
}),
|
|
8087
|
+
'packages/foundation/contracts/foundation.json': json(foundationPlan.descriptor),
|
|
7643
8088
|
'packages/foundation/contracts/costs.json': json({
|
|
7644
|
-
schemaVersion:
|
|
8089
|
+
schemaVersion: 3,
|
|
7645
8090
|
aurora: {
|
|
8091
|
+
declared: foundationPlan.resources.postgres,
|
|
7646
8092
|
engine: index_js_4.FOUNDATION_AURORA_ENGINE_FAMILY,
|
|
7647
8093
|
engineVersion: index_js_4.FOUNDATION_AURORA_ENGINE_VERSION,
|
|
7648
8094
|
},
|
|
@@ -7650,19 +8096,29 @@ domain. Without it, the shared Router uses its functional provider URL.
|
|
|
7650
8096
|
productionProfileSource: 'ebk.repository-policy.json',
|
|
7651
8097
|
stages: {
|
|
7652
8098
|
development: {
|
|
7653
|
-
aurora:
|
|
8099
|
+
aurora: foundationPlan.resources.postgres
|
|
8100
|
+
? 'scale-to-zero'
|
|
8101
|
+
: 'not-declared',
|
|
7654
8102
|
dataRetention: 'retained',
|
|
7655
|
-
nat: 'ec2',
|
|
7656
|
-
},
|
|
7657
|
-
staging: {
|
|
7658
|
-
aurora: 'scale-to-zero',
|
|
7659
|
-
dataRetention: 'retained',
|
|
7660
|
-
nat: 'ec2',
|
|
8103
|
+
nat: foundationPlan.resources.network ? 'ec2' : 'none',
|
|
7661
8104
|
},
|
|
8105
|
+
...(deploymentProfile === 'starter'
|
|
8106
|
+
? {}
|
|
8107
|
+
: {
|
|
8108
|
+
staging: {
|
|
8109
|
+
aurora: foundationPlan.resources.postgres
|
|
8110
|
+
? 'scale-to-zero'
|
|
8111
|
+
: 'not-declared',
|
|
8112
|
+
dataRetention: 'retained',
|
|
8113
|
+
nat: foundationPlan.resources.network ? 'ec2' : 'none',
|
|
8114
|
+
},
|
|
8115
|
+
}),
|
|
7662
8116
|
production: {
|
|
7663
|
-
aurora:
|
|
8117
|
+
aurora: foundationPlan.resources.postgres
|
|
8118
|
+
? 'repository-policy'
|
|
8119
|
+
: 'not-declared',
|
|
7664
8120
|
dataRetention: 'retained-and-protected',
|
|
7665
|
-
nat: 'managed',
|
|
8121
|
+
nat: foundationPlan.resources.network ? 'managed' : 'none',
|
|
7666
8122
|
},
|
|
7667
8123
|
},
|
|
7668
8124
|
usagePriced: [
|
|
@@ -7677,68 +8133,55 @@ domain. Without it, the shared Router uses its functional provider URL.
|
|
|
7677
8133
|
'packages/foundation/contracts/frontend-security-headers.json': json((0, index_js_2.foundationFrontendSecurityHeaderPolicy)()),
|
|
7678
8134
|
'packages/foundation/docs/costs.md': `# Foundation cost contract
|
|
7679
8135
|
|
|
7680
|
-
The
|
|
8136
|
+
The default Foundation has no Aurora, VPC/NAT, or ECS cluster. Slice-scoped DynamoDB
|
|
8137
|
+
is the first persistence choice. A relational requirement adds Aurora and its
|
|
8138
|
+
network/container migration dependencies; an explicit network workload can also
|
|
8139
|
+
require those dependencies without SQL. Previously provisioned capabilities stay
|
|
8140
|
+
declared until an explicit retirement operation, even with no remaining consumers.
|
|
7681
8141
|
The machine-readable stage inventory is \`../contracts/costs.json\`; keep this
|
|
7682
8142
|
seeded decision record aligned with organization-specific budgets and alerts.
|
|
7683
8143
|
|
|
7684
|
-
-
|
|
7685
|
-
|
|
7686
|
-
-
|
|
7687
|
-
Operating Policy profile selects
|
|
7688
|
-
|
|
8144
|
+
- When network/SQL is declared, development uses EC2 NAT, a development bastion,
|
|
8145
|
+
and Aurora scale-to-zero (${(0, cost_estimate_js_1.nonproductionAuroraScalingLabel)('development')}).
|
|
8146
|
+
${deploymentProfile === 'starter' ? '' : `- Declared staging network/SQL uses EC2 NAT and Aurora scale-to-zero (${(0, cost_estimate_js_1.nonproductionAuroraScalingLabel)('staging')}) by default.\n`}- Declared production networking uses managed NAT; production retains protected data. Its explicit Repository
|
|
8147
|
+
Operating Policy profile selects cost-optimized (0–2 ACUs, pause after five idle
|
|
8148
|
+
minutes), balanced (0.5–4 ACUs, always warm), or standard (2–16 ACUs, always warm).
|
|
8149
|
+
Newly generated repositories default to cost-optimized in every deployment profile.
|
|
8150
|
+
Cold resume adds latency; measure it for the workload and validate
|
|
8151
|
+
connection retries, request timeouts, migrations, and background polling.
|
|
8152
|
+
Capacity is shared by all Slices. Open connections can prevent pausing; storage
|
|
8153
|
+
and managed NAT remain billable while Aurora compute is paused.
|
|
8154
|
+
Existing 0.5–4 cost-optimized adopters must explicitly select balanced before
|
|
8155
|
+
upgrade, then opt into cost-optimized separately if cold starts are acceptable.
|
|
7689
8156
|
- ECS, EventBridge, Cognito, SNS, SSM, and CloudWatch are primarily usage-priced; logs,
|
|
7690
8157
|
metrics, alarms, and retained data still require active cost review.
|
|
8158
|
+
- Every deployed stage of every Standard Slice, including each personal development
|
|
8159
|
+
stage, adds its own fixed floor: a KMS key, secrets, a machine-to-machine app client,
|
|
8160
|
+
CloudWatch alarms and a dashboard. The estimate below lists it.
|
|
7691
8161
|
|
|
7692
8162
|
Run \`foundation:remove\` only for disposable stages. Production is retained and protected by
|
|
7693
8163
|
the SST configuration.
|
|
7694
|
-
`,
|
|
7695
|
-
'packages/foundation/docs/runbooks.md': `# Foundation runbooks
|
|
7696
|
-
|
|
7697
|
-
## Deployment
|
|
7698
8164
|
|
|
7699
|
-
|
|
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
|
-
`,
|
|
8165
|
+
${(0, cost_estimate_js_1.renderFoundationCostEstimate)({ deploymentProfile, headingLevel: 2 })}`,
|
|
7730
8166
|
'packages/foundation/docs/security.md': `# Foundation security contract
|
|
7731
8167
|
|
|
7732
8168
|
- Production resources are retained and protected.
|
|
7733
|
-
-
|
|
7734
|
-
- Aurora IAM authentication is enabled for generated short-lived migration
|
|
8169
|
+
- When relational persistence is declared, the shared Aurora master credential is Foundation-internal.
|
|
8170
|
+
- Declared Aurora IAM authentication is enabled for generated short-lived migration
|
|
7735
8171
|
tasks. The exact cluster resource ID is non-secret Foundation discovery.
|
|
7736
|
-
- Product
|
|
8172
|
+
- Relational Product Slices receive scoped logical-database credentials through the provisioning contract.
|
|
8173
|
+
- DynamoDB tables and their access are Slice/task scoped; no shared Foundation application table exists.
|
|
7737
8174
|
- Cognito owns authentication; product slices own authorization policy and app clients.
|
|
7738
8175
|
- Lambda and server-side web workloads use resource linking. Containers use the documented
|
|
7739
8176
|
stage-scoped SSM/secret path and least-privilege IAM.
|
|
7740
8177
|
- Public domains are configured explicitly through \`EBK_ROOT_DOMAIN\`; generated source never
|
|
7741
8178
|
contains credentials or customer domain assumptions.
|
|
8179
|
+
- Every stage's CloudFront Router has its own global Web ACL. The baseline always includes AWS
|
|
8180
|
+
Common Rule Set, Known Bad Inputs, Amazon IP Reputation, rate blocking, CloudWatch metrics,
|
|
8181
|
+
and sampled requests. Nonproduction is allowlisted by default. Production is public by
|
|
8182
|
+
default, but cannot deploy until the exact policy has passed a ${rehearsalStage} blocking rehearsal.
|
|
8183
|
+
- Full WAF request logging is disabled by default. Enabling it keeps blocked requests only,
|
|
8184
|
+
uses bounded retention, and redacts query strings, cookies, authorization, and API keys.
|
|
7742
8185
|
|
|
7743
8186
|
The managed \`contracts/frontend-security-headers.json\` file owns the transport
|
|
7744
8187
|
minimum: HTTPS in deployed environments, one-year HSTS there, and the common
|
|
@@ -7770,18 +8213,32 @@ declare const $app: {
|
|
|
7770
8213
|
|
|
7771
8214
|
import {
|
|
7772
8215
|
FOUNDATION_SSM_SCHEMA_VERSION,
|
|
7773
|
-
|
|
8216
|
+
createRouterWafRules,
|
|
8217
|
+
createRouterWafStagePlan,
|
|
8218
|
+
${foundationPlan.resources.postgres ? ' foundationDatabaseEndpointHost,' : ''}
|
|
7774
8219
|
foundationSsmPath,
|
|
8220
|
+
normalizeRouterWafPolicy,
|
|
8221
|
+
reconcileRouterWafBlockingRehearsal,
|
|
8222
|
+
routerWafContractDigest,
|
|
7775
8223
|
type FoundationSsmKey,
|
|
7776
8224
|
} from '@empire-builder-kit/nx/foundation';
|
|
7777
8225
|
import {
|
|
7778
|
-
FOUNDATION_AURORA_ENGINE,
|
|
7779
|
-
FOUNDATION_AURORA_ENGINE_VERSION,
|
|
8226
|
+
${foundationPlan.resources.postgres ? ' FOUNDATION_AURORA_ENGINE,\n FOUNDATION_AURORA_ENGINE_VERSION,' : ''}
|
|
7780
8227
|
productionFoundationProfileContract,
|
|
7781
8228
|
} from '@empire-builder-kit/nx/repository-policy';
|
|
8229
|
+
import { installStageIamBoundaryTransforms } from '@empire-builder-kit/nx/aws-authority';
|
|
8230
|
+
import { createStageAuthorityIdentity } from '@empire-builder-kit/nx/aws-authority';
|
|
8231
|
+
import {
|
|
8232
|
+
configuredDeploymentAccountForStage,
|
|
8233
|
+
configuredDeploymentRegion,
|
|
8234
|
+
productionRehearsalStage,
|
|
8235
|
+
} from '@empire-builder-kit/nx/repository-policy';
|
|
7782
8236
|
import { createFoundationAccountCostControlPlan } from '@empire-builder-kit/nx/performance';
|
|
7783
8237
|
import accountCostPolicy from '../performance/account-cost-policy.json';
|
|
7784
|
-
import
|
|
8238
|
+
import routerWafBlockingRehearsal from '../security/router-waf-blocking-rehearsal.json';
|
|
8239
|
+
import routerWafLiveEvidence from '../security/router-waf-live-evidence.json';
|
|
8240
|
+
import routerWafPolicy from '../security/router-waf.json';
|
|
8241
|
+
import repositoryPolicy from '../../../ebk.repository-policy.json';
|
|
7785
8242
|
|
|
7786
8243
|
function identityDomainPrefix(stage: string, accountId: string): string {
|
|
7787
8244
|
if (!/^\\d{12}$/.test(accountId)) {
|
|
@@ -7802,7 +8259,7 @@ function identityDomainPrefix(stage: string, accountId: string): string {
|
|
|
7802
8259
|
return value.slice(0, 50) + '-' + digest;
|
|
7803
8260
|
}
|
|
7804
8261
|
|
|
7805
|
-
function publishFoundation(values: Record<FoundationSsmKey, any
|
|
8262
|
+
function publishFoundation(values: Partial<Record<FoundationSsmKey, any>>) {
|
|
7806
8263
|
return Object.fromEntries(
|
|
7807
8264
|
Object.entries(values).map(([key, value]) => [
|
|
7808
8265
|
key,
|
|
@@ -7815,45 +8272,78 @@ function publishFoundation(values: Record<FoundationSsmKey, any>) {
|
|
|
7815
8272
|
);
|
|
7816
8273
|
}
|
|
7817
8274
|
|
|
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
|
-
}
|
|
8275
|
+
export function foundation() {
|
|
7826
8276
|
const isProduction = $app.stage === 'production';
|
|
7827
|
-
const isStaging = $app.stage === 'staging';
|
|
8277
|
+
${foundationPlan.resources.postgres ? " const isStaging = $app.stage === 'staging';" : ''}
|
|
7828
8278
|
const rootDomain = process.env.EBK_ROOT_DOMAIN?.trim();
|
|
7829
8279
|
const productionConfiguration = isProduction
|
|
7830
8280
|
? productionFoundationProfileContract(
|
|
7831
8281
|
process.env.EBK_PRODUCTION_FOUNDATION_PROFILE,
|
|
7832
8282
|
)
|
|
7833
8283
|
: undefined;
|
|
7834
|
-
const natMode = productionConfiguration?.contract.nat ?? 'ec2';
|
|
7835
|
-
const
|
|
7836
|
-
if (
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
8284
|
+
${foundationPlan.resources.network ? " const natMode = productionConfiguration?.contract.nat ?? 'ec2';" : ''}
|
|
8285
|
+
const normalizedRouterWafPolicy = normalizeRouterWafPolicy(routerWafPolicy);
|
|
8286
|
+
if (repositoryPolicy.deploymentProfile !== normalizedRouterWafPolicy.deploymentProfile) {
|
|
8287
|
+
throw new Error('Router WAF policy profile differs from committed repository policy.');
|
|
8288
|
+
}
|
|
8289
|
+
const rehearsalStage = productionRehearsalStage(normalizedRouterWafPolicy.deploymentProfile);
|
|
8290
|
+
const rehearsalAccount = configuredDeploymentAccountForStage(
|
|
8291
|
+
normalizedRouterWafPolicy.deploymentProfile,
|
|
8292
|
+
repositoryPolicy.deploymentAccounts,
|
|
8293
|
+
rehearsalStage,
|
|
8294
|
+
);
|
|
8295
|
+
const rehearsalRegion = configuredDeploymentRegion(repositoryPolicy.deploymentRegion);
|
|
8296
|
+
const rehearsalRoleArn = createStageAuthorityIdentity({
|
|
8297
|
+
accountId: rehearsalAccount,
|
|
8298
|
+
partition: rehearsalRegion.startsWith('us-gov-') ? 'aws-us-gov' : 'aws',
|
|
8299
|
+
region: rehearsalRegion,
|
|
8300
|
+
stage: rehearsalStage,
|
|
8301
|
+
}).deploymentRoleArn;
|
|
8302
|
+
const wafPlan = createRouterWafStagePlan({
|
|
8303
|
+
policy: normalizedRouterWafPolicy,
|
|
8304
|
+
rehearsal: isProduction
|
|
8305
|
+
? reconcileRouterWafBlockingRehearsal({
|
|
8306
|
+
evidence: routerWafLiveEvidence,
|
|
8307
|
+
expected: {
|
|
8308
|
+
accountId: rehearsalAccount,
|
|
8309
|
+
deploymentRoleArn: rehearsalRoleArn,
|
|
8310
|
+
region: rehearsalRegion,
|
|
8311
|
+
stage: rehearsalStage,
|
|
8312
|
+
wafContractDigest: routerWafContractDigest(normalizedRouterWafPolicy),
|
|
8313
|
+
workspace: '${name}',
|
|
8314
|
+
},
|
|
8315
|
+
policy: normalizedRouterWafPolicy,
|
|
8316
|
+
receipt: routerWafBlockingRehearsal,
|
|
8317
|
+
})
|
|
8318
|
+
: null,
|
|
8319
|
+
stage: $app.stage,
|
|
8320
|
+
});
|
|
8321
|
+
const deploymentAccount = process.env.AWS_EXPECTED_ACCOUNT_ID?.trim() ?? '';
|
|
8322
|
+
if (!/^\\d{12}$/.test(deploymentAccount)) {
|
|
8323
|
+
throw new Error(
|
|
8324
|
+
'Foundation deployment requires the exact committed AWS account before infrastructure construction.',
|
|
7845
8325
|
);
|
|
7846
8326
|
}
|
|
8327
|
+
installStageIamBoundaryTransforms({
|
|
8328
|
+
accountId: deploymentAccount,
|
|
8329
|
+
instanceProfileResource: aws.iam.InstanceProfile,
|
|
8330
|
+
registerTransform: $transform,
|
|
8331
|
+
roleResource: aws.iam.Role,
|
|
8332
|
+
stage: ($dev ? 'development' : $app.stage) as
|
|
8333
|
+
| 'development'
|
|
8334
|
+
| 'staging'
|
|
8335
|
+
| 'production',
|
|
8336
|
+
});
|
|
7847
8337
|
|
|
7848
|
-
|
|
8338
|
+
${foundationPlan.resources.network
|
|
8339
|
+
? ` const vpc = new sst.aws.Vpc('FoundationVpc', {
|
|
7849
8340
|
az: 2,
|
|
7850
8341
|
bastion: $app.stage === 'development',
|
|
7851
8342
|
nat:
|
|
7852
|
-
natMode === 'ec2'
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
});
|
|
7856
|
-
const cluster = new sst.aws.Cluster('FoundationCluster', { vpc });
|
|
8343
|
+
natMode === 'ec2' ? 'ec2' : 'managed',
|
|
8344
|
+
});`
|
|
8345
|
+
: ''}
|
|
8346
|
+
${foundationPlan.resources.containerCompute ? " const cluster = new sst.aws.Cluster('FoundationCluster', { vpc });" : ''}
|
|
7857
8347
|
const router = new sst.aws.Router('FoundationRouter', {
|
|
7858
8348
|
...(rootDomain
|
|
7859
8349
|
? {
|
|
@@ -7862,10 +8352,138 @@ export function foundation(
|
|
|
7862
8352
|
aliases: ['*.' + rootDomain],
|
|
7863
8353
|
dns: sst.aws.dns(),
|
|
7864
8354
|
},
|
|
7865
|
-
|
|
8355
|
+
}
|
|
7866
8356
|
: {}),
|
|
8357
|
+
waf: {
|
|
8358
|
+
logging: wafPlan.logging,
|
|
8359
|
+
managedRules: {
|
|
8360
|
+
coreRuleSet: true,
|
|
8361
|
+
knownBadInputs: true,
|
|
8362
|
+
sqlInjection: false,
|
|
8363
|
+
},
|
|
8364
|
+
rateLimitPerIp: wafPlan.rateLimitPerIp,
|
|
8365
|
+
},
|
|
8366
|
+
transform: {
|
|
8367
|
+
cdn(args: any) {
|
|
8368
|
+
if (!args.webAclArn) {
|
|
8369
|
+
throw new Error(
|
|
8370
|
+
'Foundation Router must attach its exact global Web ACL.',
|
|
8371
|
+
);
|
|
8372
|
+
}
|
|
8373
|
+
args.tags = {
|
|
8374
|
+
...(args.tags ?? {}),
|
|
8375
|
+
'ebk:managed-by': 'empire-builder-kit',
|
|
8376
|
+
'ebk:owner': 'foundation',
|
|
8377
|
+
'ebk:stage': wafPlan.stage,
|
|
8378
|
+
'ebk:workspace': '${name}',
|
|
8379
|
+
};
|
|
8380
|
+
},
|
|
8381
|
+
waf(args: any, opts: any) {
|
|
8382
|
+
if (args.scope !== 'CLOUDFRONT') {
|
|
8383
|
+
throw new Error(
|
|
8384
|
+
'Foundation Router Web ACL must use global CloudFront scope.',
|
|
8385
|
+
);
|
|
8386
|
+
}
|
|
8387
|
+
const ipSets: { ipv4Arn?: any; ipv6Arn?: any; smokeArn?: any } = {};
|
|
8388
|
+
if (wafPlan.ipv4Cidrs.length > 0) {
|
|
8389
|
+
ipSets.ipv4Arn = new aws.wafv2.IpSet(
|
|
8390
|
+
'FoundationRouterIpv4Allowlist',
|
|
8391
|
+
{
|
|
8392
|
+
addresses: wafPlan.ipv4Cidrs,
|
|
8393
|
+
ipAddressVersion: 'IPV4',
|
|
8394
|
+
name: 'foundation-' + wafPlan.stage + '-router-ipv4-allowlist',
|
|
8395
|
+
scope: 'CLOUDFRONT',
|
|
8396
|
+
tags: {
|
|
8397
|
+
'ebk:managed-by': 'empire-builder-kit',
|
|
8398
|
+
'ebk:owner': 'foundation',
|
|
8399
|
+
'ebk:policy-digest': wafPlan.policyDigest,
|
|
8400
|
+
'ebk:stage': wafPlan.stage,
|
|
8401
|
+
'ebk:workspace': '${name}',
|
|
8402
|
+
},
|
|
8403
|
+
},
|
|
8404
|
+
{ provider: opts.provider },
|
|
8405
|
+
).arn;
|
|
8406
|
+
}
|
|
8407
|
+
if (wafPlan.ipv6Cidrs.length > 0) {
|
|
8408
|
+
ipSets.ipv6Arn = new aws.wafv2.IpSet(
|
|
8409
|
+
'FoundationRouterIpv6Allowlist',
|
|
8410
|
+
{
|
|
8411
|
+
addresses: wafPlan.ipv6Cidrs,
|
|
8412
|
+
ipAddressVersion: 'IPV6',
|
|
8413
|
+
name: 'foundation-' + wafPlan.stage + '-router-ipv6-allowlist',
|
|
8414
|
+
scope: 'CLOUDFRONT',
|
|
8415
|
+
tags: {
|
|
8416
|
+
'ebk:managed-by': 'empire-builder-kit',
|
|
8417
|
+
'ebk:owner': 'foundation',
|
|
8418
|
+
'ebk:policy-digest': wafPlan.policyDigest,
|
|
8419
|
+
'ebk:stage': wafPlan.stage,
|
|
8420
|
+
'ebk:workspace': '${name}',
|
|
8421
|
+
},
|
|
8422
|
+
},
|
|
8423
|
+
{ provider: opts.provider },
|
|
8424
|
+
).arn;
|
|
8425
|
+
}
|
|
8426
|
+
if (wafPlan.access === 'allowlist') {
|
|
8427
|
+
// Smoke rotates WAF's LockToken outside Pulumi. Keep the existing
|
|
8428
|
+
// baseline IP sets untouched; AWS's Cloud Control handler owns the
|
|
8429
|
+
// separate ephemeral set's lifecycle without a cached lock token.
|
|
8430
|
+
const smokeAccess = new aws.cloudcontrol.Resource(
|
|
8431
|
+
'FoundationRouterSmokeAccess',
|
|
8432
|
+
{
|
|
8433
|
+
typeName: 'AWS::WAFv2::IPSet',
|
|
8434
|
+
desiredState: JSON.stringify({
|
|
8435
|
+
Addresses: [],
|
|
8436
|
+
IPAddressVersion: 'IPV4',
|
|
8437
|
+
Name: 'foundation-' + wafPlan.stage + '-router-smoke-access',
|
|
8438
|
+
Scope: 'CLOUDFRONT',
|
|
8439
|
+
Tags: Object.entries({
|
|
8440
|
+
'ebk:managed-by': 'empire-builder-kit',
|
|
8441
|
+
'ebk:owner': 'foundation',
|
|
8442
|
+
'ebk:purpose': 'private-smoke-access',
|
|
8443
|
+
'ebk:stage': wafPlan.stage,
|
|
8444
|
+
'ebk:workspace': '${name}',
|
|
8445
|
+
}).map(([Key, Value]) => ({ Key, Value })),
|
|
8446
|
+
}),
|
|
8447
|
+
},
|
|
8448
|
+
{
|
|
8449
|
+
provider: opts.provider,
|
|
8450
|
+
// This dedicated set has creation-only identity/ownership inputs.
|
|
8451
|
+
// Admission owns its addresses; the Web ACL owns policy binding.
|
|
8452
|
+
// Retain old inputs on upgrade too: Cloud Control patches arrays
|
|
8453
|
+
// by index, which is unsafe for service-reordered Tags (including
|
|
8454
|
+
// removing an older policy-digest tag). Never patch this document.
|
|
8455
|
+
ignoreChanges: ['desiredState'],
|
|
8456
|
+
},
|
|
8457
|
+
);
|
|
8458
|
+
ipSets.smokeArn = smokeAccess.properties.apply(
|
|
8459
|
+
(properties: string) => JSON.parse(properties).Arn,
|
|
8460
|
+
);
|
|
8461
|
+
}
|
|
8462
|
+
args.defaultAction = { allow: {} };
|
|
8463
|
+
args.rules = createRouterWafRules(wafPlan, ipSets);
|
|
8464
|
+
args.scope = 'CLOUDFRONT';
|
|
8465
|
+
args.tags = {
|
|
8466
|
+
...(args.tags ?? {}),
|
|
8467
|
+
'ebk:managed-by': 'empire-builder-kit',
|
|
8468
|
+
'ebk:owner': 'foundation',
|
|
8469
|
+
'ebk:policy-digest': wafPlan.policyDigest,
|
|
8470
|
+
'ebk:stage': wafPlan.stage,
|
|
8471
|
+
'ebk:workspace': '${name}',
|
|
8472
|
+
};
|
|
8473
|
+
args.visibilityConfig = {
|
|
8474
|
+
cloudwatchMetricsEnabled: true,
|
|
8475
|
+
metricName:
|
|
8476
|
+
'Ebk' +
|
|
8477
|
+
wafPlan.stage[0].toUpperCase() +
|
|
8478
|
+
wafPlan.stage.slice(1) +
|
|
8479
|
+
'FoundationRouterWaf',
|
|
8480
|
+
sampledRequestsEnabled: true,
|
|
8481
|
+
};
|
|
8482
|
+
},
|
|
8483
|
+
},
|
|
7867
8484
|
});
|
|
7868
|
-
|
|
8485
|
+
${foundationPlan.resources.postgres
|
|
8486
|
+
? ` const database = new sst.aws.Aurora('FoundationDatabase', {
|
|
7869
8487
|
// SQL bootstrap and migrations run inside the VPC. Keep the public HTTPS
|
|
7870
8488
|
// Data API surface disabled; protected GitHub roles also deny its actions.
|
|
7871
8489
|
dataApi: false,
|
|
@@ -7887,6 +8505,47 @@ export function foundation(
|
|
|
7887
8505
|
},
|
|
7888
8506
|
vpc,
|
|
7889
8507
|
});
|
|
8508
|
+
const databaseClusterEndpoint = database.nodes.cluster.endpoint.apply(
|
|
8509
|
+
foundationDatabaseEndpointHost,
|
|
8510
|
+
);
|
|
8511
|
+
const databaseReaderEndpoint = database.nodes.cluster.readerEndpoint.apply(
|
|
8512
|
+
foundationDatabaseEndpointHost,
|
|
8513
|
+
);
|
|
8514
|
+
// SST v4.17.1 creates a top-level LinkRef on the next tick and its Aurora
|
|
8515
|
+
// implementation unconditionally splits cluster endpoints. During a
|
|
8516
|
+
// governed refresh after an interrupted first deploy, those provider
|
|
8517
|
+
// outputs are legitimately absent. Preserve the normal link contract while
|
|
8518
|
+
// keeping that read-only recovery program total; a real deploy resolves the
|
|
8519
|
+
// exact endpoints before any discovery resource is created.
|
|
8520
|
+
database.getSSTLink = () => ({
|
|
8521
|
+
include: [
|
|
8522
|
+
sst.aws.permission({
|
|
8523
|
+
actions: ['secretsmanager:GetSecretValue'],
|
|
8524
|
+
resources: [database.secretArn],
|
|
8525
|
+
}),
|
|
8526
|
+
sst.aws.permission({
|
|
8527
|
+
actions: [
|
|
8528
|
+
'rds-data:BatchExecuteStatement',
|
|
8529
|
+
'rds-data:BeginTransaction',
|
|
8530
|
+
'rds-data:CommitTransaction',
|
|
8531
|
+
'rds-data:ExecuteStatement',
|
|
8532
|
+
'rds-data:RollbackTransaction',
|
|
8533
|
+
],
|
|
8534
|
+
resources: [database.clusterArn],
|
|
8535
|
+
}),
|
|
8536
|
+
],
|
|
8537
|
+
properties: {
|
|
8538
|
+
clusterArn: database.clusterArn,
|
|
8539
|
+
database: database.database,
|
|
8540
|
+
host: databaseClusterEndpoint,
|
|
8541
|
+
password: database.password,
|
|
8542
|
+
port: database.port,
|
|
8543
|
+
reader: databaseReaderEndpoint,
|
|
8544
|
+
secretArn: database.secretArn,
|
|
8545
|
+
username: database.username,
|
|
8546
|
+
},
|
|
8547
|
+
});`
|
|
8548
|
+
: ''}
|
|
7890
8549
|
const foundationAccountId = aws.getCallerIdentityOutput({}).accountId;
|
|
7891
8550
|
const foundationPartition = aws.getPartitionOutput({});
|
|
7892
8551
|
const foundationRegion = aws.getRegionOutput({});
|
|
@@ -7915,11 +8574,12 @@ export function foundation(
|
|
|
7915
8574
|
const accountCostControl = createFoundationAccountCostControlPlan({
|
|
7916
8575
|
evaluatedAt: new Date().toISOString(),
|
|
7917
8576
|
policy: accountCostPolicy,
|
|
8577
|
+
rehearsalStage: '${rehearsalStage}',
|
|
7918
8578
|
region: process.env.AWS_REGION,
|
|
7919
8579
|
stage: $app.stage,
|
|
7920
8580
|
workspace: '${name}',
|
|
7921
8581
|
});
|
|
7922
|
-
const databaseInstanceId = database.nodes.instance.identifier;
|
|
8582
|
+
${foundationPlan.resources.postgres ? ' const databaseInstanceId = database.nodes.instance.identifier;' : ''}
|
|
7923
8583
|
const alertTopicPolicy = new aws.sns.TopicPolicy(
|
|
7924
8584
|
'FoundationAlertTopicPolicy',
|
|
7925
8585
|
{
|
|
@@ -7949,6 +8609,7 @@ export function foundation(
|
|
|
7949
8609
|
},
|
|
7950
8610
|
],
|
|
7951
8611
|
resources: [alertTopic.arn],
|
|
8612
|
+
sid: 'AllowBudgetAlerts',
|
|
7952
8613
|
},
|
|
7953
8614
|
{
|
|
7954
8615
|
actions: ['sns:Publish'],
|
|
@@ -7962,8 +8623,10 @@ export function foundation(
|
|
|
7962
8623
|
type: 'Service',
|
|
7963
8624
|
}],
|
|
7964
8625
|
resources: [alertTopic.arn],
|
|
8626
|
+
sid: 'AllowCostAnomalyAlerts',
|
|
7965
8627
|
},
|
|
7966
|
-
|
|
8628
|
+
${foundationPlan.resources.postgres
|
|
8629
|
+
? ` {
|
|
7967
8630
|
actions: ['sns:Publish'],
|
|
7968
8631
|
conditions: [
|
|
7969
8632
|
{
|
|
@@ -7984,7 +8647,9 @@ export function foundation(
|
|
|
7984
8647
|
type: 'Service',
|
|
7985
8648
|
}],
|
|
7986
8649
|
resources: [alertTopic.arn],
|
|
7987
|
-
|
|
8650
|
+
sid: 'AllowRdsEventAlerts',
|
|
8651
|
+
},`
|
|
8652
|
+
: ''}
|
|
7988
8653
|
{
|
|
7989
8654
|
actions: ['sns:Publish'],
|
|
7990
8655
|
conditions: [
|
|
@@ -8008,6 +8673,7 @@ export function foundation(
|
|
|
8008
8673
|
},
|
|
8009
8674
|
],
|
|
8010
8675
|
resources: [alertTopic.arn],
|
|
8676
|
+
sid: 'AllowCloudWatchAlarmAlerts',
|
|
8011
8677
|
},
|
|
8012
8678
|
],
|
|
8013
8679
|
}).json,
|
|
@@ -8036,7 +8702,43 @@ export function foundation(
|
|
|
8036
8702
|
{ dependsOn: [alertTopicPolicy] },
|
|
8037
8703
|
)
|
|
8038
8704
|
: undefined;
|
|
8039
|
-
const
|
|
8705
|
+
const accountCostAnomalyMonitor = isProduction
|
|
8706
|
+
? new aws.costexplorer.AnomalyMonitor(
|
|
8707
|
+
'FoundationAccountCostAnomalyMonitor',
|
|
8708
|
+
{
|
|
8709
|
+
monitorDimension: 'SERVICE',
|
|
8710
|
+
monitorType: 'DIMENSIONAL',
|
|
8711
|
+
name: '${name}-account-service-cost-anomalies',
|
|
8712
|
+
tags: {
|
|
8713
|
+
'ebk:cost-policy-digest': accountCostControl.policyDigest,
|
|
8714
|
+
'ebk:cost-scope': '${name}/account/production',
|
|
8715
|
+
'ebk:owner': accountCostControl.owner,
|
|
8716
|
+
},
|
|
8717
|
+
},
|
|
8718
|
+
)
|
|
8719
|
+
: undefined;
|
|
8720
|
+
const accountCostAnomalySubscription =
|
|
8721
|
+
accountCostControl.anomaly && accountCostAnomalyMonitor
|
|
8722
|
+
? new aws.costexplorer.AnomalySubscription(
|
|
8723
|
+
'FoundationAccountCostAnomalySubscription',
|
|
8724
|
+
{
|
|
8725
|
+
frequency: accountCostControl.anomaly.subscription.frequency,
|
|
8726
|
+
monitorArnLists: [accountCostAnomalyMonitor.arn],
|
|
8727
|
+
name: accountCostControl.anomaly.subscription.name,
|
|
8728
|
+
subscribers: [{ address: alertTopic.arn, type: 'SNS' }],
|
|
8729
|
+
tags: {
|
|
8730
|
+
'ebk:cost-policy-digest': accountCostControl.policyDigest,
|
|
8731
|
+
'ebk:cost-scope': '${name}/account/production',
|
|
8732
|
+
'ebk:owner': accountCostControl.owner,
|
|
8733
|
+
},
|
|
8734
|
+
thresholdExpression:
|
|
8735
|
+
accountCostControl.anomaly.subscription.thresholdExpression,
|
|
8736
|
+
},
|
|
8737
|
+
{ dependsOn: [accountCostAnomalyMonitor, alertTopicPolicy] },
|
|
8738
|
+
)
|
|
8739
|
+
: undefined;
|
|
8740
|
+
${foundationPlan.resources.postgres
|
|
8741
|
+
? ` const databaseEvents = isProduction
|
|
8040
8742
|
? new aws.rds.EventSubscription(
|
|
8041
8743
|
'FoundationDatabaseEvents',
|
|
8042
8744
|
{
|
|
@@ -8118,7 +8820,14 @@ export function foundation(
|
|
|
8118
8820
|
threshold: 90,
|
|
8119
8821
|
treatMissingData: 'notBreaching',
|
|
8120
8822
|
},
|
|
8121
|
-
)
|
|
8823
|
+
);`
|
|
8824
|
+
: ` const dashboard = new aws.cloudwatch.Dashboard('FoundationDashboard', {
|
|
8825
|
+
dashboardName: 'ebk-' + $app.stage + '-foundation',
|
|
8826
|
+
dashboardBody: JSON.stringify({ widgets: [{
|
|
8827
|
+
type: 'text', x: 0, y: 0, width: 24, height: 3,
|
|
8828
|
+
properties: { markdown: '# Empire Builder Kit Foundation\\nStage: ' + $app.stage + '\\nPersistence: Slice-scoped DynamoDB; SQL not declared.' },
|
|
8829
|
+
}] }),
|
|
8830
|
+
});`}
|
|
8122
8831
|
|
|
8123
8832
|
if (new Set(['development', 'staging', 'production']).has($app.stage)) {
|
|
8124
8833
|
new aws.cloudwatch.EventArchive('FoundationEventArchive', {
|
|
@@ -8134,18 +8843,18 @@ export function foundation(
|
|
|
8134
8843
|
accountCostControl.authority.monthlyMaximumUsd,
|
|
8135
8844
|
),
|
|
8136
8845
|
alertTopicArn: alertTopic.arn,
|
|
8137
|
-
capabilities:
|
|
8138
|
-
clusterId: cluster.id,
|
|
8139
|
-
contractVersion: '
|
|
8140
|
-
|
|
8141
|
-
|
|
8142
|
-
|
|
8143
|
-
),
|
|
8846
|
+
capabilities: ${JSON.stringify(foundationPlan.descriptor.capabilities.join(','))},
|
|
8847
|
+
${foundationPlan.resources.containerCompute ? ' clusterId: cluster.id,' : ''}
|
|
8848
|
+
contractVersion: '${foundationPlan.descriptor.contractVersion}',
|
|
8849
|
+
${foundationPlan.resources.postgres
|
|
8850
|
+
? ` databaseClusterArn: database.clusterArn,
|
|
8851
|
+
databaseClusterEndpoint,
|
|
8144
8852
|
databaseClusterId: database.id,
|
|
8145
8853
|
databaseClusterResourceId: database.nodes.cluster.clusterResourceId,
|
|
8146
|
-
databaseHost:
|
|
8854
|
+
databaseHost: databaseClusterEndpoint,
|
|
8147
8855
|
databaseMasterSecretArn: database.secretArn,
|
|
8148
|
-
databasePort: database.port.apply(String)
|
|
8856
|
+
databasePort: database.port.apply(String),`
|
|
8857
|
+
: ''}
|
|
8149
8858
|
eventBusArn: eventBus.arn,
|
|
8150
8859
|
eventBusName: eventBus.name,
|
|
8151
8860
|
hasPublicDomain: rootDomain ? 'true' : 'false',
|
|
@@ -8156,19 +8865,22 @@ export function foundation(
|
|
|
8156
8865
|
routerDistributionId: router.distributionID,
|
|
8157
8866
|
routerUrl: router.url,
|
|
8158
8867
|
schemaVersion: String(FOUNDATION_SSM_SCHEMA_VERSION),
|
|
8159
|
-
vpcId: vpc.id,
|
|
8868
|
+
${foundationPlan.resources.network ? ' vpcId: vpc.id,' : ''}
|
|
8160
8869
|
});
|
|
8161
8870
|
|
|
8162
8871
|
return {
|
|
8163
8872
|
accountBudgetName: accountBudget?.name,
|
|
8164
8873
|
alertTopicArn: alertTopic.arn,
|
|
8165
|
-
capabilities:
|
|
8166
|
-
clusterId: cluster.id,
|
|
8167
|
-
contractVersion: '
|
|
8168
|
-
|
|
8874
|
+
capabilities: ${JSON.stringify(foundationPlan.descriptor.capabilities)},
|
|
8875
|
+
${foundationPlan.resources.containerCompute ? ' clusterId: cluster.id,' : ''}
|
|
8876
|
+
contractVersion: '${foundationPlan.descriptor.contractVersion}',
|
|
8877
|
+
costAnomalySubscriptionArn: accountCostAnomalySubscription?.arn,
|
|
8878
|
+
${foundationPlan.resources.postgres ? ' databaseCapacityAlarmName: databaseCapacityAlarm.name,' : ''}
|
|
8169
8879
|
dashboardName: dashboard.dashboardName,
|
|
8170
|
-
|
|
8171
|
-
|
|
8880
|
+
${foundationPlan.resources.postgres
|
|
8881
|
+
? ` databaseClusterId: database.id,
|
|
8882
|
+
databaseEventSubscriptionId: databaseEvents?.id,`
|
|
8883
|
+
: ''}
|
|
8172
8884
|
eventBusName: eventBus.name,
|
|
8173
8885
|
identityUserPoolId: userPool.id,
|
|
8174
8886
|
identityTokenEndpoint,
|
|
@@ -8176,33 +8888,68 @@ export function foundation(
|
|
|
8176
8888
|
productionFoundationProfile: productionConfiguration?.profile,
|
|
8177
8889
|
rootDomain,
|
|
8178
8890
|
routerUrl: router.url,
|
|
8179
|
-
vpcId: vpc.id,
|
|
8891
|
+
${foundationPlan.resources.network ? ' vpcId: vpc.id,' : ''}
|
|
8180
8892
|
};
|
|
8181
8893
|
}
|
|
8182
8894
|
`,
|
|
8183
8895
|
'packages/foundation/infra/index.ts': `import { foundation } from './foundation.js';
|
|
8184
|
-
import type { FoundationWorkloadRolePreflight } from './workload-roles/preflight.js';
|
|
8185
8896
|
|
|
8186
|
-
export function app(
|
|
8187
|
-
|
|
8188
|
-
return {
|
|
8189
|
-
...resources,
|
|
8190
|
-
workloadRoleManifestDigest:
|
|
8191
|
-
workloadRoles?.registry.manifestDigest ?? 'local-dev-ephemeral',
|
|
8192
|
-
};
|
|
8897
|
+
export function app() {
|
|
8898
|
+
return foundation();
|
|
8193
8899
|
}
|
|
8194
8900
|
`,
|
|
8195
8901
|
'packages/foundation/performance/account-cost-policy.json': json({
|
|
8196
8902
|
actualNotificationThresholdPercent: 80,
|
|
8197
8903
|
forecastNotificationThresholdPercent: 100,
|
|
8198
8904
|
kind: 'ebk-foundation-account-cost-policy',
|
|
8199
|
-
monthlyMaximumUsd: 1_000,
|
|
8905
|
+
monthlyMaximumUsd: deploymentProfile === 'starter' ? 250 : 1_000,
|
|
8200
8906
|
owner: 'replace-with-owner',
|
|
8201
8907
|
reviewAt: null,
|
|
8202
8908
|
schemaVersion: 1,
|
|
8203
8909
|
status: 'example',
|
|
8204
8910
|
workspace: name,
|
|
8205
8911
|
}),
|
|
8912
|
+
'packages/foundation/security/router-waf.json': json({
|
|
8913
|
+
deploymentProfile,
|
|
8914
|
+
kind: 'ebk-router-waf-policy',
|
|
8915
|
+
logging: {
|
|
8916
|
+
enabled: false,
|
|
8917
|
+
retention: '1 week',
|
|
8918
|
+
},
|
|
8919
|
+
rateLimitPerIp: 2_000,
|
|
8920
|
+
schemaVersion: 1,
|
|
8921
|
+
stages: {
|
|
8922
|
+
development: {
|
|
8923
|
+
access: 'allowlist',
|
|
8924
|
+
allowedCidrs: [],
|
|
8925
|
+
mode: 'block',
|
|
8926
|
+
},
|
|
8927
|
+
production: {
|
|
8928
|
+
access: 'public',
|
|
8929
|
+
allowedCidrs: [],
|
|
8930
|
+
mode: 'block',
|
|
8931
|
+
},
|
|
8932
|
+
...(deploymentProfile === 'starter'
|
|
8933
|
+
? {}
|
|
8934
|
+
: {
|
|
8935
|
+
staging: {
|
|
8936
|
+
access: 'allowlist',
|
|
8937
|
+
allowedCidrs: [],
|
|
8938
|
+
mode: 'block',
|
|
8939
|
+
},
|
|
8940
|
+
}),
|
|
8941
|
+
},
|
|
8942
|
+
}),
|
|
8943
|
+
'packages/foundation/security/router-waf-blocking-rehearsal.json': json({
|
|
8944
|
+
kind: 'ebk-router-waf-blocking-rehearsal',
|
|
8945
|
+
schemaVersion: 4,
|
|
8946
|
+
status: 'pending',
|
|
8947
|
+
}),
|
|
8948
|
+
'packages/foundation/security/router-waf-live-evidence.json': json({
|
|
8949
|
+
kind: 'ebk-router-waf-live-evidence',
|
|
8950
|
+
schemaVersion: 4,
|
|
8951
|
+
status: 'pending',
|
|
8952
|
+
}),
|
|
8206
8953
|
'packages/foundation/configuration/declarations.json': json({
|
|
8207
8954
|
project: 'foundation',
|
|
8208
8955
|
schemaVersion: 1,
|
|
@@ -8235,29 +8982,87 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
|
|
|
8235
8982
|
version: '4.17.1',
|
|
8236
8983
|
},
|
|
8237
8984
|
policy: {
|
|
8985
|
+
adoptedImports: [],
|
|
8238
8986
|
additiveLowRiskTypes: [
|
|
8239
8987
|
'aws:cloudwatch/dashboard:Dashboard',
|
|
8240
8988
|
'aws:cloudwatch/logGroup:LogGroup',
|
|
8241
8989
|
'aws:cloudwatch/metricAlarm:MetricAlarm',
|
|
8242
8990
|
],
|
|
8991
|
+
additiveReviewedTypes: [
|
|
8992
|
+
'aws:cloudwatch/eventArchive:EventArchive',
|
|
8993
|
+
'aws:cloudwatch/eventBus:EventBus',
|
|
8994
|
+
'aws:cognito/userPoolDomain:UserPoolDomain',
|
|
8995
|
+
'aws:ec2/defaultSecurityGroup:DefaultSecurityGroup',
|
|
8996
|
+
'aws:ec2/internetGateway:InternetGateway',
|
|
8997
|
+
'aws:ec2/routeTable:RouteTable',
|
|
8998
|
+
'aws:ec2/routeTableAssociation:RouteTableAssociation',
|
|
8999
|
+
'aws:ec2/securityGroup:SecurityGroup',
|
|
9000
|
+
'aws:ec2/subnet:Subnet',
|
|
9001
|
+
'aws:ec2/vpc:Vpc',
|
|
9002
|
+
'aws:ecs/cluster:Cluster',
|
|
9003
|
+
'aws:ecs/clusterCapacityProviders:ClusterCapacityProviders',
|
|
9004
|
+
'aws:rds/clusterParameterGroup:ClusterParameterGroup',
|
|
9005
|
+
'aws:rds/eventSubscription:EventSubscription',
|
|
9006
|
+
'aws:rds/parameterGroup:ParameterGroup',
|
|
9007
|
+
'aws:rds/subnetGroup:SubnetGroup',
|
|
9008
|
+
'aws:servicediscovery/privateDnsNamespace:PrivateDnsNamespace',
|
|
9009
|
+
'aws:sns/topic:Topic',
|
|
9010
|
+
'aws:sns/topicPolicy:TopicPolicy',
|
|
9011
|
+
'aws:wafv2/webAclLoggingConfiguration:WebAclLoggingConfiguration',
|
|
9012
|
+
'pulumi-nodejs:dynamic:Resource',
|
|
9013
|
+
'pulumi:providers:aws',
|
|
9014
|
+
'pulumi:providers:pulumi-nodejs',
|
|
9015
|
+
'pulumi:providers:random',
|
|
9016
|
+
'pulumi:pulumi:Stack',
|
|
9017
|
+
'sst:aws:Aurora',
|
|
9018
|
+
'sst:aws:Bus',
|
|
9019
|
+
'sst:aws:CDN',
|
|
9020
|
+
'sst:aws:Cluster',
|
|
9021
|
+
'sst:aws:CognitoUserPool',
|
|
9022
|
+
'sst:aws:Router',
|
|
9023
|
+
'sst:aws:SnsTopic',
|
|
9024
|
+
'sst:aws:Vpc',
|
|
9025
|
+
'sst:sst:LinkRef',
|
|
9026
|
+
'sst:sst:Version',
|
|
9027
|
+
],
|
|
8243
9028
|
costOrQuotaTypes: [
|
|
8244
9029
|
'aws:apigatewayv2/api:Api',
|
|
8245
9030
|
'aws:budgets/budget:Budget',
|
|
9031
|
+
'aws:cloudfront/cachePolicy:CachePolicy',
|
|
8246
9032
|
'aws:cloudfront/distribution:Distribution',
|
|
9033
|
+
'aws:cloudfront/function:Function',
|
|
9034
|
+
'aws:cloudfront/keyValueStore:KeyValueStore',
|
|
9035
|
+
'aws:costexplorer/anomalyMonitor:AnomalyMonitor',
|
|
9036
|
+
'aws:costexplorer/anomalySubscription:AnomalySubscription',
|
|
8247
9037
|
'aws:cognito/userPool:UserPool',
|
|
9038
|
+
'aws:ec2/eip:Eip',
|
|
9039
|
+
'aws:ec2/eipAssociation:EipAssociation',
|
|
9040
|
+
'aws:ec2/instance:Instance',
|
|
8248
9041
|
'aws:ec2/natGateway:NatGateway',
|
|
8249
9042
|
'aws:rds/cluster:Cluster',
|
|
9043
|
+
'aws:rds/clusterInstance:ClusterInstance',
|
|
9044
|
+
'aws:wafv2/ipSet:IpSet',
|
|
9045
|
+
'aws:wafv2/webAcl:WebAcl',
|
|
8250
9046
|
],
|
|
8251
9047
|
failClosedOnUnknown: true,
|
|
8252
9048
|
protectedPlanAuthority: 'deploy-role-residual',
|
|
8253
9049
|
standingPolicyRiskClasses: ['no-change', 'additive-low-risk'],
|
|
8254
9050
|
statefulTypes: [
|
|
9051
|
+
'aws:cloudwatch/eventArchive:EventArchive',
|
|
9052
|
+
'aws:cloudwatch/eventBus:EventBus',
|
|
8255
9053
|
'aws:cognito/userPool:UserPool',
|
|
8256
9054
|
'aws:dynamodb/table:Table',
|
|
8257
9055
|
'aws:events/eventBus:EventBus',
|
|
8258
9056
|
'aws:kms/key:Key',
|
|
8259
9057
|
'aws:rds/cluster:Cluster',
|
|
8260
9058
|
'aws:s3/bucket:Bucket',
|
|
9059
|
+
'aws:secretsmanager/secret:Secret',
|
|
9060
|
+
'aws:secretsmanager/secretVersion:SecretVersion',
|
|
9061
|
+
'aws:sns/topic:Topic',
|
|
9062
|
+
'aws:ssm/parameter:Parameter',
|
|
9063
|
+
'aws:wafv2/ipSet:IpSet',
|
|
9064
|
+
'aws:wafv2/webAcl:WebAcl',
|
|
9065
|
+
'random:index/randomPassword:RandomPassword',
|
|
8261
9066
|
],
|
|
8262
9067
|
},
|
|
8263
9068
|
project: 'foundation',
|
|
@@ -8287,10 +9092,6 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
|
|
|
8287
9092
|
'verify',
|
|
8288
9093
|
{ target: 'profile-preflight', params: 'forward' },
|
|
8289
9094
|
],
|
|
8290
|
-
outputs: [
|
|
8291
|
-
`{projectRoot}/.ebk/evidence/infrastructure-convergence.json`,
|
|
8292
|
-
`{projectRoot}/.ebk/evidence/infrastructure-mutation-binding.json`,
|
|
8293
|
-
],
|
|
8294
9095
|
options: {
|
|
8295
9096
|
cwd: 'packages/foundation',
|
|
8296
9097
|
stage: 'development',
|
|
@@ -8307,15 +9108,7 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
|
|
|
8307
9108
|
'infrastructure-plan': {
|
|
8308
9109
|
cache: false,
|
|
8309
9110
|
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
9111
|
options: {
|
|
8318
|
-
action: 'deploy',
|
|
8319
9112
|
cwd: 'packages/foundation',
|
|
8320
9113
|
stage: 'development',
|
|
8321
9114
|
stageMode: 'foundation',
|
|
@@ -8324,10 +9117,6 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
|
|
|
8324
9117
|
refresh: {
|
|
8325
9118
|
executor: '@empire-builder-kit/nx:refresh',
|
|
8326
9119
|
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
9120
|
options: {
|
|
8332
9121
|
cwd: 'packages/foundation',
|
|
8333
9122
|
stage: 'development',
|
|
@@ -8337,10 +9126,6 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
|
|
|
8337
9126
|
remove: {
|
|
8338
9127
|
executor: '@empire-builder-kit/nx:remove',
|
|
8339
9128
|
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
9129
|
options: {
|
|
8345
9130
|
cwd: 'packages/foundation',
|
|
8346
9131
|
stage: 'development',
|
|
@@ -8355,6 +9140,13 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
|
|
|
8355
9140
|
executor: '@empire-builder-kit/nx:foundation-profile-preflight',
|
|
8356
9141
|
options: { stage: 'development' },
|
|
8357
9142
|
},
|
|
9143
|
+
'router-waf-rehearsal': {
|
|
9144
|
+
cache: false,
|
|
9145
|
+
executor: '@empire-builder-kit/nx:router-waf-rehearsal',
|
|
9146
|
+
outputs: [
|
|
9147
|
+
`{projectRoot}/security/router-waf-blocking-rehearsal.json`,
|
|
9148
|
+
],
|
|
9149
|
+
},
|
|
8358
9150
|
typecheck: {
|
|
8359
9151
|
executor: 'nx:run-commands',
|
|
8360
9152
|
options: {
|
|
@@ -8382,6 +9174,19 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
|
|
|
8382
9174
|
}),
|
|
8383
9175
|
'packages/foundation/scripts/verify-foundation.mjs': `import { readFileSync } from 'node:fs';
|
|
8384
9176
|
|
|
9177
|
+
import {
|
|
9178
|
+
normalizeRouterWafBlockingRehearsal,
|
|
9179
|
+
normalizeRouterWafPolicy,
|
|
9180
|
+
reconcileRouterWafBlockingRehearsal,
|
|
9181
|
+
routerWafContractDigest,
|
|
9182
|
+
} from '@empire-builder-kit/nx/foundation';
|
|
9183
|
+
import { createStageAuthorityIdentity } from '@empire-builder-kit/nx/aws-authority';
|
|
9184
|
+
import {
|
|
9185
|
+
configuredDeploymentAccountForStage,
|
|
9186
|
+
configuredDeploymentRegion,
|
|
9187
|
+
productionRehearsalStage,
|
|
9188
|
+
} from '@empire-builder-kit/nx/repository-policy';
|
|
9189
|
+
|
|
8385
9190
|
const descriptor = JSON.parse(
|
|
8386
9191
|
readFileSync(new URL('../contracts/foundation.json', import.meta.url), 'utf8'),
|
|
8387
9192
|
);
|
|
@@ -8397,35 +9202,92 @@ const frontendSecurityHeaders = JSON.parse(
|
|
|
8397
9202
|
'utf8',
|
|
8398
9203
|
),
|
|
8399
9204
|
);
|
|
8400
|
-
const
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
'
|
|
8410
|
-
|
|
9205
|
+
const routerWafPolicy = normalizeRouterWafPolicy(
|
|
9206
|
+
JSON.parse(
|
|
9207
|
+
readFileSync(
|
|
9208
|
+
new URL('../security/router-waf.json', import.meta.url),
|
|
9209
|
+
'utf8',
|
|
9210
|
+
),
|
|
9211
|
+
),
|
|
9212
|
+
);
|
|
9213
|
+
const repositoryPolicy = JSON.parse(
|
|
9214
|
+
readFileSync(new URL('../../../ebk.repository-policy.json', import.meta.url), 'utf8'),
|
|
9215
|
+
);
|
|
9216
|
+
const workspaceManifest = JSON.parse(
|
|
9217
|
+
readFileSync(new URL('../../../package.json', import.meta.url), 'utf8'),
|
|
9218
|
+
);
|
|
9219
|
+
const rehearsalReceipt = JSON.parse(
|
|
9220
|
+
readFileSync(
|
|
9221
|
+
new URL(
|
|
9222
|
+
'../security/router-waf-blocking-rehearsal.json',
|
|
9223
|
+
import.meta.url,
|
|
9224
|
+
),
|
|
9225
|
+
'utf8',
|
|
9226
|
+
),
|
|
9227
|
+
);
|
|
9228
|
+
const normalizedRehearsal = normalizeRouterWafBlockingRehearsal(rehearsalReceipt);
|
|
9229
|
+
if (normalizedRehearsal) {
|
|
9230
|
+
const rehearsalStage = productionRehearsalStage(repositoryPolicy.deploymentProfile);
|
|
9231
|
+
const rehearsalAccount = configuredDeploymentAccountForStage(
|
|
9232
|
+
repositoryPolicy.deploymentProfile,
|
|
9233
|
+
repositoryPolicy.deploymentAccounts,
|
|
9234
|
+
rehearsalStage,
|
|
9235
|
+
);
|
|
9236
|
+
const rehearsalRegion = configuredDeploymentRegion(repositoryPolicy.deploymentRegion);
|
|
9237
|
+
reconcileRouterWafBlockingRehearsal({
|
|
9238
|
+
evidence: JSON.parse(
|
|
9239
|
+
readFileSync(
|
|
9240
|
+
new URL('../security/router-waf-live-evidence.json', import.meta.url),
|
|
9241
|
+
'utf8',
|
|
9242
|
+
),
|
|
9243
|
+
),
|
|
9244
|
+
expected: {
|
|
9245
|
+
accountId: rehearsalAccount,
|
|
9246
|
+
deploymentRoleArn: createStageAuthorityIdentity({
|
|
9247
|
+
accountId: rehearsalAccount,
|
|
9248
|
+
partition: rehearsalRegion.startsWith('us-gov-') ? 'aws-us-gov' : 'aws',
|
|
9249
|
+
region: rehearsalRegion,
|
|
9250
|
+
stage: rehearsalStage,
|
|
9251
|
+
}).deploymentRoleArn,
|
|
9252
|
+
region: rehearsalRegion,
|
|
9253
|
+
stage: rehearsalStage,
|
|
9254
|
+
wafContractDigest: routerWafContractDigest(routerWafPolicy),
|
|
9255
|
+
workspace: workspaceManifest.name,
|
|
9256
|
+
},
|
|
9257
|
+
policy: routerWafPolicy,
|
|
9258
|
+
receipt: normalizedRehearsal,
|
|
9259
|
+
});
|
|
9260
|
+
}
|
|
9261
|
+
const expectedCapabilities = ${JSON.stringify(foundationPlan.descriptor.capabilities)};
|
|
8411
9262
|
|
|
8412
|
-
if (descriptor.contractVersion !== '
|
|
8413
|
-
throw new Error('Foundation contractVersion must be
|
|
9263
|
+
if (descriptor.contractVersion !== '2.0.0') {
|
|
9264
|
+
throw new Error('Foundation contractVersion must be 2.0.0.');
|
|
9265
|
+
}
|
|
9266
|
+
if (
|
|
9267
|
+
JSON.stringify(Object.keys(routerWafPolicy.stages).sort()) !==
|
|
9268
|
+
JSON.stringify(${JSON.stringify([...activeStages].sort())}) ||
|
|
9269
|
+
routerWafPolicy.stages.development.access !== 'allowlist' ||
|
|
9270
|
+
${deploymentProfile !== 'starter' ? "routerWafPolicy.stages.staging?.access !== 'allowlist' ||" : ''}
|
|
9271
|
+
routerWafPolicy.stages.production.mode !== 'block'
|
|
9272
|
+
) {
|
|
9273
|
+
throw new Error('Foundation Router WAF stage defaults are invalid.');
|
|
8414
9274
|
}
|
|
8415
9275
|
if (JSON.stringify(descriptor.capabilities) !== JSON.stringify(expectedCapabilities)) {
|
|
8416
|
-
throw new Error('Foundation capability descriptor does not match
|
|
9276
|
+
throw new Error('Foundation capability descriptor does not match its generated resource declaration.');
|
|
8417
9277
|
}
|
|
8418
9278
|
if (
|
|
8419
|
-
costs.schemaVersion !==
|
|
9279
|
+
costs.schemaVersion !== 3 ||
|
|
9280
|
+
costs.aurora?.declared !== ${foundationPlan.resources.postgres} ||
|
|
8420
9281
|
costs.aurora?.engineVersion !== '${index_js_4.FOUNDATION_AURORA_ENGINE_VERSION}' ||
|
|
8421
9282
|
costs.productionProfileSource !== 'ebk.repository-policy.json' ||
|
|
8422
9283
|
costs.productionProfiles?.standard?.aurora?.min !== '2 ACU' ||
|
|
8423
|
-
costs.productionProfiles
|
|
9284
|
+
JSON.stringify(costs.productionProfiles) !== JSON.stringify(${JSON.stringify(index_js_4.PRODUCTION_FOUNDATION_PROFILE_CONTRACTS)}) ||
|
|
8424
9285
|
!costs.stages?.development ||
|
|
8425
|
-
|
|
9286
|
+
JSON.stringify(Object.keys(costs.stages ?? {}).sort()) !==
|
|
9287
|
+
JSON.stringify(${JSON.stringify([...activeStages].sort())}) ||
|
|
8426
9288
|
costs.stages?.production?.dataRetention !== 'retained-and-protected'
|
|
8427
9289
|
) {
|
|
8428
|
-
throw new Error('Foundation cost manifest does not match the
|
|
9290
|
+
throw new Error('Foundation cost manifest does not match the declared capability and stage contract.');
|
|
8429
9291
|
}
|
|
8430
9292
|
const localHeaders =
|
|
8431
9293
|
frontendSecurityHeaders.profiles?.['local-development']?.headers ?? [];
|
|
@@ -8462,9 +9324,9 @@ export {};
|
|
|
8462
9324
|
export default $config({
|
|
8463
9325
|
app(input: { stage?: string } | undefined) {
|
|
8464
9326
|
const stage = input?.stage ?? 'development';
|
|
8465
|
-
if (!
|
|
9327
|
+
if (!new Set<string>(${JSON.stringify(activeStages)}).has(stage)) {
|
|
8466
9328
|
throw new Error(
|
|
8467
|
-
'Foundation stage ' + stage + ' is
|
|
9329
|
+
'Foundation stage ' + stage + ' is inactive for the ${deploymentProfile} deployment profile.',
|
|
8468
9330
|
);
|
|
8469
9331
|
}
|
|
8470
9332
|
const production = stage === 'production';
|
|
@@ -8476,16 +9338,15 @@ export default $config({
|
|
|
8476
9338
|
providers: {
|
|
8477
9339
|
aws: {
|
|
8478
9340
|
defaultTags: {
|
|
9341
|
+
// SST contributes sst:app and sst:stage. Keep the provider baseline
|
|
9342
|
+
// at eight tags so generated S3 asset objects remain within AWS's
|
|
9343
|
+
// hard ten-tag object limit. Richer performance tags stay explicit.
|
|
8479
9344
|
tags: {
|
|
8480
9345
|
'ebk:cost-allocation': 'foundation',
|
|
8481
9346
|
'ebk:cost-scope': '${name}/foundation/' + stage,
|
|
8482
9347
|
'ebk:framework-version': '${frameworkVersion}',
|
|
8483
9348
|
'ebk:managed-by': 'empire-builder-kit',
|
|
8484
9349
|
'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
9350
|
'ebk:slice': 'foundation',
|
|
8490
9351
|
'ebk:stage': stage,
|
|
8491
9352
|
'ebk:workspace': '${name}',
|
|
@@ -8497,15 +9358,8 @@ export default $config({
|
|
|
8497
9358
|
};
|
|
8498
9359
|
},
|
|
8499
9360
|
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
9361
|
const { app } = await import('./infra/index.js');
|
|
8508
|
-
return app(
|
|
9362
|
+
return app();
|
|
8509
9363
|
},
|
|
8510
9364
|
});
|
|
8511
9365
|
`,
|
|
@@ -8520,6 +9374,7 @@ export default $config({
|
|
|
8520
9374
|
],
|
|
8521
9375
|
exclude: ['.sst', 'dist', 'node_modules'],
|
|
8522
9376
|
}),
|
|
9377
|
+
[dependency_overrides_js_1.DEPENDENCY_OVERRIDES_PATH]: json(dependencyOverrides),
|
|
8523
9378
|
'pnpm-workspace.yaml': `packages:
|
|
8524
9379
|
- 'packages/*'
|
|
8525
9380
|
- 'packages/*/apps/*'
|
|
@@ -8554,6 +9409,7 @@ supportedArchitectures:
|
|
|
8554
9409
|
strictDepBuilds: true
|
|
8555
9410
|
allowBuilds:
|
|
8556
9411
|
${index_js_6.FRAMEWORK_REVIEWED_BUILD_PACKAGES.map((packageName) => ` '${packageName}': true`).join('\n')}
|
|
9412
|
+
${(0, dependency_overrides_js_1.renderDependencyOverridesYaml)(dependencyOverrides)}
|
|
8557
9413
|
`,
|
|
8558
9414
|
'tsconfig.base.json': json({
|
|
8559
9415
|
compilerOptions: {
|
|
@@ -9376,7 +10232,9 @@ process.stdout.write(
|
|
|
9376
10232
|
);
|
|
9377
10233
|
`,
|
|
9378
10234
|
'tools/ebk-git-policy.mjs': `import { evaluateGitPolicy } from '@empire-builder-kit/nx/git-policy';
|
|
10235
|
+
import { deliveryLaneOf, readRepositoryOperatingPolicy } from '@empire-builder-kit/nx/repository-policy';
|
|
9379
10236
|
import { spawnSync } from 'node:child_process';
|
|
10237
|
+
const deliveryLane = deliveryLaneOf(readRepositoryOperatingPolicy(process.cwd()));
|
|
9380
10238
|
|
|
9381
10239
|
const git = (...args) => {
|
|
9382
10240
|
const result = spawnSync('git', args, { encoding: 'utf8', shell: false });
|
|
@@ -9396,32 +10254,40 @@ if (!['pull_request', 'push', 'workflow_dispatch'].includes(eventName)) {
|
|
|
9396
10254
|
throw new Error('Unsupported or missing GITHUB_EVENT_NAME; Git policy fails closed.');
|
|
9397
10255
|
}
|
|
9398
10256
|
|
|
9399
|
-
const request = { eventName, ref: process.env.GITHUB_REF };
|
|
10257
|
+
const request = { deliveryLane, eventName, ref: process.env.GITHUB_REF };
|
|
9400
10258
|
if (eventName === 'pull_request' || eventName === 'push') {
|
|
9401
10259
|
git(
|
|
9402
10260
|
'fetch',
|
|
9403
10261
|
'--no-tags',
|
|
9404
10262
|
'origin',
|
|
9405
10263
|
'main:refs/remotes/origin/main',
|
|
9406
|
-
'develop:refs/remotes/origin/develop',
|
|
10264
|
+
...(deliveryLane === 'trunk' ? [] : ['develop:refs/remotes/origin/develop']),
|
|
9407
10265
|
);
|
|
9408
10266
|
}
|
|
9409
10267
|
if (eventName === 'pull_request') {
|
|
9410
10268
|
const baseRef = process.env.GITHUB_BASE_REF;
|
|
10269
|
+
const headRepository = process.env.EBK_HEAD_REPOSITORY;
|
|
9411
10270
|
const headRef = process.env.GITHUB_HEAD_REF;
|
|
9412
|
-
|
|
9413
|
-
|
|
10271
|
+
const repository = process.env.GITHUB_REPOSITORY;
|
|
10272
|
+
if (!baseRef || !headRepository || !headRef || !repository) {
|
|
10273
|
+
throw new Error('Pull-request branch and repository metadata is missing.');
|
|
9414
10274
|
}
|
|
9415
10275
|
Object.assign(request, {
|
|
9416
10276
|
baseIsAncestorOfHead: isAncestor('refs/remotes/origin/' + baseRef, 'HEAD'),
|
|
9417
10277
|
baseRef,
|
|
10278
|
+
headRepository,
|
|
9418
10279
|
headRef,
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
|
|
9422
|
-
|
|
10280
|
+
...(deliveryLane === 'trunk'
|
|
10281
|
+
? {}
|
|
10282
|
+
: {
|
|
10283
|
+
mainIsAncestorOfDevelop: isAncestor(
|
|
10284
|
+
'refs/remotes/origin/main',
|
|
10285
|
+
'refs/remotes/origin/develop',
|
|
10286
|
+
),
|
|
10287
|
+
}),
|
|
10288
|
+
repository,
|
|
9423
10289
|
});
|
|
9424
|
-
} else if (eventName === 'push') {
|
|
10290
|
+
} else if (eventName === 'push' && deliveryLane !== 'trunk') {
|
|
9425
10291
|
Object.assign(request, {
|
|
9426
10292
|
baseIsAncestorOfHead: isAncestor('refs/remotes/origin/develop', 'HEAD'),
|
|
9427
10293
|
mainIsAncestorOfDevelop: isAncestor(
|
|
@@ -9435,7 +10301,7 @@ const decision = evaluateGitPolicy(request);
|
|
|
9435
10301
|
process.stdout.write(JSON.stringify(decision, null, 2) + '\\n');
|
|
9436
10302
|
if (!decision.allowed) process.exitCode = 1;
|
|
9437
10303
|
`,
|
|
9438
|
-
'tools/workspace-health.mjs': `import {
|
|
10304
|
+
'tools/workspace-health.mjs': `import { readRepositoryOperatingPolicy } from '@empire-builder-kit/nx/repository-policy';
|
|
9439
10305
|
import { assertWorkMarkerReport, formatWorkMarkerReport, scanWorkMarkers } from '@empire-builder-kit/nx/work-markers';
|
|
9440
10306
|
import { createHash } from 'node:crypto';
|
|
9441
10307
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
@@ -9498,13 +10364,7 @@ if (
|
|
|
9498
10364
|
validateOwnership('.', workspace, 'Workspace');
|
|
9499
10365
|
validateOwnership('packages/foundation', foundation, 'Foundation');
|
|
9500
10366
|
|
|
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
|
-
}
|
|
10367
|
+
readRepositoryOperatingPolicy(process.cwd());
|
|
9508
10368
|
|
|
9509
10369
|
for (const name of readdirSync('packages')) {
|
|
9510
10370
|
if (name === 'foundation') continue;
|
|
@@ -9522,7 +10382,63 @@ assertWorkMarkerReport(markerReport);
|
|
|
9522
10382
|
process.stdout.write('Empire Builder Kit workspace source health is green.\\n');
|
|
9523
10383
|
`,
|
|
9524
10384
|
};
|
|
9525
|
-
Object.assign(files, SUPPLY_CHAIN_WORKSPACE_FILE_PLAN.files,
|
|
10385
|
+
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)({
|
|
10386
|
+
deploymentProfile,
|
|
10387
|
+
name,
|
|
10388
|
+
workspaceDeploymentId,
|
|
10389
|
+
}));
|
|
10390
|
+
Object.assign(files, (0, workspace_files_ecr_inspector_js_1.renderThinEcrInspectorWorkflow)({
|
|
10391
|
+
actionPins: {
|
|
10392
|
+
checkout: checkoutAction,
|
|
10393
|
+
configureAwsCredentials: configureAwsAction,
|
|
10394
|
+
downloadArtifact: downloadArtifactAction,
|
|
10395
|
+
pnpmSetup: pnpmAction,
|
|
10396
|
+
setupNode: setupNodeAction,
|
|
10397
|
+
uploadArtifact: uploadArtifactAction,
|
|
10398
|
+
},
|
|
10399
|
+
deploymentProfile,
|
|
10400
|
+
name,
|
|
10401
|
+
nodeVersion: tool_versions_js_1.WORKSPACE_NODE_VERSION,
|
|
10402
|
+
}), (0, workspace_files_thin_deploy_js_1.renderThinDeployWorkflowFilePlan)({
|
|
10403
|
+
deliveryLane,
|
|
10404
|
+
productionApprovalMode,
|
|
10405
|
+
actionPins: {
|
|
10406
|
+
cache: cacheAction,
|
|
10407
|
+
checkout: checkoutAction,
|
|
10408
|
+
configureAwsCredentials: configureAwsAction,
|
|
10409
|
+
downloadArtifact: downloadArtifactAction,
|
|
10410
|
+
pnpmSetup: pnpmAction,
|
|
10411
|
+
setupNode: setupNodeAction,
|
|
10412
|
+
uploadArtifact: uploadArtifactAction,
|
|
10413
|
+
},
|
|
10414
|
+
deploymentProfile,
|
|
10415
|
+
name,
|
|
10416
|
+
toolVersions: {
|
|
10417
|
+
node: tool_versions_js_1.WORKSPACE_NODE_VERSION,
|
|
10418
|
+
sst: tool_versions_js_1.WORKSPACE_SST_VERSION,
|
|
10419
|
+
},
|
|
10420
|
+
}).files, (0, workspace_files_thin_docs_js_1.renderThinLifecycleDocumentation)({
|
|
10421
|
+
deliveryLane,
|
|
10422
|
+
deploymentProfile,
|
|
10423
|
+
name,
|
|
10424
|
+
productionApprovalMode,
|
|
10425
|
+
}));
|
|
10426
|
+
const thinReleaseWorkflowPlan = (0, workspace_files_thin_release_js_1.renderThinReleaseWorkflowPlan)();
|
|
10427
|
+
Object.assign(files, thinReleaseWorkflowPlan.files);
|
|
10428
|
+
for (const obsoletePath of [
|
|
10429
|
+
...thinReleaseWorkflowPlan.obsoletePaths,
|
|
10430
|
+
// The trunk lane has no develop branch to sync main back into; trunk
|
|
10431
|
+
// promotion only ever fast-forwards main from develop, so there is
|
|
10432
|
+
// nothing to sync back either.
|
|
10433
|
+
...(trunkLane || trunkPromotion ? ['.github/workflows/sync-back.yml'] : []),
|
|
10434
|
+
'.github/workflows/performance-aggregate-counts.yml',
|
|
10435
|
+
'.github/workflows/performance-cost-actual.yml',
|
|
10436
|
+
'.github/workflows/performance-load.yml',
|
|
10437
|
+
'packages/foundation/operations/infrastructure-change.json',
|
|
10438
|
+
'tools/ebk-infrastructure-plan-summary.mjs',
|
|
10439
|
+
]) {
|
|
10440
|
+
delete files[obsoletePath];
|
|
10441
|
+
}
|
|
9526
10442
|
for (const [path, source] of Object.entries(files)) {
|
|
9527
10443
|
if (/^\.github\/workflows\/[^/]+\.ya?ml$/u.test(path)) {
|
|
9528
10444
|
files[path] = isolateOidcTokenBeforeAwsAuthority(source, configureAwsAction);
|