@empire-builder-kit/nx 1.0.0-rc.4 → 1.0.0-rc.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/executors.json +21 -1
- package/generators.json +10 -0
- package/package.json +31 -7
- package/src/aws/child-environment.d.ts +10 -0
- package/src/aws/child-environment.js +77 -0
- package/src/aws/identity.d.ts +13 -0
- package/src/aws/identity.js +41 -0
- package/src/aws-authority/index.d.ts +2 -0
- package/src/aws-authority/index.js +6 -0
- package/src/aws-authority/lambda-policy-ordering.d.ts +38 -0
- package/src/aws-authority/lambda-policy-ordering.js +56 -0
- package/src/aws-authority/stage-authority.d.ts +248 -0
- package/src/aws-authority/stage-authority.js +3558 -0
- package/src/blueprints/external-sandbox.js +21 -9
- package/src/blueprints/manifest.d.ts +1 -1
- package/src/blueprints/resolver.js +28 -15
- package/src/cli/bootstrap.d.ts +1 -1
- package/src/cli/deploy-runtime.d.ts +6 -0
- package/src/cli/deploy-runtime.js +58 -0
- package/src/cli/dev-command.d.ts +16 -4
- package/src/cli/dev-command.js +63 -0
- package/src/cli/main.d.ts +9 -1
- package/src/cli/main.js +139 -20
- package/src/cli/output.d.ts +1 -1
- package/src/cli/output.js +1 -1
- package/src/developer-agent/context.js +41 -9
- package/src/developer-agent/generator-planning.js +20 -6
- package/src/developer-agent/git-inspection.d.ts +6 -2
- package/src/developer-agent/git-inspection.js +78 -6
- package/src/developer-agent/model.d.ts +5 -3
- package/src/developer-agent/model.js +4 -4
- package/src/developer-agent/operation-registry.d.ts +5 -0
- package/src/developer-agent/operation-registry.js +286 -40
- package/src/developer-agent/planning.js +16 -13
- package/src/developer-agent/preview-changes.d.ts +2 -0
- package/src/developer-agent/preview-changes.js +14 -2
- package/src/developer-agent/preview-source.js +3 -0
- package/src/developer-agent/validation.js +25 -9
- package/src/developer-agent/workspace-inspection.js +60 -5
- package/src/executors/blueprint-trust/blueprint-trust.js +6 -0
- package/src/executors/deployment-plan/deployment-plan.d.ts +2 -0
- package/src/executors/deployment-plan/deployment-plan.js +57 -2
- package/src/executors/dev-doctor/dev-doctor.js +13 -6
- package/src/executors/dev-task-cleanup/dev-task-cleanup.d.ts +23 -0
- package/src/executors/dev-task-cleanup/dev-task-cleanup.js +145 -0
- package/src/executors/dev-task-cleanup/schema.json +9 -0
- package/src/executors/dev-task-cleanup/task-stack-absence.d.ts +27 -0
- package/src/executors/dev-task-cleanup/task-stack-absence.js +281 -0
- package/src/executors/dev-task-cleanup/task-stack-checkpoint.d.ts +12 -0
- package/src/executors/dev-task-cleanup/task-stack-checkpoint.js +92 -0
- package/src/executors/fleet/fleet.js +1 -1
- package/src/executors/foundation-preflight/foundation-preflight.d.ts +1 -0
- package/src/executors/foundation-preflight/foundation-preflight.js +6 -2
- package/src/executors/foundation-preflight/schema.json +5 -0
- package/src/executors/foundation-profile-preflight/foundation-profile-preflight.d.ts +13 -2
- package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js +95 -31
- package/src/executors/foundation-profile-preflight/production-database-safeguard.d.ts +65 -0
- package/src/executors/foundation-profile-preflight/production-database-safeguard.js +223 -0
- package/src/executors/foundation-profile-preflight/schema.json +15 -4
- package/src/executors/foundation-remove-preflight/foundation-remove-preflight.d.ts +11 -1
- package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js +70 -20
- package/src/executors/foundation-remove-preflight/schema.json +6 -1
- package/src/executors/github-bootstrap/github-bootstrap.d.ts +1 -0
- package/src/executors/github-bootstrap/github-bootstrap.js +3 -0
- package/src/executors/github-bootstrap/schema.json +7 -1
- package/src/executors/infrastructure-plan/infrastructure-plan.d.ts +29 -136
- package/src/executors/infrastructure-plan/infrastructure-plan.js +230 -3282
- package/src/executors/infrastructure-plan/schema.json +12 -52
- package/src/executors/local-deploy/federated-credential-transport.d.ts +48 -0
- package/src/executors/local-deploy/federated-credential-transport.js +252 -0
- package/src/executors/local-deploy/local-approval.d.ts +18 -0
- package/src/executors/local-deploy/local-approval.js +39 -0
- package/src/executors/local-deploy/local-deploy.d.ts +112 -0
- package/src/executors/local-deploy/local-deploy.js +1404 -0
- package/src/executors/local-deploy/schema.json +70 -0
- package/src/executors/refresh/refresh.js +2 -2
- package/src/executors/remove/remove.js +2 -2
- package/src/executors/router-waf-rehearsal/router-waf-rehearsal.d.ts +42 -0
- package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js +1229 -0
- package/src/executors/router-waf-rehearsal/schema.json +29 -0
- package/src/executors/secret-initialize/schema.json +42 -0
- package/src/executors/secret-initialize/secret-initialize.d.ts +34 -0
- package/src/executors/secret-initialize/secret-initialize.js +297 -0
- package/src/executors/secret-metadata/schema.json +5 -0
- package/src/executors/secret-metadata/secret-metadata.d.ts +16 -2
- package/src/executors/secret-metadata/secret-metadata.js +129 -30
- package/src/executors/sst-lifecycle/generated-secret-retirement.d.ts +53 -0
- package/src/executors/sst-lifecycle/generated-secret-retirement.js +192 -0
- package/src/executors/sst-lifecycle/governed-native-operation.d.ts +48 -0
- package/src/executors/sst-lifecycle/governed-native-operation.js +162 -0
- package/src/executors/sst-lifecycle/logging-preflight.d.ts +5 -0
- package/src/executors/sst-lifecycle/logging-preflight.js +23 -13
- package/src/executors/sst-lifecycle/run-sst-command.d.ts +40 -6
- package/src/executors/sst-lifecycle/run-sst-command.js +779 -70
- package/src/executors/sst-lifecycle/schema.json +8 -1
- package/src/executors/sst-lifecycle/secret-preflight.d.ts +2 -0
- package/src/executors/sst-lifecycle/secret-preflight.js +13 -7
- package/src/executors/sst-lifecycle/sst-environment-source.d.ts +33 -0
- package/src/executors/sst-lifecycle/sst-environment-source.js +267 -0
- package/src/executors/sst-lifecycle/task-unlock.d.ts +15 -0
- package/src/executors/sst-lifecycle/task-unlock.js +126 -0
- package/src/executors/unlock/unlock.js +2 -2
- package/src/foundation/capability-plan.d.ts +43 -0
- package/src/foundation/capability-plan.js +168 -0
- package/src/foundation/discovery.d.ts +14 -11
- package/src/foundation/discovery.js +32 -6
- package/src/foundation/index.d.ts +4 -0
- package/src/foundation/index.js +11 -0
- package/src/foundation/local-database-contract.d.ts +20 -0
- package/src/foundation/local-database-contract.js +262 -13
- package/src/foundation/local-private-smoke-access.d.ts +20 -0
- package/src/foundation/local-private-smoke-access.js +197 -0
- package/src/foundation/logging-preflight.js +1 -1
- package/src/foundation/logging.d.ts +3 -1
- package/src/foundation/logging.js +1 -1
- package/src/foundation/partial-state.d.ts +10 -0
- package/src/foundation/partial-state.js +21 -0
- package/src/foundation/preflight.js +37 -18
- package/src/foundation/private-smoke-access.d.ts +25 -0
- package/src/foundation/private-smoke-access.js +135 -0
- package/src/foundation/private-smoke-admission.d.ts +45 -0
- package/src/foundation/private-smoke-admission.js +180 -0
- package/src/foundation/private-smoke-control-plane.d.ts +18 -0
- package/src/foundation/private-smoke-control-plane.js +184 -0
- package/src/foundation/provision-database-bindings.d.ts +5 -0
- package/src/foundation/provision-database-bindings.js +41 -8
- package/src/foundation/provision-logical-database.js +15 -8
- package/src/foundation/requirements.d.ts +3 -3
- package/src/foundation/requirements.js +24 -12
- package/src/foundation/router-waf-rate-rehearsal.d.ts +97 -0
- package/src/foundation/router-waf-rate-rehearsal.js +210 -0
- package/src/foundation/router-waf.d.ts +149 -0
- package/src/foundation/router-waf.js +809 -0
- package/src/foundation/stages.d.ts +1 -0
- package/src/foundation/stages.js +11 -0
- package/src/generators/blueprint/blueprint.d.ts +5 -0
- package/src/generators/blueprint/blueprint.js +102 -0
- package/src/generators/blueprint/files.d.ts +6 -0
- package/src/generators/blueprint/files.js +217 -0
- package/src/generators/blueprint/schema.d.ts +4 -0
- package/src/generators/blueprint/schema.json +18 -0
- package/src/generators/formatting.d.ts +8 -0
- package/src/generators/formatting.js +141 -0
- package/src/generators/function/function.js +38 -17
- package/src/generators/logging-registry.d.ts +2 -0
- package/src/generators/logging-registry.js +22 -0
- package/src/generators/post-generation-install.js +2 -2
- package/src/generators/preset/dependency-overrides.d.ts +8 -0
- package/src/generators/preset/dependency-overrides.js +55 -0
- package/src/generators/preset/generator.js +43 -3
- package/src/generators/preset/schema.d.ts +10 -0
- package/src/generators/preset/schema.json +47 -0
- package/src/generators/preset/tool-versions.d.ts +2 -0
- package/src/generators/preset/tool-versions.js +3 -1
- package/src/generators/preset/workspace-files-adopter-skills.d.ts +7 -0
- package/src/generators/preset/workspace-files-adopter-skills.js +303 -0
- package/src/generators/preset/workspace-files-ci-acceptance.d.ts +1 -0
- package/src/generators/preset/workspace-files-ci-acceptance.js +791 -0
- package/src/generators/preset/workspace-files-ci-install.d.ts +2 -0
- package/src/generators/preset/workspace-files-ci-install.js +196 -0
- package/src/generators/preset/workspace-files-developer-agent.d.ts +2 -0
- package/src/generators/preset/workspace-files-developer-agent.js +90 -28
- package/src/generators/preset/workspace-files-ecr-inspector.d.ts +18 -0
- package/src/generators/preset/workspace-files-ecr-inspector.js +298 -0
- package/src/generators/preset/workspace-files-foundation-workload-roles.js +5 -2
- package/src/generators/preset/workspace-files-performance-cost.d.ts +2 -2
- package/src/generators/preset/workspace-files-performance-cost.js +8 -8
- package/src/generators/preset/workspace-files-performance-load.d.ts +1 -1
- package/src/generators/preset/workspace-files-performance-load.js +39 -25
- package/src/generators/preset/workspace-files-stage-authority.d.ts +6 -0
- package/src/generators/preset/workspace-files-stage-authority.js +518 -0
- package/src/generators/preset/workspace-files-thin-deploy.d.ts +55 -0
- package/src/generators/preset/workspace-files-thin-deploy.js +2473 -0
- package/src/generators/preset/workspace-files-thin-docs.d.ts +7 -0
- package/src/generators/preset/workspace-files-thin-docs.js +352 -0
- package/src/generators/preset/workspace-files-thin-release.d.ts +14 -0
- package/src/generators/preset/workspace-files-thin-release.js +26 -0
- package/src/generators/preset/workspace-files-workload-role-baseline.js +254 -42
- package/src/generators/preset/workspace-files.d.ts +18 -2
- package/src/generators/preset/workspace-files.js +1504 -608
- package/src/generators/repository-policy/repository-policy.js +235 -5
- package/src/generators/repository-policy/schema.d.ts +10 -0
- package/src/generators/repository-policy/schema.json +80 -4
- package/src/generators/repository-policy/tree-policy.d.ts +3 -0
- package/src/generators/repository-policy/tree-policy.js +25 -0
- package/src/generators/slice/built-in-plan-api-contract-files.js +42 -7
- package/src/generators/slice/built-in-plan-api-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-api-files.js +130 -414
- package/src/generators/slice/built-in-plan-async-work-files.js +166 -1969
- package/src/generators/slice/built-in-plan-authorization-files.js +92 -329
- package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js +60 -2
- package/src/generators/slice/built-in-plan-authorization-storage.d.ts +3 -0
- package/src/generators/slice/built-in-plan-authorization-storage.js +119 -0
- package/src/generators/slice/built-in-plan-background-work-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-background-work-files.js +99 -0
- package/src/generators/slice/built-in-plan-data-deletion-files.js +2 -830
- package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js +5 -31
- package/src/generators/slice/built-in-plan-data-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-data-files.js +163 -1196
- package/src/generators/slice/built-in-plan-data-lifecycle-files.js +53 -28
- package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js +37 -2
- package/src/generators/slice/built-in-plan-data-retention-job-files.js +7 -2443
- package/src/generators/slice/built-in-plan-data-retention-launcher-files.js +50 -8
- package/src/generators/slice/built-in-plan-data-rights-files.js +6 -4482
- package/src/generators/slice/built-in-plan-database-lifecycle-files.js +44 -25
- package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js +20 -1
- package/src/generators/slice/built-in-plan-foundation-files.d.ts +2 -1
- package/src/generators/slice/built-in-plan-foundation-files.js +39 -18
- package/src/generators/slice/built-in-plan-frontend-files.js +7 -3
- package/src/generators/slice/built-in-plan-infrastructure-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-infrastructure-files.js +933 -729
- package/src/generators/slice/built-in-plan-infrastructure-safety-files.js +67 -11
- package/src/generators/slice/built-in-plan-key-value-cleanup.d.ts +4 -0
- package/src/generators/slice/built-in-plan-key-value-cleanup.js +100 -0
- package/src/generators/slice/built-in-plan-key-value-consumer.d.ts +4 -0
- package/src/generators/slice/built-in-plan-key-value-consumer.js +151 -0
- package/src/generators/slice/built-in-plan-key-value-deletion.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-deletion.js +377 -0
- package/src/generators/slice/built-in-plan-key-value-delivery.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-delivery.js +137 -0
- package/src/generators/slice/built-in-plan-key-value-export.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-export.js +595 -0
- package/src/generators/slice/built-in-plan-key-value-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-files.js +31 -0
- package/src/generators/slice/built-in-plan-key-value-holds.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-holds.js +189 -0
- package/src/generators/slice/built-in-plan-key-value-infrastructure.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-infrastructure.js +102 -0
- package/src/generators/slice/built-in-plan-key-value-performance.d.ts +5 -0
- package/src/generators/slice/built-in-plan-key-value-performance.js +530 -0
- package/src/generators/slice/built-in-plan-key-value-provider.d.ts +4 -0
- package/src/generators/slice/built-in-plan-key-value-provider.js +366 -0
- package/src/generators/slice/built-in-plan-key-value-records.d.ts +3 -0
- package/src/generators/slice/built-in-plan-key-value-records.js +232 -0
- package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.js +103 -0
- package/src/generators/slice/built-in-plan-key-value-retention-job-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention-job-files.js +160 -0
- package/src/generators/slice/built-in-plan-key-value-retention-maintenance.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention-maintenance.js +329 -0
- package/src/generators/slice/built-in-plan-key-value-retention-worker.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention-worker.js +477 -0
- package/src/generators/slice/built-in-plan-key-value-retention.d.ts +2 -0
- package/src/generators/slice/built-in-plan-key-value-retention.js +184 -0
- package/src/generators/slice/built-in-plan-metadata-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-metadata-files.js +157 -28
- package/src/generators/slice/built-in-plan-native-runtime-authority-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-native-runtime-authority-files.js +324 -0
- package/src/generators/slice/built-in-plan-operations-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-operations-files.js +585 -97
- package/src/generators/slice/built-in-plan-performance-files.d.ts +2 -1
- package/src/generators/slice/built-in-plan-performance-files.js +220 -1074
- package/src/generators/slice/built-in-plan-project-files.js +141 -135
- package/src/generators/slice/built-in-plan-release-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-release-files.js +310 -70
- package/src/generators/slice/built-in-plan-reliability-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-reliability-files.js +279 -43
- package/src/generators/slice/built-in-plan-secret-runtime-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-secret-runtime-files.js +87 -76
- package/src/generators/slice/built-in-plan-shared.d.ts +4 -2
- package/src/generators/slice/built-in-plan-shared.js +41 -19
- package/src/generators/slice/built-in-plan-sql-events.d.ts +2 -0
- package/src/generators/slice/built-in-plan-sql-events.js +276 -0
- package/src/generators/slice/built-in-plan-testing-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-testing-files.js +304 -172
- package/src/generators/slice/built-in-plan-testing-persistence-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-testing-persistence-files.js +178 -0
- package/src/generators/slice/built-in-plan-thin-release-files.d.ts +11 -0
- package/src/generators/slice/built-in-plan-thin-release-files.js +162 -0
- package/src/generators/slice/built-in-plan-verification-files.js +76 -2
- package/src/generators/slice/built-in-plan-web-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-web-files.js +223 -16
- package/src/generators/slice/built-in-plan-workload-role-files.d.ts +1 -1
- package/src/generators/slice/built-in-plan-workload-role-files.js +143 -279
- package/src/generators/slice/built-in-plan.d.ts +11 -1
- package/src/generators/slice/built-in-plan.js +80 -29
- package/src/generators/slice/data-policy-review-seed.d.ts +2 -0
- package/src/generators/slice/data-policy-review-seed.js +22 -0
- package/src/generators/slice/deployed-smoke-readiness.d.ts +2 -0
- package/src/generators/slice/deployed-smoke-readiness.js +214 -0
- package/src/generators/slice/local-source-identity.d.ts +2 -0
- package/src/generators/slice/local-source-identity.js +31 -0
- package/src/generators/slice/persistence-contract.d.ts +14 -0
- package/src/generators/slice/persistence-contract.js +32 -0
- package/src/generators/slice/slice.js +61 -50
- package/src/generators/sql/schema.d.ts +7 -0
- package/src/generators/sql/schema.json +38 -0
- package/src/generators/sql/sql.d.ts +6 -0
- package/src/generators/sql/sql.js +53 -0
- package/src/generators/upgrade/sql-seeded-reconciliation.d.ts +2 -0
- package/src/generators/upgrade/sql-seeded-reconciliation.js +170 -0
- package/src/generators/upgrade/upgrade.d.ts +15 -1
- package/src/generators/upgrade/upgrade.js +435 -44
- package/src/generators/workload/performance-registration.d.ts +1 -0
- package/src/generators/workload/performance-registration.js +9 -11
- package/src/generators/workload/service-performance-files.d.ts +1 -0
- package/src/generators/workload/service-performance-files.js +27 -7
- package/src/generators/workload/workload.d.ts +1 -1
- package/src/generators/workload/workload.js +105 -29
- package/src/git-policy/action-pins.d.ts +5 -0
- package/src/git-policy/action-pins.js +5 -0
- package/src/git-policy/github-bootstrap.d.ts +39 -8
- package/src/git-policy/github-bootstrap.js +329 -175
- package/src/git-policy/routing.d.ts +7 -1
- package/src/git-policy/routing.js +62 -1
- package/src/index.d.ts +1 -0
- package/src/index.js +1 -0
- package/src/infrastructure-change/local-capability.d.ts +73 -0
- package/src/infrastructure-change/local-capability.js +114 -0
- package/src/infrastructure-change/model.d.ts +25 -4
- package/src/infrastructure-change/model.js +1 -0
- package/src/infrastructure-change/normalize.d.ts +4 -0
- package/src/infrastructure-change/normalize.js +47 -7
- package/src/infrastructure-change/plan.d.ts +30 -3
- package/src/infrastructure-change/plan.js +323 -13
- package/src/infrastructure-change/risk.d.ts +3 -1
- package/src/infrastructure-change/risk.js +96 -16
- package/src/local-dev/browser-verification.d.ts +37 -0
- package/src/local-dev/browser-verification.js +281 -0
- package/src/local-dev/contracts.d.ts +10 -1
- package/src/local-dev/contracts.js +114 -13
- package/src/local-dev/dev-command-core.js +24 -5
- package/src/local-dev/docker-credential-health.d.ts +14 -0
- package/src/local-dev/docker-credential-health.js +94 -0
- package/src/local-dev/environment-inspector.d.ts +7 -0
- package/src/local-dev/environment-inspector.js +42 -15
- package/src/local-dev/environment-state.js +2 -2
- package/src/local-dev/errors.d.ts +1 -1
- package/src/local-dev/errors.js +2 -0
- package/src/local-dev/fleet-client.js +2 -2
- package/src/local-dev/fleet-configuration.js +57 -25
- package/src/local-dev/fleet-inventory.js +46 -18
- package/src/local-dev/fleet-ipc.d.ts +7 -0
- package/src/local-dev/fleet-ipc.js +14 -2
- package/src/local-dev/fleet-launcher.d.ts +6 -0
- package/src/local-dev/fleet-launcher.js +14 -2
- package/src/local-dev/fleet-logs.js +50 -22
- package/src/local-dev/fleet-process.d.ts +20 -1
- package/src/local-dev/fleet-process.js +197 -41
- package/src/local-dev/fleet-runner-evidence.d.ts +5 -0
- package/src/local-dev/fleet-runner-evidence.js +56 -0
- package/src/local-dev/fleet-runtime.js +268 -229
- package/src/local-dev/fleet-session.d.ts +2 -0
- package/src/local-dev/fleet-session.js +28 -11
- package/src/local-dev/fleet-slice-runner-daemon.js +4 -0
- package/src/local-dev/fleet-state.d.ts +5 -2
- package/src/local-dev/fleet-state.js +25 -4
- package/src/local-dev/fleet-supervisor.d.ts +3 -0
- package/src/local-dev/fleet-supervisor.js +66 -11
- package/src/local-dev/fleet-web-start.d.ts +12 -0
- package/src/local-dev/fleet-web-start.js +48 -0
- package/src/local-dev/identity.js +3 -14
- package/src/local-dev/index.d.ts +1 -0
- package/src/local-dev/index.js +1 -0
- package/src/local-dev/native-completion.d.ts +9 -0
- package/src/local-dev/native-completion.js +130 -0
- package/src/local-dev/nx-workspace.d.ts +4 -2
- package/src/local-dev/nx-workspace.js +11 -6
- package/src/local-dev/personal-stage.d.ts +4 -5
- package/src/local-dev/personal-stage.js +6 -54
- package/src/local-dev/port-claims.d.ts +13 -0
- package/src/local-dev/port-claims.js +46 -0
- package/src/local-dev/ports.d.ts +14 -2
- package/src/local-dev/ports.js +78 -3
- package/src/local-dev/private-state.d.ts +11 -0
- package/src/local-dev/private-state.js +146 -40
- package/src/local-dev/process-group.d.ts +4 -0
- package/src/local-dev/process-group.js +29 -0
- package/src/local-dev/process-identity.d.ts +8 -0
- package/src/local-dev/process-identity.js +31 -1
- package/src/local-dev/proxy.js +11 -12
- package/src/local-dev/runtime-paths.js +1 -1
- package/src/local-dev/slice-claim.js +1 -1
- package/src/local-dev/slice-runner.d.ts +15 -6
- package/src/local-dev/slice-runner.js +189 -54
- package/src/local-dev/task-attachment.d.ts +17 -0
- package/src/local-dev/task-attachment.js +73 -0
- package/src/local-dev/task-authority.d.ts +10 -0
- package/src/local-dev/task-authority.js +27 -0
- package/src/local-dev/task-identity.d.ts +33 -0
- package/src/local-dev/task-identity.js +418 -0
- package/src/local-dev/task-native-writer.d.ts +18 -0
- package/src/local-dev/task-native-writer.js +329 -0
- package/src/local-dev/tls.js +8 -2
- package/src/local-dev/web-launch.d.ts +9 -0
- package/src/local-dev/web-launch.js +130 -0
- package/src/ownership/composition.d.ts +2 -1
- package/src/ownership/composition.js +1 -0
- package/src/performance/cost-collection.d.ts +1 -1
- package/src/performance/cost-collection.js +4 -2
- package/src/performance/cost-controls.d.ts +8 -4
- package/src/performance/cost-controls.js +39 -28
- package/src/performance/evidence.d.ts +1 -1
- package/src/performance/evidence.js +5 -3
- package/src/performance/load-safety.js +5 -3
- package/src/performance/model.d.ts +4 -4
- package/src/performance/model.js +4 -5
- package/src/performance/scenario.d.ts +22 -0
- package/src/performance/scenario.js +100 -92
- package/src/release/index.d.ts +0 -2
- package/src/release/index.js +0 -2
- package/src/release/source-authority.d.ts +5 -5
- package/src/release/source-authority.js +141 -124
- package/src/repository-policy/aurora-preflight.d.ts +1 -1
- package/src/repository-policy/aurora-preflight.js +4 -1
- package/src/repository-policy/capacity-migration.d.ts +3 -0
- package/src/repository-policy/capacity-migration.js +21 -0
- package/src/repository-policy/deployment-plan.d.ts +4 -1
- package/src/repository-policy/deployment-plan.js +9 -0
- package/src/repository-policy/foundation-profile.js +10 -1
- package/src/repository-policy/policy.d.ts +57 -3
- package/src/repository-policy/policy.js +497 -11
- package/src/schemas/blueprint-v1.json +5 -4
- package/src/schemas/foundation-requirement-v1.json +5 -4
- package/src/schemas/performance-policy-v1.json +3 -3
- package/src/schemas/repository-policy-v1.json +130 -6
- package/src/schemas/testing-evidence-result-v1.json +1 -1
- package/src/secrets/adapters.d.ts +7 -2
- package/src/secrets/adapters.js +34 -3
- package/src/secrets/aws-control-plane.d.ts +10 -1
- package/src/secrets/aws-control-plane.js +67 -0
- package/src/secrets/generated-aws-control-plane.d.ts +23 -0
- package/src/secrets/generated-aws-control-plane.js +259 -0
- package/src/secrets/metadata.js +38 -10
- package/src/secrets/model.d.ts +8 -1
- package/src/secrets/model.js +24 -3
- package/src/secrets/operator.d.ts +91 -3
- package/src/secrets/operator.js +463 -13
- package/src/secrets/registry.d.ts +2 -1
- package/src/secrets/registry.js +18 -1
- package/src/secrets/verification.js +9 -1
- package/src/secrets/workload-access.js +10 -2
- package/src/sst-environment-source-format.d.ts +6 -0
- package/src/sst-environment-source-format.js +199 -0
- package/src/supply-chain/action-pins.js +61 -7
- package/src/supply-chain/blueprint-trust-git.js +85 -24
- package/src/supply-chain/blueprint-verification-authority.js +55 -3
- package/src/supply-chain/build-output-bounds.d.ts +2 -0
- package/src/supply-chain/build-output-bounds.js +12 -0
- package/src/supply-chain/build-output.d.ts +7 -1
- package/src/supply-chain/build-output.js +177 -24
- package/src/supply-chain/ecr-scanning.d.ts +5 -3
- package/src/supply-chain/ecr-scanning.js +11 -7
- package/src/supply-chain/ecs-deployed-image.d.ts +5 -3
- package/src/supply-chain/ecs-deployed-image.js +6 -4
- package/src/supply-chain/generated-blueprint-trust-command.js +18 -1
- package/src/supply-chain/generated-command.js +1 -0
- package/src/supply-chain/generated-container-release-command.js +1285 -124
- package/src/supply-chain/generated-ecr-collector-command.d.ts +0 -1
- package/src/supply-chain/generated-ecr-collector-command.js +200 -93
- package/src/supply-chain/generated-files.js +25 -14
- package/src/supply-chain/generated-scanner-command.js +2 -1
- package/src/supply-chain/index.d.ts +1 -1
- package/src/supply-chain/index.js +2 -1
- package/src/supply-chain/sbom.d.ts +1 -1
- package/src/supply-chain/sbom.js +30 -5
- package/src/supply-chain/scanner.js +3 -0
- package/src/testing-evidence/fixture-scope.d.ts +2 -1
- package/src/testing-evidence/fixture-scope.js +4 -2
- package/src/testing-evidence/result.d.ts +2 -1
- package/src/testing-evidence/result.js +18 -4
- package/src/workload-roles/account-baseline.d.ts +11 -0
- package/src/workload-roles/account-baseline.js +253 -16
- package/src/workload-roles/authority.d.ts +3 -1
- package/src/workload-roles/authority.js +48 -2
- package/src/workload-roles/aws-s3-state.js +27 -0
- package/src/workload-roles/boundary.d.ts +2 -1
- package/src/workload-roles/boundary.js +9 -2
- package/src/workload-roles/handler.js +52 -24
- package/src/workload-roles/index.d.ts +10 -17
- package/src/workload-roles/index.js +5 -18
- package/src/workload-roles/injection.js +1 -1
- package/src/workload-roles/provisioner.js +12 -4
- package/src/workload-roles/registration.d.ts +1 -0
- package/src/workload-roles/registration.js +5 -0
- package/src/workload-roles/stage-boundary.js +1 -1
- package/src/workload-roles/standard-slice.js +3 -3
- package/src/workspace-deployment/affected-lifecycle.d.ts +53 -0
- package/src/workspace-deployment/affected-lifecycle.js +84 -0
- package/src/workspace-deployment/identity.d.ts +3 -0
- package/src/workspace-deployment/identity.js +22 -0
- package/src/workspace-deployment/index.d.ts +4 -0
- package/src/workspace-deployment/index.js +8 -0
- package/src/workspace-deployment/migration-evidence.d.ts +29 -0
- package/src/workspace-deployment/migration-evidence.js +83 -0
- package/src/workspace-deployment/migration.d.ts +27 -0
- package/src/workspace-deployment/migration.js +109 -0
- package/src/workspace-deployment/ownership.d.ts +81 -0
- package/src/workspace-deployment/ownership.js +381 -0
- package/src/api-contract/baseline-update.js.map +0 -1
- package/src/api-contract/compatibility.js.map +0 -1
- package/src/api-contract/contract-graph.js.map +0 -1
- package/src/api-contract/evidence.js.map +0 -1
- package/src/api-contract/index.js.map +0 -1
- package/src/api-contract/model.fixtures.d.ts +0 -2
- package/src/api-contract/model.fixtures.js +0 -81
- package/src/api-contract/model.fixtures.js.map +0 -1
- package/src/api-contract/model.js.map +0 -1
- package/src/api-contract/rollout.js.map +0 -1
- package/src/authorization/compatibility.js.map +0 -1
- package/src/authorization/index.js.map +0 -1
- package/src/blueprints/execution.js.map +0 -1
- package/src/blueprints/external-sandbox.js.map +0 -1
- package/src/blueprints/index.js.map +0 -1
- package/src/blueprints/inputs.js.map +0 -1
- package/src/blueprints/manifest.js.map +0 -1
- package/src/blueprints/resolver.js.map +0 -1
- package/src/blueprints/transitions.js.map +0 -1
- package/src/cli/agent-command.js.map +0 -1
- package/src/cli/agent-runtime.js.map +0 -1
- package/src/cli/blueprint-authority.js.map +0 -1
- package/src/cli/blueprint-command.js.map +0 -1
- package/src/cli/bootstrap.js.map +0 -1
- package/src/cli/dev-command.js.map +0 -1
- package/src/cli/main.js.map +0 -1
- package/src/cli/output.js.map +0 -1
- package/src/cli/personal-secret-runtime.js.map +0 -1
- package/src/cli/secret-terminal.js.map +0 -1
- package/src/container-images.js.map +0 -1
- package/src/data-lifecycle/index.js.map +0 -1
- package/src/data-lifecycle/model.fixtures.d.ts +0 -2
- package/src/data-lifecycle/model.fixtures.js +0 -244
- package/src/data-lifecycle/model.fixtures.js.map +0 -1
- package/src/data-lifecycle/model.js.map +0 -1
- package/src/data-lifecycle/sensitive-artifacts.js.map +0 -1
- package/src/data-lifecycle/validation.js.map +0 -1
- package/src/database-retirement/contract.js.map +0 -1
- package/src/database-retirement/index.js.map +0 -1
- package/src/deterministic-order.js.map +0 -1
- package/src/developer-agent/context.js.map +0 -1
- package/src/developer-agent/generator-planning.js.map +0 -1
- package/src/developer-agent/generator-preview.js.map +0 -1
- package/src/developer-agent/git-inspection.js.map +0 -1
- package/src/developer-agent/handoff.js.map +0 -1
- package/src/developer-agent/index.js.map +0 -1
- package/src/developer-agent/model.js.map +0 -1
- package/src/developer-agent/operation-registry.js.map +0 -1
- package/src/developer-agent/ordering.js.map +0 -1
- package/src/developer-agent/planning.js.map +0 -1
- package/src/developer-agent/preview-changes.js.map +0 -1
- package/src/developer-agent/preview-source.js.map +0 -1
- package/src/developer-agent/validation.js.map +0 -1
- package/src/developer-agent/workspace-inspection.js.map +0 -1
- package/src/developer-agent/workspace-path.js.map +0 -1
- package/src/executors/blueprint-trust/blueprint-trust.js.map +0 -1
- package/src/executors/blueprint-verification/blueprint-verification.js.map +0 -1
- package/src/executors/deploy/deploy.js.map +0 -1
- package/src/executors/deployment-plan/deployment-plan.js.map +0 -1
- package/src/executors/dev/dev.js.map +0 -1
- package/src/executors/dev-doctor/dev-doctor.js.map +0 -1
- package/src/executors/dev-setup/dev-setup.js.map +0 -1
- package/src/executors/developer-context/developer-context.js.map +0 -1
- package/src/executors/fleet/fleet.js.map +0 -1
- package/src/executors/foundation-preflight/foundation-preflight.js.map +0 -1
- package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js.map +0 -1
- package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js.map +0 -1
- package/src/executors/github-bootstrap/github-bootstrap.js.map +0 -1
- package/src/executors/infrastructure-plan/infrastructure-plan.js.map +0 -1
- package/src/executors/refresh/refresh.js.map +0 -1
- package/src/executors/remove/remove.js.map +0 -1
- package/src/executors/secret-guardrails/secret-guardrails.js.map +0 -1
- package/src/executors/secret-metadata/secret-metadata.js.map +0 -1
- package/src/executors/sst-lifecycle/logging-preflight.js.map +0 -1
- package/src/executors/sst-lifecycle/run-sst-command.js.map +0 -1
- package/src/executors/sst-lifecycle/secret-preflight.js.map +0 -1
- package/src/executors/unlock/unlock.js.map +0 -1
- package/src/executors/work-markers/work-markers.js.map +0 -1
- package/src/foundation/discovery.js.map +0 -1
- package/src/foundation/index.js.map +0 -1
- package/src/foundation/local-database-contract.js.map +0 -1
- package/src/foundation/logging-preflight.js.map +0 -1
- package/src/foundation/logging.js.map +0 -1
- package/src/foundation/preflight.js.map +0 -1
- package/src/foundation/provision-database-bindings.js.map +0 -1
- package/src/foundation/provision-logical-database.js.map +0 -1
- package/src/foundation/requirements.js.map +0 -1
- package/src/foundation/router-origin-proof.js.map +0 -1
- package/src/foundation/stages.js.map +0 -1
- package/src/frontend-security/index.js.map +0 -1
- package/src/frontend-security/security-headers.js.map +0 -1
- package/src/generators/feature-registry.js.map +0 -1
- package/src/generators/function/function.js.map +0 -1
- package/src/generators/job/job.js.map +0 -1
- package/src/generators/logging-registry.js.map +0 -1
- package/src/generators/post-generation-install.js.map +0 -1
- package/src/generators/preset/generator.js.map +0 -1
- package/src/generators/preset/tool-versions.js.map +0 -1
- package/src/generators/preset/workspace-files-developer-agent.js.map +0 -1
- package/src/generators/preset/workspace-files-foundation-workload-roles.js.map +0 -1
- package/src/generators/preset/workspace-files-performance-cost.js.map +0 -1
- package/src/generators/preset/workspace-files-performance-load.js.map +0 -1
- package/src/generators/preset/workspace-files-workload-role-baseline.js.map +0 -1
- package/src/generators/preset/workspace-files.js.map +0 -1
- package/src/generators/repository-policy/repository-policy.js.map +0 -1
- package/src/generators/service/service.js.map +0 -1
- package/src/generators/slice/built-in-plan-api-contract-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-api-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-async-work-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-authorization-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-deletion-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-lifecycle-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-retention-job-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-retention-launcher-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-data-rights-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-database-lifecycle-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-foundation-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-frontend-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-infrastructure-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-infrastructure-safety-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-metadata-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-operations-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-performance-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-project-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-release-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-reliability-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-secret-runtime-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-shared.js.map +0 -1
- package/src/generators/slice/built-in-plan-testing-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-verification-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-web-files.js.map +0 -1
- package/src/generators/slice/built-in-plan-workload-role-files.js.map +0 -1
- package/src/generators/slice/built-in-plan.js.map +0 -1
- package/src/generators/slice/slice.js.map +0 -1
- package/src/generators/upgrade/upgrade.js.map +0 -1
- package/src/generators/workload/frozen-container.js.map +0 -1
- package/src/generators/workload/performance-registration.js.map +0 -1
- package/src/generators/workload/reliability-registration.js.map +0 -1
- package/src/generators/workload/service-performance-files.js.map +0 -1
- package/src/generators/workload/workload.js.map +0 -1
- package/src/git-policy/action-pins.js.map +0 -1
- package/src/git-policy/github-bootstrap.js.map +0 -1
- package/src/git-policy/index.js.map +0 -1
- package/src/git-policy/routing.js.map +0 -1
- package/src/index.js.map +0 -1
- package/src/infrastructure-change/convergence.js.map +0 -1
- package/src/infrastructure-change/drift.js.map +0 -1
- package/src/infrastructure-change/index.js.map +0 -1
- package/src/infrastructure-change/model.js.map +0 -1
- package/src/infrastructure-change/normalize.js.map +0 -1
- package/src/infrastructure-change/plan.js.map +0 -1
- package/src/infrastructure-change/risk.js.map +0 -1
- package/src/infrastructure-change/summary.js.map +0 -1
- package/src/local-dev/context-discovery.js.map +0 -1
- package/src/local-dev/contract-validation.js.map +0 -1
- package/src/local-dev/contracts.js.map +0 -1
- package/src/local-dev/control-contract.js.map +0 -1
- package/src/local-dev/dev-command-core.js.map +0 -1
- package/src/local-dev/effects.js.map +0 -1
- package/src/local-dev/environment-inspector.js.map +0 -1
- package/src/local-dev/environment-state.js.map +0 -1
- package/src/local-dev/errors.js.map +0 -1
- package/src/local-dev/fleet-client.js.map +0 -1
- package/src/local-dev/fleet-configuration.js.map +0 -1
- package/src/local-dev/fleet-inventory.js.map +0 -1
- package/src/local-dev/fleet-ipc.js.map +0 -1
- package/src/local-dev/fleet-launcher.js.map +0 -1
- package/src/local-dev/fleet-lock.js.map +0 -1
- package/src/local-dev/fleet-logs.js.map +0 -1
- package/src/local-dev/fleet-process.js.map +0 -1
- package/src/local-dev/fleet-runtime.js.map +0 -1
- package/src/local-dev/fleet-session.js.map +0 -1
- package/src/local-dev/fleet-slice-runner-daemon.js.map +0 -1
- package/src/local-dev/fleet-startup.js.map +0 -1
- package/src/local-dev/fleet-state.js.map +0 -1
- package/src/local-dev/fleet-supervisor-daemon.js.map +0 -1
- package/src/local-dev/fleet-supervisor.js.map +0 -1
- package/src/local-dev/gateway-daemon.js.map +0 -1
- package/src/local-dev/gateway.js.map +0 -1
- package/src/local-dev/identity.js.map +0 -1
- package/src/local-dev/index.js.map +0 -1
- package/src/local-dev/local-context.js.map +0 -1
- package/src/local-dev/nx-workspace.js.map +0 -1
- package/src/local-dev/personal-secret-ledger.js.map +0 -1
- package/src/local-dev/personal-stage.js.map +0 -1
- package/src/local-dev/portless.js.map +0 -1
- package/src/local-dev/ports.js.map +0 -1
- package/src/local-dev/private-state.js.map +0 -1
- package/src/local-dev/process-identity.js.map +0 -1
- package/src/local-dev/proxy.js.map +0 -1
- package/src/local-dev/runtime-paths.js.map +0 -1
- package/src/local-dev/secret-slice-claim.js.map +0 -1
- package/src/local-dev/slice-claim.js.map +0 -1
- package/src/local-dev/slice-runner.js.map +0 -1
- package/src/local-dev/tls.js.map +0 -1
- package/src/ownership/composition.js.map +0 -1
- package/src/ownership/foundation-record.js.map +0 -1
- package/src/ownership/hash.js.map +0 -1
- package/src/ownership/index.js.map +0 -1
- package/src/ownership/json-ownership.js.map +0 -1
- package/src/ownership/record.js.map +0 -1
- package/src/ownership/workspace-record.js.map +0 -1
- package/src/performance/cost-collection.js.map +0 -1
- package/src/performance/cost-controls.js.map +0 -1
- package/src/performance/evidence.js.map +0 -1
- package/src/performance/index.js.map +0 -1
- package/src/performance/load-safety.js.map +0 -1
- package/src/performance/model.js.map +0 -1
- package/src/performance/pr-artifact-budget.js.map +0 -1
- package/src/performance/runner.js.map +0 -1
- package/src/performance/scenario-endpoint.js.map +0 -1
- package/src/performance/scenario.js.map +0 -1
- package/src/performance/test-fixtures.d.ts +0 -7
- package/src/performance/test-fixtures.js +0 -169
- package/src/performance/test-fixtures.js.map +0 -1
- package/src/performance/unit-cost.js.map +0 -1
- package/src/release/index.js.map +0 -1
- package/src/release/lifecycle.js.map +0 -1
- package/src/release/manifest.js.map +0 -1
- package/src/release/rollout.js.map +0 -1
- package/src/release/source-authority.js.map +0 -1
- package/src/repository-policy/aurora-preflight.js.map +0 -1
- package/src/repository-policy/deployment-plan.js.map +0 -1
- package/src/repository-policy/foundation-profile.js.map +0 -1
- package/src/repository-policy/index.js.map +0 -1
- package/src/repository-policy/policy.js.map +0 -1
- package/src/secrets/adapters.js.map +0 -1
- package/src/secrets/aws-control-plane.js.map +0 -1
- package/src/secrets/declaration-verification.js.map +0 -1
- package/src/secrets/guardrails.js.map +0 -1
- package/src/secrets/index.js.map +0 -1
- package/src/secrets/metadata.js.map +0 -1
- package/src/secrets/model.js.map +0 -1
- package/src/secrets/operator.js.map +0 -1
- package/src/secrets/permissions.js.map +0 -1
- package/src/secrets/personal-command.js.map +0 -1
- package/src/secrets/personal-ledger.js.map +0 -1
- package/src/secrets/registry.js.map +0 -1
- package/src/secrets/verification.js.map +0 -1
- package/src/secrets/workload-access.js.map +0 -1
- package/src/supply-chain/action-pins.js.map +0 -1
- package/src/supply-chain/blueprint-trust-git.js.map +0 -1
- package/src/supply-chain/blueprint-trust-process.js.map +0 -1
- package/src/supply-chain/blueprint-trust.js.map +0 -1
- package/src/supply-chain/blueprint-verification-authority.js.map +0 -1
- package/src/supply-chain/blueprint-verification-execution.js.map +0 -1
- package/src/supply-chain/build-output.js.map +0 -1
- package/src/supply-chain/common.js.map +0 -1
- package/src/supply-chain/container-artifact-contract.js.map +0 -1
- package/src/supply-chain/container-release.js.map +0 -1
- package/src/supply-chain/ecr-applicability.js.map +0 -1
- package/src/supply-chain/ecr-scanning.js.map +0 -1
- package/src/supply-chain/ecs-deployed-image.js.map +0 -1
- package/src/supply-chain/errors.js.map +0 -1
- package/src/supply-chain/generated-blueprint-trust-command.js.map +0 -1
- package/src/supply-chain/generated-command.js.map +0 -1
- package/src/supply-chain/generated-container-release-command.js.map +0 -1
- package/src/supply-chain/generated-ecr-collector-command.js.map +0 -1
- package/src/supply-chain/generated-files.js.map +0 -1
- package/src/supply-chain/generated-scanner-command.js.map +0 -1
- package/src/supply-chain/index.js.map +0 -1
- package/src/supply-chain/inventory.js.map +0 -1
- package/src/supply-chain/license-expression.js.map +0 -1
- package/src/supply-chain/pnpm-toolchain-authority.js.map +0 -1
- package/src/supply-chain/policy.js.map +0 -1
- package/src/supply-chain/publication.js.map +0 -1
- package/src/supply-chain/sbom.js.map +0 -1
- package/src/supply-chain/scanner.js.map +0 -1
- package/src/supply-chain/vendored-source.js.map +0 -1
- package/src/testing-evidence/fixture-scope.js.map +0 -1
- package/src/testing-evidence/index.js.map +0 -1
- package/src/testing-evidence/quarantine.js.map +0 -1
- package/src/testing-evidence/result.js.map +0 -1
- package/src/testing-evidence/runner-adapter.js.map +0 -1
- package/src/testing-evidence/sensitive-artifacts.js.map +0 -1
- package/src/validation.js.map +0 -1
- package/src/work-markers/index.js.map +0 -1
- package/src/work-markers/work-markers.js.map +0 -1
- package/src/workload-roles/account-baseline.js.map +0 -1
- package/src/workload-roles/authority.js.map +0 -1
- package/src/workload-roles/aws-adapter.js.map +0 -1
- package/src/workload-roles/aws-internal-handler.js.map +0 -1
- package/src/workload-roles/aws-s3-state.js.map +0 -1
- package/src/workload-roles/aws-sdk-control-plane.js.map +0 -1
- package/src/workload-roles/boundary.js.map +0 -1
- package/src/workload-roles/convergence.js.map +0 -1
- package/src/workload-roles/effective-policy.js.map +0 -1
- package/src/workload-roles/handler.js.map +0 -1
- package/src/workload-roles/history.js.map +0 -1
- package/src/workload-roles/index.js.map +0 -1
- package/src/workload-roles/injection.js.map +0 -1
- package/src/workload-roles/manifest.js.map +0 -1
- package/src/workload-roles/provisioner.js.map +0 -1
- package/src/workload-roles/registration.js.map +0 -1
- package/src/workload-roles/stage-boundary.js.map +0 -1
- package/src/workload-roles/standard-slice.js.map +0 -1
|
@@ -5,7 +5,75 @@ exports.renderContainerReleaseCommand = renderContainerReleaseCommand;
|
|
|
5
5
|
const generated_ecr_collector_command_js_1 = require("./generated-ecr-collector-command.js");
|
|
6
6
|
const safe_reference_1 = require("@empire-builder-kit/runtime/safe-reference");
|
|
7
7
|
exports.CRANE_VERSION = '0.21.7';
|
|
8
|
+
const WINDOWS_HARDEN_PRIVATE_ACL = String.raw `
|
|
9
|
+
$ErrorActionPreference = 'Stop'
|
|
10
|
+
$path = $env:EBK_PRIVATE_PATH
|
|
11
|
+
$kind = $env:EBK_PRIVATE_KIND
|
|
12
|
+
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
|
|
13
|
+
if ($kind -eq 'directory') {
|
|
14
|
+
$info = [System.IO.DirectoryInfo]::new($path)
|
|
15
|
+
} else {
|
|
16
|
+
$info = [System.IO.FileInfo]::new($path)
|
|
17
|
+
}
|
|
18
|
+
$acl = $info.GetAccessControl()
|
|
19
|
+
$acl.SetAccessRuleProtection($true, $false)
|
|
20
|
+
foreach ($rule in @($acl.Access)) {
|
|
21
|
+
[void]$acl.RemoveAccessRuleSpecific($rule)
|
|
22
|
+
}
|
|
23
|
+
$rights = [System.Security.AccessControl.FileSystemRights]::FullControl
|
|
24
|
+
$type = [System.Security.AccessControl.AccessControlType]::Allow
|
|
25
|
+
if ($kind -eq 'directory') {
|
|
26
|
+
$inheritance = [System.Security.AccessControl.InheritanceFlags]'ContainerInherit,ObjectInherit'
|
|
27
|
+
$propagation = [System.Security.AccessControl.PropagationFlags]::None
|
|
28
|
+
$rule = [System.Security.AccessControl.FileSystemAccessRule]::new(
|
|
29
|
+
$identity.User, $rights, $inheritance, $propagation, $type
|
|
30
|
+
)
|
|
31
|
+
} else {
|
|
32
|
+
$rule = [System.Security.AccessControl.FileSystemAccessRule]::new(
|
|
33
|
+
$identity.User, $rights, $type
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
$acl.AddAccessRule($rule)
|
|
37
|
+
$info.SetAccessControl($acl)
|
|
38
|
+
`;
|
|
39
|
+
const WINDOWS_ASSERT_PRIVATE_ACL = String.raw `
|
|
40
|
+
$ErrorActionPreference = 'Stop'
|
|
41
|
+
$path = $env:EBK_PRIVATE_PATH
|
|
42
|
+
$kind = $env:EBK_PRIVATE_KIND
|
|
43
|
+
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
|
|
44
|
+
if ($kind -eq 'directory') {
|
|
45
|
+
$info = [System.IO.DirectoryInfo]::new($path)
|
|
46
|
+
} else {
|
|
47
|
+
$info = [System.IO.FileInfo]::new($path)
|
|
48
|
+
}
|
|
49
|
+
$acl = $info.GetAccessControl()
|
|
50
|
+
if (-not $acl.AreAccessRulesProtected) {
|
|
51
|
+
throw 'ACL inheritance is enabled.'
|
|
52
|
+
}
|
|
53
|
+
$allowRules = @($acl.Access | Where-Object {
|
|
54
|
+
$_.AccessControlType -eq [System.Security.AccessControl.AccessControlType]::Allow
|
|
55
|
+
})
|
|
56
|
+
$unsafeRules = @($allowRules | Where-Object {
|
|
57
|
+
$_.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]).Value -ne $identity.User.Value
|
|
58
|
+
})
|
|
59
|
+
$selfRules = @($allowRules | Where-Object {
|
|
60
|
+
$_.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]).Value -eq $identity.User.Value
|
|
61
|
+
})
|
|
62
|
+
if ($unsafeRules.Count -ne 0 -or $selfRules.Count -eq 0) {
|
|
63
|
+
throw 'ACL grants access beyond the current user or omits the current user.'
|
|
64
|
+
}
|
|
65
|
+
`;
|
|
8
66
|
exports.CRANE_RELEASES = Object.freeze({
|
|
67
|
+
'darwin-arm64': {
|
|
68
|
+
archiveFormat: 'tar-gzip',
|
|
69
|
+
sha256: '1858c55dcd6053fe869bcb0c4ec20666383ddce445ad0f7e15e1e506b1f7fe52',
|
|
70
|
+
source: 'https://github.com/google/go-containerregistry/releases/download/v0.21.7/go-containerregistry_Darwin_arm64.tar.gz',
|
|
71
|
+
},
|
|
72
|
+
'darwin-x64': {
|
|
73
|
+
archiveFormat: 'tar-gzip',
|
|
74
|
+
sha256: '63a7dd15168d4dcac37933c7f6745438f2943d5898a1cf7896ad3341d8519bf2',
|
|
75
|
+
source: 'https://github.com/google/go-containerregistry/releases/download/v0.21.7/go-containerregistry_Darwin_x86_64.tar.gz',
|
|
76
|
+
},
|
|
9
77
|
'linux-arm64': {
|
|
10
78
|
archiveFormat: 'tar-gzip',
|
|
11
79
|
sha256: 'b6ee979d9411dfb05ce35ab9e156fe5de7def11a230764a7856ffa2eb971fa88',
|
|
@@ -19,7 +87,7 @@ exports.CRANE_RELEASES = Object.freeze({
|
|
|
19
87
|
});
|
|
20
88
|
function validatedReleases(releases) {
|
|
21
89
|
if (Object.keys(releases).length === 0 ||
|
|
22
|
-
Object.entries(releases).some(([platform, release]) => !/^linux-(?:arm64|x64)$/.test(platform) ||
|
|
90
|
+
Object.entries(releases).some(([platform, release]) => !/^(?:darwin|linux)-(?:arm64|x64)$/.test(platform) ||
|
|
23
91
|
!['binary', 'tar-gzip'].includes(release.archiveFormat) ||
|
|
24
92
|
!/^[a-f0-9]{64}$/.test(release.sha256) ||
|
|
25
93
|
!/^https:\/\/github\.com\/anchore\/syft\/releases\/download\/v\d+\.\d+\.\d+\/[A-Za-z0-9._-]+$/.test(release.source))) {
|
|
@@ -29,10 +97,10 @@ function validatedReleases(releases) {
|
|
|
29
97
|
}
|
|
30
98
|
function validatedCraneReleases(releases) {
|
|
31
99
|
if (Object.keys(releases).length === 0 ||
|
|
32
|
-
Object.entries(releases).some(([platform, release]) => !/^linux-(?:arm64|x64)$/.test(platform) ||
|
|
100
|
+
Object.entries(releases).some(([platform, release]) => !/^(?:darwin|linux)-(?:arm64|x64)$/.test(platform) ||
|
|
33
101
|
release.archiveFormat !== 'tar-gzip' ||
|
|
34
102
|
!/^[a-f0-9]{64}$/.test(release.sha256) ||
|
|
35
|
-
!/^https:\/\/github\.com\/google\/go-containerregistry\/releases\/download\/v\d+\.\d+\.\d+\/go-
|
|
103
|
+
!/^https:\/\/github\.com\/google\/go-containerregistry\/releases\/download\/v\d+\.\d+\.\d+\/go-containerregistry_(?:Darwin|Linux)_(?:arm64|x86_64)\.tar\.gz$/.test(release.source))) {
|
|
36
104
|
throw new Error('Container-release Crane assets must be exact official integrity-bound releases.');
|
|
37
105
|
}
|
|
38
106
|
return releases;
|
|
@@ -49,10 +117,11 @@ function renderContainerReleaseCommand(options = {}) {
|
|
|
49
117
|
!/^\d+\.\d+\.\d+$/.test(craneVersion)) {
|
|
50
118
|
throw new Error('The generated container release configuration is invalid.');
|
|
51
119
|
}
|
|
52
|
-
return `import { createHash } from 'node:crypto';
|
|
53
|
-
import { execFileSync, spawnSync } from 'node:child_process';
|
|
120
|
+
return `import { createHash, randomBytes } from 'node:crypto';
|
|
121
|
+
import { execFileSync, spawn, spawnSync } from 'node:child_process';
|
|
54
122
|
import {
|
|
55
123
|
chmodSync,
|
|
124
|
+
copyFileSync,
|
|
56
125
|
existsSync,
|
|
57
126
|
lstatSync,
|
|
58
127
|
mkdirSync,
|
|
@@ -60,11 +129,13 @@ import {
|
|
|
60
129
|
readFileSync,
|
|
61
130
|
realpathSync,
|
|
62
131
|
readdirSync,
|
|
132
|
+
renameSync,
|
|
63
133
|
rmSync,
|
|
134
|
+
unlinkSync,
|
|
64
135
|
writeFileSync,
|
|
65
136
|
} from 'node:fs';
|
|
66
137
|
import { basename, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
67
|
-
import { tmpdir } from 'node:os';
|
|
138
|
+
import { platform as hostOperatingSystem, tmpdir } from 'node:os';
|
|
68
139
|
import {
|
|
69
140
|
CONTAINER_NATIVE_LIFECYCLE_PROOFS,
|
|
70
141
|
CONTAINER_RELEASE_PLATFORMS,
|
|
@@ -72,6 +143,7 @@ import {
|
|
|
72
143
|
createContainerNativeRunProof,
|
|
73
144
|
createContainerPlatformEvidence,
|
|
74
145
|
createContainerReleaseBinding,
|
|
146
|
+
hasSensitiveSupplyChainShape,
|
|
75
147
|
resolveContainerReleaseArtifact,
|
|
76
148
|
supplyChainDigest,
|
|
77
149
|
validateContainerArtifactIdentity,
|
|
@@ -85,6 +157,8 @@ const SYFT_VERSION = ${JSON.stringify(syftVersion)};
|
|
|
85
157
|
const SYFT_RELEASES = Object.freeze(${JSON.stringify(releases)});
|
|
86
158
|
const CRANE_VERSION = ${JSON.stringify(craneVersion)};
|
|
87
159
|
const CRANE_RELEASES = Object.freeze(${JSON.stringify(craneReleases)});
|
|
160
|
+
const WINDOWS_HARDEN_PRIVATE_ACL = ${JSON.stringify(WINDOWS_HARDEN_PRIVATE_ACL)};
|
|
161
|
+
const WINDOWS_ASSERT_PRIVATE_ACL = ${JSON.stringify(WINDOWS_ASSERT_PRIVATE_ACL)};
|
|
88
162
|
const SHA256 = /^[a-f0-9]{64}$/;
|
|
89
163
|
const IMAGE_DIGEST = /^sha256:[a-f0-9]{64}$/;
|
|
90
164
|
const COMMIT = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/;
|
|
@@ -95,6 +169,39 @@ const AWS_REGION_NAME =
|
|
|
95
169
|
/^(?=.{9,32}$)[a-z]{2}(?:-gov)?-[a-z]+(?:-[a-z]+)*-\\d$/;
|
|
96
170
|
const MAX_TOOL_BYTES = 100 * 1024 * 1024;
|
|
97
171
|
const MAX_OUTPUT_BYTES = 64 * 1024 * 1024;
|
|
172
|
+
// Optional absolute cache roots supplied by the workflow (runner-local, restored
|
|
173
|
+
// by the managed cache step). Absent or relative values disable the caches.
|
|
174
|
+
const optionalCacheRoot = (name) => {
|
|
175
|
+
const value = process.env[name]?.trim() ?? '';
|
|
176
|
+
return value !== '' && isAbsolute(value) && !value.includes('..') ? value : '';
|
|
177
|
+
};
|
|
178
|
+
const buildxLayerCache = (repositoryName, architecture) => {
|
|
179
|
+
const root = optionalCacheRoot('EBK_BUILDX_CACHE_DIR');
|
|
180
|
+
if (root === '') return { arguments: [], commit: () => {} };
|
|
181
|
+
const scope = repositoryName.replaceAll('/', '-') + '-' + architecture;
|
|
182
|
+
const current = join(root, scope);
|
|
183
|
+
const next = join(root, scope + '.next');
|
|
184
|
+
mkdirSync(root, { mode: 0o700, recursive: true });
|
|
185
|
+
rmSync(next, { force: true, recursive: true });
|
|
186
|
+
return {
|
|
187
|
+
arguments: [
|
|
188
|
+
...(existsSync(current)
|
|
189
|
+
? ['--cache-from', 'type=local,src=' + current]
|
|
190
|
+
: []),
|
|
191
|
+
'--cache-to',
|
|
192
|
+
'type=local,dest=' + next + ',mode=max',
|
|
193
|
+
],
|
|
194
|
+
commit: () => {
|
|
195
|
+
if (!existsSync(next)) return;
|
|
196
|
+
rmSync(current, { force: true, recursive: true });
|
|
197
|
+
renameSync(next, current);
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
};
|
|
201
|
+
const cachedReleaseToolArchive = (name, sha256) => {
|
|
202
|
+
const root = optionalCacheRoot('EBK_RELEASE_TOOL_CACHE_DIR');
|
|
203
|
+
return root === '' ? '' : join(root, name + '-' + sha256);
|
|
204
|
+
};
|
|
98
205
|
const mode = process.argv[2];
|
|
99
206
|
const requestedPlatform = process.argv[3];
|
|
100
207
|
if (
|
|
@@ -129,23 +236,102 @@ const sourceCommit =
|
|
|
129
236
|
const stage = process.env.EBK_STAGE?.trim() ?? '';
|
|
130
237
|
const accountId = process.env.AWS_EXPECTED_ACCOUNT_ID?.trim() ?? '';
|
|
131
238
|
const region = process.env.AWS_REGION?.trim() ?? '';
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
const
|
|
239
|
+
const packageRegistryInput =
|
|
240
|
+
process.env.EBK_CONTAINER_PACKAGE_REGISTRY?.trim() ?? '';
|
|
241
|
+
const localFederated =
|
|
242
|
+
process.env.GITHUB_ACTIONS !== 'true' &&
|
|
243
|
+
process.env.EBK_LOCAL_DEPLOYMENT_MODE === 'federated';
|
|
244
|
+
const localRunId = process.env.EBK_LOCAL_CONTAINER_RUN_ID?.trim() ?? '';
|
|
245
|
+
const policy = JSON.parse(
|
|
246
|
+
readFileSync(join(repositoryRoot, 'ebk.repository-policy.json'), 'utf8'),
|
|
247
|
+
);
|
|
248
|
+
const selectedDeploymentProfile =
|
|
249
|
+
process.env.EBK_DEPLOYMENT_PROFILE?.trim() ?? '';
|
|
250
|
+
const activeStages = {
|
|
251
|
+
starter: ['development', 'production'],
|
|
252
|
+
'production-isolated': ['development', 'staging', 'production'],
|
|
253
|
+
'fully-isolated': ['development', 'staging', 'production'],
|
|
254
|
+
}[policy.deploymentProfile];
|
|
255
|
+
const locallyAdmittedStages = {
|
|
256
|
+
starter: ['development', 'production'],
|
|
257
|
+
'production-isolated': ['development', 'staging'],
|
|
258
|
+
'fully-isolated': ['development'],
|
|
259
|
+
}[policy.deploymentProfile];
|
|
260
|
+
if (
|
|
261
|
+
selectedDeploymentProfile !== policy.deploymentProfile ||
|
|
262
|
+
!activeStages ||
|
|
263
|
+
!activeStages.includes(stage) ||
|
|
264
|
+
policy.deploymentAccounts?.[stage] !== accountId ||
|
|
265
|
+
policy.deploymentRegion !== region
|
|
266
|
+
) {
|
|
267
|
+
throw new Error(
|
|
268
|
+
'Container release requires the exact committed profile, active stage, account, and Region.',
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
if (
|
|
272
|
+
localFederated &&
|
|
273
|
+
(!locallyAdmittedStages || !locallyAdmittedStages.includes(stage))
|
|
274
|
+
) {
|
|
275
|
+
throw new Error(
|
|
276
|
+
'Local container release requires one of the committed profile locally admitted stages.',
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
const runId = localFederated
|
|
280
|
+
? localRunId
|
|
281
|
+
: process.env.GITHUB_RUN_ID?.trim() ?? '';
|
|
282
|
+
const runAttempt = localFederated
|
|
283
|
+
? '1'
|
|
284
|
+
: process.env.GITHUB_RUN_ATTEMPT?.trim() ?? '';
|
|
285
|
+
const workflow = localFederated
|
|
286
|
+
? 'local-federated/' + stage + '/' + accountId + '/' + region
|
|
287
|
+
: process.env.GITHUB_WORKFLOW_REF?.trim() ?? '';
|
|
135
288
|
if (
|
|
136
289
|
!COMMIT.test(deploymentSourceCommit) ||
|
|
137
290
|
!COMMIT.test(sourceCommit) ||
|
|
138
291
|
!['development', 'staging', 'production'].includes(stage) ||
|
|
139
292
|
!/^\\d{12}$/.test(accountId) ||
|
|
140
293
|
!AWS_REGION_NAME.test(region) ||
|
|
294
|
+
(!localFederated && process.env.GITHUB_ACTIONS !== 'true') ||
|
|
141
295
|
!/^[1-9]\\d*$/.test(runId) ||
|
|
142
296
|
!/^[1-9]\\d*$/.test(runAttempt) ||
|
|
143
297
|
!REFERENCE.test(workflow)
|
|
144
298
|
) {
|
|
145
299
|
throw new Error(
|
|
146
|
-
'Container release requires one exact
|
|
300
|
+
'Container release requires one exact GitHub or local-federated source, run, account, Region, and stage.',
|
|
147
301
|
);
|
|
148
302
|
}
|
|
303
|
+
const packageRegistry = (() => {
|
|
304
|
+
if (packageRegistryInput === '') return undefined;
|
|
305
|
+
if (packageRegistryInput.length > 2_048) {
|
|
306
|
+
throw new Error('Container package registry authority is invalid.');
|
|
307
|
+
}
|
|
308
|
+
let parsed;
|
|
309
|
+
try {
|
|
310
|
+
parsed = new URL(packageRegistryInput);
|
|
311
|
+
} catch {
|
|
312
|
+
throw new Error('Container package registry authority is invalid.');
|
|
313
|
+
}
|
|
314
|
+
const normalized = parsed.href;
|
|
315
|
+
const canonicalInput =
|
|
316
|
+
packageRegistryInput === normalized ||
|
|
317
|
+
packageRegistryInput + '/' === normalized;
|
|
318
|
+
const localPlaintext =
|
|
319
|
+
parsed.protocol === 'http:' &&
|
|
320
|
+
['127.0.0.1', 'localhost', 'host.docker.internal', '[::1]'].includes(
|
|
321
|
+
parsed.hostname,
|
|
322
|
+
);
|
|
323
|
+
if (
|
|
324
|
+
!canonicalInput ||
|
|
325
|
+
(parsed.protocol !== 'https:' && !localPlaintext) ||
|
|
326
|
+
parsed.username !== '' ||
|
|
327
|
+
parsed.password !== '' ||
|
|
328
|
+
parsed.search !== '' ||
|
|
329
|
+
parsed.hash !== ''
|
|
330
|
+
) {
|
|
331
|
+
throw new Error('Container package registry authority is invalid.');
|
|
332
|
+
}
|
|
333
|
+
return normalized.endsWith('/') ? normalized.slice(0, -1) : normalized;
|
|
334
|
+
})();
|
|
149
335
|
const requireReviewedCheckout = () => {
|
|
150
336
|
validateReviewedReleaseCheckout({
|
|
151
337
|
deploymentSourceCommit,
|
|
@@ -290,12 +476,657 @@ const execute = (command, args, label, options = {}) => {
|
|
|
290
476
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
291
477
|
});
|
|
292
478
|
} catch (error) {
|
|
479
|
+
if (options.failureDiagnostics === 'bounded-closure') {
|
|
480
|
+
const diagnosticStream = (value) => {
|
|
481
|
+
const raw = value === undefined || value === null ? '' : String(value);
|
|
482
|
+
if (raw.length === 0) return '[empty]';
|
|
483
|
+
if (hasSensitiveSupplyChainShape(raw)) {
|
|
484
|
+
return '[redacted-sensitive-output]';
|
|
485
|
+
}
|
|
486
|
+
const bytes = Buffer.from(raw, 'utf8');
|
|
487
|
+
const truncated = bytes.byteLength > 1_024;
|
|
488
|
+
const tail = truncated
|
|
489
|
+
? bytes.subarray(bytes.byteLength - 1_024).toString('utf8')
|
|
490
|
+
: raw;
|
|
491
|
+
return JSON.stringify((truncated ? '[truncated]\\n' : '') + tail);
|
|
492
|
+
};
|
|
493
|
+
const status = Number.isSafeInteger(error?.status)
|
|
494
|
+
? String(error.status)
|
|
495
|
+
: 'unavailable';
|
|
496
|
+
const signal =
|
|
497
|
+
typeof error?.signal === 'string' && /^[A-Z0-9]+$/.test(error.signal)
|
|
498
|
+
? error.signal
|
|
499
|
+
: 'none';
|
|
500
|
+
throw new Error(
|
|
501
|
+
label +
|
|
502
|
+
' failed without complete evidence: ' +
|
|
503
|
+
'status=' +
|
|
504
|
+
status +
|
|
505
|
+
'; signal=' +
|
|
506
|
+
signal +
|
|
507
|
+
'; stdout=' +
|
|
508
|
+
diagnosticStream(error?.stdout) +
|
|
509
|
+
'; stderr=' +
|
|
510
|
+
diagnosticStream(error?.stderr),
|
|
511
|
+
);
|
|
512
|
+
}
|
|
293
513
|
throw new Error(
|
|
294
514
|
label + ' failed without complete evidence: ' +
|
|
295
515
|
String(error?.stderr ?? error?.message ?? error).slice(0, 2_000),
|
|
296
516
|
);
|
|
297
517
|
}
|
|
298
518
|
};
|
|
519
|
+
let releaseBuilder;
|
|
520
|
+
let releaseBuildChild;
|
|
521
|
+
let releaseBuildStopPromise;
|
|
522
|
+
let releaseBuilderSignal;
|
|
523
|
+
const releaseBuilderSignalHandlers = new Map();
|
|
524
|
+
const releaseToolTemporaryDirectories = new Set();
|
|
525
|
+
const yieldToReleaseSignals = () =>
|
|
526
|
+
new Promise((resolve) => setImmediate(resolve));
|
|
527
|
+
const stopExactReleaseBuildChild = async (signal = 'SIGTERM') => {
|
|
528
|
+
if (releaseBuildStopPromise) return releaseBuildStopPromise;
|
|
529
|
+
const observed = releaseBuildChild;
|
|
530
|
+
if (!observed) return;
|
|
531
|
+
releaseBuildStopPromise = (async () => {
|
|
532
|
+
const child = observed.child;
|
|
533
|
+
if (hostOperatingSystem() === 'win32') {
|
|
534
|
+
const taskkill = join(process.env.SystemRoot ?? '', 'System32', 'taskkill.exe');
|
|
535
|
+
if (!isAbsolute(taskkill) || !existsSync(taskkill) || realpathSync(taskkill) !== taskkill) {
|
|
536
|
+
throw new Error('Exact Windows Buildx cleanup requires canonical SystemRoot.');
|
|
537
|
+
}
|
|
538
|
+
const stopped = spawnSync(
|
|
539
|
+
taskkill,
|
|
540
|
+
['/PID', String(child.pid), '/T', '/F'],
|
|
541
|
+
{
|
|
542
|
+
cwd: repositoryRoot,
|
|
543
|
+
encoding: 'utf8',
|
|
544
|
+
env: process.env,
|
|
545
|
+
maxBuffer: 1024 * 1024,
|
|
546
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
547
|
+
windowsHide: true,
|
|
548
|
+
},
|
|
549
|
+
);
|
|
550
|
+
if (
|
|
551
|
+
child.exitCode === null &&
|
|
552
|
+
(stopped.error || stopped.signal || stopped.status !== 0)
|
|
553
|
+
) {
|
|
554
|
+
throw new Error('Exact Windows Buildx process-tree cleanup failed.');
|
|
555
|
+
}
|
|
556
|
+
} else if (child.pid) {
|
|
557
|
+
try {
|
|
558
|
+
process.kill(-child.pid, signal);
|
|
559
|
+
} catch (error) {
|
|
560
|
+
if (error?.code !== 'ESRCH') throw error;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
if (child.exitCode === null) {
|
|
564
|
+
const closed = await Promise.race([
|
|
565
|
+
observed.close.then(() => true),
|
|
566
|
+
new Promise((resolve) => setTimeout(() => resolve(false), 5_000)),
|
|
567
|
+
]);
|
|
568
|
+
if (!closed && hostOperatingSystem() !== 'win32' && child.pid) {
|
|
569
|
+
try {
|
|
570
|
+
process.kill(-child.pid, 'SIGKILL');
|
|
571
|
+
} catch (error) {
|
|
572
|
+
if (error?.code !== 'ESRCH') throw error;
|
|
573
|
+
}
|
|
574
|
+
const killed = await Promise.race([
|
|
575
|
+
observed.close.then(() => true),
|
|
576
|
+
new Promise((resolve) => setTimeout(() => resolve(false), 5_000)),
|
|
577
|
+
]);
|
|
578
|
+
if (!killed) {
|
|
579
|
+
throw new Error('Exact Buildx process group did not terminate.');
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
})().finally(() => {
|
|
584
|
+
if (releaseBuildChild === observed) releaseBuildChild = undefined;
|
|
585
|
+
releaseBuildStopPromise = undefined;
|
|
586
|
+
});
|
|
587
|
+
return releaseBuildStopPromise;
|
|
588
|
+
};
|
|
589
|
+
const executeObservedBuildx = async (args, label) => {
|
|
590
|
+
if (releaseBuildChild) {
|
|
591
|
+
throw new Error('Only one invocation-owned Buildx child may run at a time.');
|
|
592
|
+
}
|
|
593
|
+
const child = spawn('docker', args, {
|
|
594
|
+
cwd: repositoryRoot,
|
|
595
|
+
detached: hostOperatingSystem() !== 'win32',
|
|
596
|
+
env: process.env,
|
|
597
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
598
|
+
windowsHide: true,
|
|
599
|
+
});
|
|
600
|
+
const stdout = [];
|
|
601
|
+
const stderr = [];
|
|
602
|
+
let outputBytes = 0;
|
|
603
|
+
const retain = (chunks, stream, chunk) => {
|
|
604
|
+
outputBytes += chunk.byteLength;
|
|
605
|
+
if (outputBytes > MAX_OUTPUT_BYTES) {
|
|
606
|
+
void stopExactReleaseBuildChild('SIGTERM');
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
chunks.push(chunk);
|
|
610
|
+
stream.write(chunk);
|
|
611
|
+
};
|
|
612
|
+
child.stdout.on('data', (chunk) => retain(stdout, process.stdout, chunk));
|
|
613
|
+
child.stderr.on('data', (chunk) => retain(stderr, process.stderr, chunk));
|
|
614
|
+
const close = new Promise((resolve) => {
|
|
615
|
+
child.once('error', (error) => resolve({ error }));
|
|
616
|
+
child.once('close', (status, signal) => resolve({ signal, status }));
|
|
617
|
+
});
|
|
618
|
+
releaseBuildChild = { child, close };
|
|
619
|
+
const result = await close;
|
|
620
|
+
await stopExactReleaseBuildChild();
|
|
621
|
+
if (
|
|
622
|
+
outputBytes > MAX_OUTPUT_BYTES ||
|
|
623
|
+
result.error ||
|
|
624
|
+
result.signal ||
|
|
625
|
+
result.status !== 0
|
|
626
|
+
) {
|
|
627
|
+
throw new Error(
|
|
628
|
+
label + ' failed without complete evidence: ' +
|
|
629
|
+
Buffer.concat(stderr).toString('utf8').slice(-2_000),
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
return Buffer.concat(stdout).toString('utf8');
|
|
633
|
+
};
|
|
634
|
+
const runWindowsPrivateAcl = (script, path, kind) => {
|
|
635
|
+
const systemRoot = process.env.SystemRoot;
|
|
636
|
+
if (!systemRoot || !isAbsolute(systemRoot)) {
|
|
637
|
+
throw new Error('Windows private-path hardening requires an absolute SystemRoot.');
|
|
638
|
+
}
|
|
639
|
+
const powershell = join(
|
|
640
|
+
systemRoot,
|
|
641
|
+
'System32',
|
|
642
|
+
'WindowsPowerShell',
|
|
643
|
+
'v1.0',
|
|
644
|
+
'powershell.exe',
|
|
645
|
+
);
|
|
646
|
+
const result = spawnSync(
|
|
647
|
+
powershell,
|
|
648
|
+
[
|
|
649
|
+
'-NoLogo',
|
|
650
|
+
'-NoProfile',
|
|
651
|
+
'-NonInteractive',
|
|
652
|
+
'-EncodedCommand',
|
|
653
|
+
Buffer.from(script, 'utf16le').toString('base64'),
|
|
654
|
+
],
|
|
655
|
+
{
|
|
656
|
+
cwd: repositoryRoot,
|
|
657
|
+
encoding: 'utf8',
|
|
658
|
+
env: {
|
|
659
|
+
...process.env,
|
|
660
|
+
EBK_PRIVATE_KIND: kind,
|
|
661
|
+
EBK_PRIVATE_PATH: path,
|
|
662
|
+
},
|
|
663
|
+
maxBuffer: 1024 * 1024,
|
|
664
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
665
|
+
timeout: 15_000,
|
|
666
|
+
windowsHide: true,
|
|
667
|
+
},
|
|
668
|
+
);
|
|
669
|
+
if (result.error || result.signal || result.status !== 0) {
|
|
670
|
+
throw new Error(
|
|
671
|
+
'Windows private-path ACL operation failed closed: ' +
|
|
672
|
+
String(result.stderr || result.error?.message || result.signal || result.status)
|
|
673
|
+
.slice(-2_000),
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
const assertPrivateReleaseBuilderPathIdentity = (path, kind) => {
|
|
678
|
+
const metadata = lstatSync(path);
|
|
679
|
+
if (
|
|
680
|
+
metadata.isSymbolicLink() ||
|
|
681
|
+
(kind === 'directory' ? !metadata.isDirectory() : !metadata.isFile()) ||
|
|
682
|
+
realpathSync(path) !== path
|
|
683
|
+
) {
|
|
684
|
+
throw new Error(
|
|
685
|
+
'Container-release builder ownership path failed canonical type validation.',
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
return metadata;
|
|
689
|
+
};
|
|
690
|
+
const assertPrivateReleaseBuilderPath = (path, kind) => {
|
|
691
|
+
const metadata = assertPrivateReleaseBuilderPathIdentity(path, kind);
|
|
692
|
+
if (hostOperatingSystem() === 'win32') {
|
|
693
|
+
runWindowsPrivateAcl(WINDOWS_ASSERT_PRIVATE_ACL, path, kind);
|
|
694
|
+
return metadata;
|
|
695
|
+
}
|
|
696
|
+
const expectedMode = kind === 'directory' ? 0o700 : 0o600;
|
|
697
|
+
if (
|
|
698
|
+
(metadata.mode & 0o777) !== expectedMode ||
|
|
699
|
+
(typeof process.getuid === 'function' && metadata.uid !== process.getuid())
|
|
700
|
+
) {
|
|
701
|
+
throw new Error(
|
|
702
|
+
'Container-release builder ownership path is not current-user private.',
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
return metadata;
|
|
706
|
+
};
|
|
707
|
+
const hardenPrivateReleaseBuilderPath = (path, kind) => {
|
|
708
|
+
assertPrivateReleaseBuilderPathIdentity(path, kind);
|
|
709
|
+
if (hostOperatingSystem() === 'win32') {
|
|
710
|
+
runWindowsPrivateAcl(WINDOWS_HARDEN_PRIVATE_ACL, path, kind);
|
|
711
|
+
} else {
|
|
712
|
+
chmodSync(path, kind === 'directory' ? 0o700 : 0o600);
|
|
713
|
+
}
|
|
714
|
+
return assertPrivateReleaseBuilderPath(path, kind);
|
|
715
|
+
};
|
|
716
|
+
const assertCanonicalAncestorsBeforeCreate = (requested) => {
|
|
717
|
+
const requestedRelative = relative(repositoryRoot, requested);
|
|
718
|
+
if (
|
|
719
|
+
requestedRelative === '' ||
|
|
720
|
+
requestedRelative === '..' ||
|
|
721
|
+
requestedRelative.startsWith('..' + sep) ||
|
|
722
|
+
isAbsolute(requestedRelative)
|
|
723
|
+
) {
|
|
724
|
+
throw new Error('Container-release builder ownership path escapes its repository.');
|
|
725
|
+
}
|
|
726
|
+
let cursor = repositoryRoot;
|
|
727
|
+
for (const segment of requestedRelative.split(sep)) {
|
|
728
|
+
cursor = join(cursor, segment);
|
|
729
|
+
if (!existsSync(cursor)) break;
|
|
730
|
+
const metadata = lstatSync(cursor);
|
|
731
|
+
if (metadata.isSymbolicLink() || realpathSync(cursor) !== cursor) {
|
|
732
|
+
throw new Error(
|
|
733
|
+
'Container-release builder ownership path has a symbolic or noncanonical ancestor.',
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
const releaseBuilderLeaseRoot = () => {
|
|
739
|
+
const requested = resolve(
|
|
740
|
+
repositoryRoot,
|
|
741
|
+
'.ebk/cache/container-release-builders',
|
|
742
|
+
);
|
|
743
|
+
assertCanonicalAncestorsBeforeCreate(requested);
|
|
744
|
+
mkdirSync(requested, { mode: 0o700, recursive: true });
|
|
745
|
+
hardenPrivateReleaseBuilderPath(requested, 'directory');
|
|
746
|
+
const canonical = realpathSync(requested);
|
|
747
|
+
if (canonical !== requested) {
|
|
748
|
+
throw new Error(
|
|
749
|
+
'Container-release builder leases require one repository-private owned directory.',
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
return canonical;
|
|
753
|
+
};
|
|
754
|
+
const readReleaseBuilderLease = (leasePath, expectedName) => {
|
|
755
|
+
const metadata = assertPrivateReleaseBuilderPath(leasePath, 'file');
|
|
756
|
+
if (
|
|
757
|
+
metadata.nlink !== 1 ||
|
|
758
|
+
metadata.size < 2 ||
|
|
759
|
+
metadata.size > 16 * 1024
|
|
760
|
+
) {
|
|
761
|
+
throw new Error('Container-release builder lease is not one private file.');
|
|
762
|
+
}
|
|
763
|
+
let lease;
|
|
764
|
+
try {
|
|
765
|
+
lease = JSON.parse(readFileSync(leasePath, 'utf8'));
|
|
766
|
+
} catch {
|
|
767
|
+
throw new Error('Container-release builder lease is invalid JSON.');
|
|
768
|
+
}
|
|
769
|
+
const keys = Object.keys(lease ?? {}).sort();
|
|
770
|
+
if (
|
|
771
|
+
JSON.stringify(keys) !==
|
|
772
|
+
JSON.stringify([
|
|
773
|
+
'builder',
|
|
774
|
+
'cleanup',
|
|
775
|
+
'createdAt',
|
|
776
|
+
'driver',
|
|
777
|
+
'kind',
|
|
778
|
+
'ownerPid',
|
|
779
|
+
'schemaVersion',
|
|
780
|
+
]) ||
|
|
781
|
+
lease.kind !== 'ebk-container-release-builder-lease' ||
|
|
782
|
+
lease.schemaVersion !== 1 ||
|
|
783
|
+
lease.builder !== expectedName ||
|
|
784
|
+
!/^ebk-release-[a-f0-9]{32}$/.test(lease.builder) ||
|
|
785
|
+
lease.driver !== 'docker-container' ||
|
|
786
|
+
JSON.stringify(lease.cleanup) !==
|
|
787
|
+
JSON.stringify(['docker', 'buildx', 'rm', lease.builder]) ||
|
|
788
|
+
!Number.isSafeInteger(lease.ownerPid) ||
|
|
789
|
+
lease.ownerPid <= 0 ||
|
|
790
|
+
!Number.isFinite(Date.parse(lease.createdAt)) ||
|
|
791
|
+
new Date(lease.createdAt).toISOString() !== lease.createdAt
|
|
792
|
+
) {
|
|
793
|
+
throw new Error('Container-release builder lease has an invalid identity.');
|
|
794
|
+
}
|
|
795
|
+
return lease;
|
|
796
|
+
};
|
|
797
|
+
const removeReleaseBuilderSignalHandlers = () => {
|
|
798
|
+
for (const [signal, handler] of releaseBuilderSignalHandlers) {
|
|
799
|
+
process.removeListener(signal, handler);
|
|
800
|
+
}
|
|
801
|
+
releaseBuilderSignalHandlers.clear();
|
|
802
|
+
};
|
|
803
|
+
const ensureReleaseBuilderSignalHandlers = () => {
|
|
804
|
+
if (releaseBuilderSignalHandlers.size > 0) return;
|
|
805
|
+
for (const signal of ['SIGINT', 'SIGTERM']) {
|
|
806
|
+
const handler = () => void handleReleaseBuilderSignal(signal);
|
|
807
|
+
releaseBuilderSignalHandlers.set(signal, handler);
|
|
808
|
+
process.on(signal, handler);
|
|
809
|
+
}
|
|
810
|
+
};
|
|
811
|
+
const removeReleaseBuilderSignalHandlersWhenIdle = () => {
|
|
812
|
+
if (!releaseBuilder && releaseToolTemporaryDirectories.size === 0) {
|
|
813
|
+
removeReleaseBuilderSignalHandlers();
|
|
814
|
+
}
|
|
815
|
+
};
|
|
816
|
+
const createReleaseToolTemporaryDirectory = (prefix) => {
|
|
817
|
+
const created = mkdtempSync(join(tmpdir(), prefix));
|
|
818
|
+
const metadata = lstatSync(created);
|
|
819
|
+
if (
|
|
820
|
+
!metadata.isDirectory() ||
|
|
821
|
+
metadata.isSymbolicLink() ||
|
|
822
|
+
!isAbsolute(created)
|
|
823
|
+
) {
|
|
824
|
+
throw new Error('Container-release tool temporary directory is not exact.');
|
|
825
|
+
}
|
|
826
|
+
const temporary = realpathSync(created);
|
|
827
|
+
const canonicalMetadata = lstatSync(temporary);
|
|
828
|
+
if (
|
|
829
|
+
!isAbsolute(temporary) ||
|
|
830
|
+
!canonicalMetadata.isDirectory() ||
|
|
831
|
+
canonicalMetadata.isSymbolicLink() ||
|
|
832
|
+
canonicalMetadata.dev !== metadata.dev ||
|
|
833
|
+
canonicalMetadata.ino !== metadata.ino
|
|
834
|
+
) {
|
|
835
|
+
throw new Error('Container-release tool temporary directory is not canonical.');
|
|
836
|
+
}
|
|
837
|
+
releaseToolTemporaryDirectories.add(temporary);
|
|
838
|
+
ensureReleaseBuilderSignalHandlers();
|
|
839
|
+
return temporary;
|
|
840
|
+
};
|
|
841
|
+
const removeExactReleaseToolTemporaryDirectory = (temporary) => {
|
|
842
|
+
if (!releaseToolTemporaryDirectories.has(temporary)) {
|
|
843
|
+
throw new Error('Container-release tool temporary directory is not owned.');
|
|
844
|
+
}
|
|
845
|
+
const metadata = lstatSync(temporary);
|
|
846
|
+
if (
|
|
847
|
+
!metadata.isDirectory() ||
|
|
848
|
+
metadata.isSymbolicLink() ||
|
|
849
|
+
!isAbsolute(temporary) ||
|
|
850
|
+
realpathSync(temporary) !== temporary
|
|
851
|
+
) {
|
|
852
|
+
throw new Error(
|
|
853
|
+
'Container-release tool temporary directory changed identity before cleanup.',
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
rmSync(temporary, { force: true, recursive: true });
|
|
857
|
+
if (existsSync(temporary)) {
|
|
858
|
+
throw new Error('Container-release tool temporary directory cleanup failed.');
|
|
859
|
+
}
|
|
860
|
+
releaseToolTemporaryDirectories.delete(temporary);
|
|
861
|
+
removeReleaseBuilderSignalHandlersWhenIdle();
|
|
862
|
+
};
|
|
863
|
+
const removeAllReleaseToolTemporaryDirectories = () => {
|
|
864
|
+
const cleanupErrors = [];
|
|
865
|
+
for (const temporary of [...releaseToolTemporaryDirectories]) {
|
|
866
|
+
try {
|
|
867
|
+
removeExactReleaseToolTemporaryDirectory(temporary);
|
|
868
|
+
} catch (error) {
|
|
869
|
+
cleanupErrors.push(error);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
if (cleanupErrors.length > 0) {
|
|
873
|
+
throw new AggregateError(
|
|
874
|
+
cleanupErrors,
|
|
875
|
+
'Exact container-release tool temporary cleanup failed.',
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
const releaseBuilderCleanupDiagnostic = (builder) =>
|
|
880
|
+
'docker buildx rm ' + builder.name +
|
|
881
|
+
' (ownership lease: ' + builder.leasePath + ')';
|
|
882
|
+
const listExactReleaseBuilderNames = ({ signal } = {}) => {
|
|
883
|
+
const inventory = spawnSync(
|
|
884
|
+
'docker',
|
|
885
|
+
['buildx', 'ls', '--format', '{{json .Name}}'],
|
|
886
|
+
{
|
|
887
|
+
cwd: repositoryRoot,
|
|
888
|
+
encoding: 'utf8',
|
|
889
|
+
env: process.env,
|
|
890
|
+
maxBuffer: 1024 * 1024,
|
|
891
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
892
|
+
timeout: signal ? 5_000 : undefined,
|
|
893
|
+
},
|
|
894
|
+
);
|
|
895
|
+
if (inventory.error || inventory.signal || inventory.status !== 0) {
|
|
896
|
+
throw new Error(
|
|
897
|
+
'Invocation-owned Buildx inventory failed while proving exact cleanup: ' +
|
|
898
|
+
String(
|
|
899
|
+
inventory.stderr ||
|
|
900
|
+
inventory.error?.message ||
|
|
901
|
+
inventory.signal ||
|
|
902
|
+
inventory.status,
|
|
903
|
+
).slice(-2_000),
|
|
904
|
+
);
|
|
905
|
+
}
|
|
906
|
+
const lines = inventory.stdout.trim() === ''
|
|
907
|
+
? []
|
|
908
|
+
: inventory.stdout.trim().split(/\\r?\\n/u);
|
|
909
|
+
if (lines.length > 256) {
|
|
910
|
+
throw new Error('Invocation-owned Buildx inventory is unbounded.');
|
|
911
|
+
}
|
|
912
|
+
const names = new Set();
|
|
913
|
+
for (const line of lines) {
|
|
914
|
+
let name;
|
|
915
|
+
try {
|
|
916
|
+
name = JSON.parse(line);
|
|
917
|
+
} catch {
|
|
918
|
+
throw new Error('Invocation-owned Buildx inventory is not exact JSON.');
|
|
919
|
+
}
|
|
920
|
+
if (
|
|
921
|
+
typeof name !== 'string' ||
|
|
922
|
+
name.length === 0 ||
|
|
923
|
+
name.length > 256 ||
|
|
924
|
+
names.has(name)
|
|
925
|
+
) {
|
|
926
|
+
throw new Error('Invocation-owned Buildx inventory has an invalid identity.');
|
|
927
|
+
}
|
|
928
|
+
names.add(name);
|
|
929
|
+
}
|
|
930
|
+
return names;
|
|
931
|
+
};
|
|
932
|
+
const removeExactReleaseBuilder = (builder, { signal } = {}) => {
|
|
933
|
+
readReleaseBuilderLease(builder.leasePath, builder.name);
|
|
934
|
+
const cleanup = spawnSync(
|
|
935
|
+
'docker',
|
|
936
|
+
['buildx', 'rm', builder.name],
|
|
937
|
+
{
|
|
938
|
+
cwd: repositoryRoot,
|
|
939
|
+
encoding: 'utf8',
|
|
940
|
+
env: process.env,
|
|
941
|
+
maxBuffer: 1024 * 1024,
|
|
942
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
943
|
+
timeout: signal ? 5_000 : undefined,
|
|
944
|
+
},
|
|
945
|
+
);
|
|
946
|
+
if (cleanup.error || cleanup.signal || cleanup.status !== 0) {
|
|
947
|
+
let builderNames;
|
|
948
|
+
try {
|
|
949
|
+
builderNames = listExactReleaseBuilderNames({ signal });
|
|
950
|
+
} catch (inventoryError) {
|
|
951
|
+
const error = new AggregateError(
|
|
952
|
+
[
|
|
953
|
+
cleanup.error ?? new Error(String(cleanup.stderr || cleanup.status)),
|
|
954
|
+
inventoryError,
|
|
955
|
+
],
|
|
956
|
+
'Invocation-owned Buildx cleanup and exact absence proof both failed; ' +
|
|
957
|
+
'retain ' + releaseBuilderCleanupDiagnostic(builder) + '.',
|
|
958
|
+
);
|
|
959
|
+
if (signal) {
|
|
960
|
+
process.stderr.write(error.message + '\\n');
|
|
961
|
+
return false;
|
|
962
|
+
}
|
|
963
|
+
throw error;
|
|
964
|
+
}
|
|
965
|
+
if (!builderNames.has(builder.name)) {
|
|
966
|
+
unlinkSync(builder.leasePath);
|
|
967
|
+
process.stderr.write(
|
|
968
|
+
'ebk-release-builder-already-absent ' +
|
|
969
|
+
JSON.stringify({ builder: builder.name, leasePath: builder.leasePath }) +
|
|
970
|
+
'\\n',
|
|
971
|
+
);
|
|
972
|
+
return true;
|
|
973
|
+
}
|
|
974
|
+
const diagnostic = [cleanup.stdout, cleanup.stderr]
|
|
975
|
+
.filter(Boolean)
|
|
976
|
+
.join('\\n')
|
|
977
|
+
.slice(-2_000);
|
|
978
|
+
const error = new Error(
|
|
979
|
+
'Invocation-owned Buildx cleanup failed; use only the exact retained ' +
|
|
980
|
+
'hard-kill cleanup command ' + releaseBuilderCleanupDiagnostic(builder) +
|
|
981
|
+
(diagnostic ? ': ' + diagnostic : ''),
|
|
982
|
+
);
|
|
983
|
+
if (signal) {
|
|
984
|
+
process.stderr.write(error.message + '\\n');
|
|
985
|
+
return false;
|
|
986
|
+
}
|
|
987
|
+
throw error;
|
|
988
|
+
}
|
|
989
|
+
unlinkSync(builder.leasePath);
|
|
990
|
+
return true;
|
|
991
|
+
};
|
|
992
|
+
const recoverReleaseBuilders = () => {
|
|
993
|
+
const leaseRoot = releaseBuilderLeaseRoot();
|
|
994
|
+
const entries = readdirSync(leaseRoot, { withFileTypes: true }).sort(
|
|
995
|
+
(left, right) =>
|
|
996
|
+
left.name < right.name ? -1 : left.name > right.name ? 1 : 0,
|
|
997
|
+
);
|
|
998
|
+
if (entries.length > 64) {
|
|
999
|
+
throw new Error('Container-release builder lease inventory is unbounded.');
|
|
1000
|
+
}
|
|
1001
|
+
for (const entry of entries) {
|
|
1002
|
+
const matched = /^(ebk-release-[a-f0-9]{32})\\.json$/.exec(entry.name);
|
|
1003
|
+
if (!entry.isFile() || !matched) {
|
|
1004
|
+
throw new Error(
|
|
1005
|
+
'Container-release builder lease directory contains an unexpected entry.',
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
const leasePath = join(leaseRoot, entry.name);
|
|
1009
|
+
const lease = readReleaseBuilderLease(leasePath, matched[1]);
|
|
1010
|
+
const builderNames = listExactReleaseBuilderNames();
|
|
1011
|
+
if (builderNames.has(lease.builder)) {
|
|
1012
|
+
throw new Error(
|
|
1013
|
+
'A retained invocation-owned Buildx builder still exists; inspect it and use only the exact cleanup command ' +
|
|
1014
|
+
releaseBuilderCleanupDiagnostic({ leasePath, name: lease.builder }) + '.',
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
1017
|
+
unlinkSync(leasePath);
|
|
1018
|
+
process.stderr.write(
|
|
1019
|
+
'ebk-release-builder-recovered ' +
|
|
1020
|
+
JSON.stringify({ builder: lease.builder, leasePath }) +
|
|
1021
|
+
'\\n',
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
1024
|
+
};
|
|
1025
|
+
const cleanupReleaseBuilder = ({ signal } = {}) => {
|
|
1026
|
+
const builder = releaseBuilder;
|
|
1027
|
+
if (!builder) {
|
|
1028
|
+
removeReleaseBuilderSignalHandlersWhenIdle();
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
releaseBuilder = undefined;
|
|
1032
|
+
const removed = removeExactReleaseBuilder(builder, { signal });
|
|
1033
|
+
removeReleaseBuilderSignalHandlersWhenIdle();
|
|
1034
|
+
return removed;
|
|
1035
|
+
};
|
|
1036
|
+
const handleReleaseBuilderSignal = async (signal) => {
|
|
1037
|
+
if (releaseBuilderSignal) return;
|
|
1038
|
+
releaseBuilderSignal = signal;
|
|
1039
|
+
removeReleaseBuilderSignalHandlers();
|
|
1040
|
+
const cleanupErrors = [];
|
|
1041
|
+
let childStopped = false;
|
|
1042
|
+
try {
|
|
1043
|
+
await stopExactReleaseBuildChild(signal);
|
|
1044
|
+
childStopped = true;
|
|
1045
|
+
} catch (error) {
|
|
1046
|
+
cleanupErrors.push(error);
|
|
1047
|
+
}
|
|
1048
|
+
if (childStopped) {
|
|
1049
|
+
try {
|
|
1050
|
+
cleanupReleaseBuilder({ signal });
|
|
1051
|
+
} catch (error) {
|
|
1052
|
+
cleanupErrors.push(error);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
try {
|
|
1056
|
+
removeAllReleaseToolTemporaryDirectories();
|
|
1057
|
+
} catch (error) {
|
|
1058
|
+
cleanupErrors.push(error);
|
|
1059
|
+
}
|
|
1060
|
+
if (cleanupErrors.length > 0) {
|
|
1061
|
+
process.stderr.write(
|
|
1062
|
+
'Invocation-owned Buildx signal cleanup failed closed; the exact ' +
|
|
1063
|
+
'ownership lease was retained: ' +
|
|
1064
|
+
cleanupErrors
|
|
1065
|
+
.map((error) => String(error?.message ?? error))
|
|
1066
|
+
.join('; ')
|
|
1067
|
+
.slice(-2_000) + '\\n',
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
try {
|
|
1071
|
+
removeReleaseBuilderSignalHandlers();
|
|
1072
|
+
} finally {
|
|
1073
|
+
process.kill(process.pid, signal);
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
const ensureReleaseBuilder = () => {
|
|
1077
|
+
if (releaseBuilder) return releaseBuilder;
|
|
1078
|
+
const name = 'ebk-release-' + randomBytes(16).toString('hex');
|
|
1079
|
+
const leaseRoot = releaseBuilderLeaseRoot();
|
|
1080
|
+
const leasePath = join(leaseRoot, name + '.json');
|
|
1081
|
+
const builder = {
|
|
1082
|
+
leasePath,
|
|
1083
|
+
leaseRoot,
|
|
1084
|
+
name,
|
|
1085
|
+
};
|
|
1086
|
+
writeFileSync(
|
|
1087
|
+
leasePath,
|
|
1088
|
+
JSON.stringify({
|
|
1089
|
+
builder: name,
|
|
1090
|
+
cleanup: ['docker', 'buildx', 'rm', name],
|
|
1091
|
+
createdAt: new Date().toISOString(),
|
|
1092
|
+
driver: 'docker-container',
|
|
1093
|
+
kind: 'ebk-container-release-builder-lease',
|
|
1094
|
+
ownerPid: process.pid,
|
|
1095
|
+
schemaVersion: 1,
|
|
1096
|
+
}) + '\\n',
|
|
1097
|
+
{ encoding: 'utf8', flag: 'wx', mode: 0o600 },
|
|
1098
|
+
);
|
|
1099
|
+
hardenPrivateReleaseBuilderPath(leasePath, 'file');
|
|
1100
|
+
releaseBuilder = builder;
|
|
1101
|
+
ensureReleaseBuilderSignalHandlers();
|
|
1102
|
+
process.stderr.write(
|
|
1103
|
+
'ebk-release-builder-ownership ' +
|
|
1104
|
+
JSON.stringify({
|
|
1105
|
+
builder: name,
|
|
1106
|
+
hardKillCleanup: ['docker', 'buildx', 'rm', name],
|
|
1107
|
+
leasePath,
|
|
1108
|
+
}) +
|
|
1109
|
+
'\\n',
|
|
1110
|
+
);
|
|
1111
|
+
try {
|
|
1112
|
+
execute(
|
|
1113
|
+
'docker',
|
|
1114
|
+
['buildx', 'create', '--name', name, '--driver', 'docker-container'],
|
|
1115
|
+
'Invocation-owned native Buildx builder creation',
|
|
1116
|
+
);
|
|
1117
|
+
} catch (error) {
|
|
1118
|
+
try {
|
|
1119
|
+
cleanupReleaseBuilder();
|
|
1120
|
+
} catch (cleanupError) {
|
|
1121
|
+
throw new AggregateError(
|
|
1122
|
+
[error, cleanupError],
|
|
1123
|
+
'Invocation-owned Buildx creation and cleanup both failed.',
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
throw error;
|
|
1127
|
+
}
|
|
1128
|
+
return builder;
|
|
1129
|
+
};
|
|
299
1130
|
const awsJson = (args, label) => {
|
|
300
1131
|
const raw = execute('aws', args, label);
|
|
301
1132
|
try {
|
|
@@ -332,11 +1163,12 @@ const describeRepository = (repositoryName) => {
|
|
|
332
1163
|
throw new Error('ECR repository lookup returned invalid JSON.');
|
|
333
1164
|
}
|
|
334
1165
|
}
|
|
335
|
-
const error = String(result.stderr ?? '');
|
|
1166
|
+
const error = String(result.stderr ?? '').trimStart();
|
|
1167
|
+
const notFound =
|
|
1168
|
+
'An error occurred (RepositoryNotFoundException) when calling the DescribeRepositories operation:';
|
|
336
1169
|
if (
|
|
337
|
-
!error.startsWith(
|
|
338
|
-
|
|
339
|
-
)
|
|
1170
|
+
!error.startsWith(notFound) &&
|
|
1171
|
+
!error.startsWith('aws: [ERROR]: ' + notFound)
|
|
340
1172
|
) {
|
|
341
1173
|
throw new Error('ECR repository lookup failed outside the create-if-absent path.');
|
|
342
1174
|
}
|
|
@@ -349,6 +1181,18 @@ const caller = awsJson(
|
|
|
349
1181
|
if (caller?.Account !== accountId) {
|
|
350
1182
|
throw new Error('Container release AWS caller differs from the target account.');
|
|
351
1183
|
}
|
|
1184
|
+
execute(
|
|
1185
|
+
process.execPath,
|
|
1186
|
+
[
|
|
1187
|
+
safePath(
|
|
1188
|
+
'tools/stage-authority-render.mjs',
|
|
1189
|
+
'Workspace ownership doctor',
|
|
1190
|
+
),
|
|
1191
|
+
'--stage=' + stage,
|
|
1192
|
+
'--ownership-doctor=true',
|
|
1193
|
+
],
|
|
1194
|
+
'Workspace stage ownership',
|
|
1195
|
+
);
|
|
352
1196
|
const controls = readJson(
|
|
353
1197
|
safePath('operations/release.json', 'Release controls', { base: sliceRoot }),
|
|
354
1198
|
'Release controls',
|
|
@@ -404,10 +1248,17 @@ const releaseContainerInputs = declaredContainers.map((declared, index) => {
|
|
|
404
1248
|
? captured.digest
|
|
405
1249
|
: captured?.candidateDigest;
|
|
406
1250
|
const candidate = declared.candidate;
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
)
|
|
1251
|
+
// Promotion copies reviewed staging images without rebuilding, so the build
|
|
1252
|
+
// material is never present in that job; the candidate evidence restored
|
|
1253
|
+
// from the exact release artifact already binds the material digest
|
|
1254
|
+
// (EBK-INT-2026-076).
|
|
1255
|
+
const materialDigest =
|
|
1256
|
+
mode === 'promote'
|
|
1257
|
+
? candidateArtifactDigest
|
|
1258
|
+
: digestCandidatePaths(
|
|
1259
|
+
candidate.materialPaths,
|
|
1260
|
+
'OCI artifact candidate material',
|
|
1261
|
+
);
|
|
411
1262
|
const buildRecipeDigest = digestCandidatePaths(
|
|
412
1263
|
candidate.recipePaths,
|
|
413
1264
|
'OCI artifact candidate recipe',
|
|
@@ -684,18 +1535,132 @@ const exactImageByTag = (repositoryName, tag) => {
|
|
|
684
1535
|
}
|
|
685
1536
|
return response.imageDetails[0].imageDigest;
|
|
686
1537
|
};
|
|
1538
|
+
const exactImageByDigest = (repositoryName, imageDigest) => {
|
|
1539
|
+
const response = awsJson(
|
|
1540
|
+
[
|
|
1541
|
+
'ecr',
|
|
1542
|
+
'describe-images',
|
|
1543
|
+
'--registry-id',
|
|
1544
|
+
accountId,
|
|
1545
|
+
'--repository-name',
|
|
1546
|
+
repositoryName,
|
|
1547
|
+
'--image-ids',
|
|
1548
|
+
'imageDigest=' + imageDigest,
|
|
1549
|
+
'--region',
|
|
1550
|
+
region,
|
|
1551
|
+
'--output',
|
|
1552
|
+
'json',
|
|
1553
|
+
],
|
|
1554
|
+
'Exact ECR image digest lookup',
|
|
1555
|
+
);
|
|
1556
|
+
return (
|
|
1557
|
+
Array.isArray(response?.imageDetails) &&
|
|
1558
|
+
response.imageDetails.length === 1 &&
|
|
1559
|
+
response.imageDetails[0]?.registryId === accountId &&
|
|
1560
|
+
response.imageDetails[0]?.repositoryName === repositoryName &&
|
|
1561
|
+
response.imageDetails[0]?.imageDigest === imageDigest
|
|
1562
|
+
);
|
|
1563
|
+
};
|
|
1564
|
+
// An unchanged artifact (same candidate material, recipe, Dockerfile, platform,
|
|
1565
|
+
// and production closure) may reuse the image and SBOM retained by the newest
|
|
1566
|
+
// successful deployment on this branch instead of rebuilding. Declining reuse
|
|
1567
|
+
// is always safe: the full native build runs. The native run proof is always
|
|
1568
|
+
// re-executed against the reused image.
|
|
1569
|
+
const reusableBaselinePlatformRecord = (input, requestedPlatform, architecture, repositoryName, expectedClosure) => {
|
|
1570
|
+
const root = optionalCacheRoot('EBK_CONTAINER_BASELINE_ROOT');
|
|
1571
|
+
if (root === '' || process.env.EBK_OCI_REBUILD?.trim() === 'all') return undefined;
|
|
1572
|
+
const safeArtifact = input.declared.name.replace(/[^A-Za-z0-9._-]/g, '_');
|
|
1573
|
+
const recordPath = join(
|
|
1574
|
+
root,
|
|
1575
|
+
'container-release',
|
|
1576
|
+
'partials',
|
|
1577
|
+
stage,
|
|
1578
|
+
slice,
|
|
1579
|
+
architecture,
|
|
1580
|
+
safeArtifact + '.platform.json',
|
|
1581
|
+
);
|
|
1582
|
+
const decline = (reason) => {
|
|
1583
|
+
process.stderr.write(
|
|
1584
|
+
'ebk-container-release-reuse ' +
|
|
1585
|
+
JSON.stringify({ artifact: input.declared.id, platform: requestedPlatform, reason, reused: false }) +
|
|
1586
|
+
'\\n',
|
|
1587
|
+
);
|
|
1588
|
+
return undefined;
|
|
1589
|
+
};
|
|
1590
|
+
if (!existsSync(recordPath) || lstatSync(recordPath).isSymbolicLink()) {
|
|
1591
|
+
return decline('no baseline platform record');
|
|
1592
|
+
}
|
|
1593
|
+
let record;
|
|
1594
|
+
try {
|
|
1595
|
+
record = validateContainerPlatformEvidence(JSON.parse(readFileSync(recordPath, 'utf8')));
|
|
1596
|
+
} catch (error) {
|
|
1597
|
+
return decline('baseline platform record is invalid: ' + String(error?.message ?? error));
|
|
1598
|
+
}
|
|
1599
|
+
if (
|
|
1600
|
+
record.accountId !== accountId ||
|
|
1601
|
+
record.region !== region ||
|
|
1602
|
+
record.stage !== stage ||
|
|
1603
|
+
record.slice !== slice ||
|
|
1604
|
+
record.artifactId !== input.declared.id ||
|
|
1605
|
+
record.artifactName !== input.declared.name ||
|
|
1606
|
+
record.platform !== requestedPlatform ||
|
|
1607
|
+
record.repositoryName !== repositoryName ||
|
|
1608
|
+
record.candidateArtifactDigest !== input.candidateArtifactDigest ||
|
|
1609
|
+
record.dockerfileDigest !== digestBytes(readFileSync(input.dockerfile)) ||
|
|
1610
|
+
record.productionClosureDigest !== expectedClosure.digest ||
|
|
1611
|
+
!IMAGE_DIGEST.test(record.imageDigest) ||
|
|
1612
|
+
record.imageReference !== registry + '/' + repositoryName + '@' + record.imageDigest
|
|
1613
|
+
) {
|
|
1614
|
+
return decline('artifact inputs changed since the baseline');
|
|
1615
|
+
}
|
|
1616
|
+
const evidencePrefix = '.ebk/evidence/';
|
|
1617
|
+
const sbomPath = join(
|
|
1618
|
+
root,
|
|
1619
|
+
record.sbom.path.startsWith(evidencePrefix)
|
|
1620
|
+
? record.sbom.path.slice(evidencePrefix.length)
|
|
1621
|
+
: record.sbom.path,
|
|
1622
|
+
);
|
|
1623
|
+
if (!existsSync(sbomPath) || lstatSync(sbomPath).isSymbolicLink()) {
|
|
1624
|
+
return decline('baseline SBOM is absent');
|
|
1625
|
+
}
|
|
1626
|
+
const sbomBytes = readFileSync(sbomPath);
|
|
1627
|
+
if (digestBytes(sbomBytes) !== record.sbom.sha256) {
|
|
1628
|
+
return decline('baseline SBOM bytes differ from the retained record');
|
|
1629
|
+
}
|
|
1630
|
+
if (!exactImageByDigest(repositoryName, record.imageDigest)) {
|
|
1631
|
+
return decline('baseline image is no longer in the registry');
|
|
1632
|
+
}
|
|
1633
|
+
process.stderr.write(
|
|
1634
|
+
'ebk-container-release-reuse ' +
|
|
1635
|
+
JSON.stringify({
|
|
1636
|
+
artifact: input.declared.id,
|
|
1637
|
+
baselineCommit: record.sourceCommit,
|
|
1638
|
+
imageDigest: record.imageDigest,
|
|
1639
|
+
platform: requestedPlatform,
|
|
1640
|
+
reused: true,
|
|
1641
|
+
}) +
|
|
1642
|
+
'\\n',
|
|
1643
|
+
);
|
|
1644
|
+
return { imageDigest: record.imageDigest, sbomBytes };
|
|
1645
|
+
};
|
|
687
1646
|
const acquireSyft = async () => {
|
|
688
1647
|
const platformKey = process.platform + '-' + process.arch;
|
|
689
1648
|
const release = SYFT_RELEASES[platformKey];
|
|
690
1649
|
if (!release) {
|
|
691
1650
|
throw new Error('No reviewed Syft release exists for ' + platformKey + '.');
|
|
692
1651
|
}
|
|
693
|
-
const temporary =
|
|
1652
|
+
const temporary = createReleaseToolTemporaryDirectory('ebk-container-syft-');
|
|
1653
|
+
try {
|
|
694
1654
|
const supplied = process.env.EBK_SYFT_ARCHIVE_PATH?.trim();
|
|
695
1655
|
const archive = supplied
|
|
696
1656
|
? safePath(relative(repositoryRoot, resolve(repositoryRoot, supplied)), 'Supplied Syft archive')
|
|
697
1657
|
: join(temporary, 'syft-archive');
|
|
698
|
-
|
|
1658
|
+
const cachedArchive = supplied ? '' : cachedReleaseToolArchive('syft', release.sha256);
|
|
1659
|
+
if (!supplied && cachedArchive && existsSync(cachedArchive)) {
|
|
1660
|
+
// Reuse the archive verified by an earlier invocation on this runner; the
|
|
1661
|
+
// digest check below still runs against the copied bytes.
|
|
1662
|
+
copyFileSync(cachedArchive, archive);
|
|
1663
|
+
} else if (!supplied) {
|
|
699
1664
|
const response = await fetch(release.source, {
|
|
700
1665
|
redirect: 'follow',
|
|
701
1666
|
signal: AbortSignal.timeout(120_000),
|
|
@@ -720,6 +1685,10 @@ const acquireSyft = async () => {
|
|
|
720
1685
|
) {
|
|
721
1686
|
throw new Error('Pinned Syft archive integrity does not match policy.');
|
|
722
1687
|
}
|
|
1688
|
+
if (cachedArchive && !existsSync(cachedArchive)) {
|
|
1689
|
+
mkdirSync(optionalCacheRoot('EBK_RELEASE_TOOL_CACHE_DIR'), { mode: 0o700, recursive: true });
|
|
1690
|
+
writeFileSync(cachedArchive, bytes, { mode: 0o600 });
|
|
1691
|
+
}
|
|
723
1692
|
const path = join(temporary, 'syft');
|
|
724
1693
|
if (release.archiveFormat === 'binary') {
|
|
725
1694
|
writeFileSync(path, bytes, { mode: 0o700 });
|
|
@@ -728,6 +1697,7 @@ const acquireSyft = async () => {
|
|
|
728
1697
|
chmodSync(path, 0o700);
|
|
729
1698
|
}
|
|
730
1699
|
const version = String(execute(path, ['version'], 'Syft version'));
|
|
1700
|
+
await yieldToReleaseSignals();
|
|
731
1701
|
if (
|
|
732
1702
|
!new RegExp('(?:Version|version):?\\\\s*' + SYFT_VERSION.replaceAll('.', '\\\\.')).test(
|
|
733
1703
|
version,
|
|
@@ -735,7 +1705,18 @@ const acquireSyft = async () => {
|
|
|
735
1705
|
) {
|
|
736
1706
|
throw new Error('Syft runtime version differs from reviewed policy.');
|
|
737
1707
|
}
|
|
738
|
-
|
|
1708
|
+
return { path, temporary };
|
|
1709
|
+
} catch (error) {
|
|
1710
|
+
try {
|
|
1711
|
+
removeExactReleaseToolTemporaryDirectory(temporary);
|
|
1712
|
+
} catch (cleanupError) {
|
|
1713
|
+
throw new AggregateError(
|
|
1714
|
+
[error, cleanupError],
|
|
1715
|
+
'Syft acquisition and exact temporary cleanup both failed.',
|
|
1716
|
+
);
|
|
1717
|
+
}
|
|
1718
|
+
throw error;
|
|
1719
|
+
}
|
|
739
1720
|
};
|
|
740
1721
|
const acquireCrane = async () => {
|
|
741
1722
|
const platformKey = process.platform + '-' + process.arch;
|
|
@@ -743,7 +1724,8 @@ const acquireCrane = async () => {
|
|
|
743
1724
|
if (!release) {
|
|
744
1725
|
throw new Error('No reviewed Crane release exists for ' + platformKey + '.');
|
|
745
1726
|
}
|
|
746
|
-
const temporary =
|
|
1727
|
+
const temporary = createReleaseToolTemporaryDirectory('ebk-container-crane-');
|
|
1728
|
+
try {
|
|
747
1729
|
const supplied = process.env.EBK_CRANE_ARCHIVE_PATH?.trim();
|
|
748
1730
|
const archive = supplied
|
|
749
1731
|
? safePath(
|
|
@@ -751,7 +1733,10 @@ const acquireCrane = async () => {
|
|
|
751
1733
|
'Supplied Crane archive',
|
|
752
1734
|
)
|
|
753
1735
|
: join(temporary, 'crane-archive.tar.gz');
|
|
754
|
-
|
|
1736
|
+
const cachedArchive = supplied ? '' : cachedReleaseToolArchive('crane', release.sha256);
|
|
1737
|
+
if (!supplied && cachedArchive && existsSync(cachedArchive)) {
|
|
1738
|
+
copyFileSync(cachedArchive, archive);
|
|
1739
|
+
} else if (!supplied) {
|
|
755
1740
|
const response = await fetch(release.source, {
|
|
756
1741
|
redirect: 'follow',
|
|
757
1742
|
signal: AbortSignal.timeout(120_000),
|
|
@@ -776,10 +1761,15 @@ const acquireCrane = async () => {
|
|
|
776
1761
|
) {
|
|
777
1762
|
throw new Error('Pinned Crane archive integrity does not match policy.');
|
|
778
1763
|
}
|
|
1764
|
+
if (cachedArchive && !existsSync(cachedArchive)) {
|
|
1765
|
+
mkdirSync(optionalCacheRoot('EBK_RELEASE_TOOL_CACHE_DIR'), { mode: 0o700, recursive: true });
|
|
1766
|
+
writeFileSync(cachedArchive, bytes, { mode: 0o600 });
|
|
1767
|
+
}
|
|
779
1768
|
execute('tar', ['-xzf', archive, '-C', temporary, 'crane'], 'Crane extraction');
|
|
780
1769
|
const path = join(temporary, 'crane');
|
|
781
1770
|
chmodSync(path, 0o700);
|
|
782
1771
|
const version = String(execute(path, ['version'], 'Crane version')).trim();
|
|
1772
|
+
await yieldToReleaseSignals();
|
|
783
1773
|
if (
|
|
784
1774
|
!new RegExp('(?:^|\\s|v)' + CRANE_VERSION.replaceAll('.', '\\.') + '(?:$|\\s)').test(
|
|
785
1775
|
version,
|
|
@@ -787,12 +1777,23 @@ const acquireCrane = async () => {
|
|
|
787
1777
|
) {
|
|
788
1778
|
throw new Error('Crane runtime version differs from reviewed policy.');
|
|
789
1779
|
}
|
|
790
|
-
|
|
1780
|
+
return { path, temporary };
|
|
1781
|
+
} catch (error) {
|
|
1782
|
+
try {
|
|
1783
|
+
removeExactReleaseToolTemporaryDirectory(temporary);
|
|
1784
|
+
} catch (cleanupError) {
|
|
1785
|
+
throw new AggregateError(
|
|
1786
|
+
[error, cleanupError],
|
|
1787
|
+
'Crane acquisition and exact temporary cleanup both failed.',
|
|
1788
|
+
);
|
|
1789
|
+
}
|
|
1790
|
+
throw error;
|
|
1791
|
+
}
|
|
791
1792
|
};
|
|
792
1793
|
const parseCycloneDx = (value, label) => {
|
|
793
1794
|
if (
|
|
794
1795
|
value?.bomFormat !== 'CycloneDX' ||
|
|
795
|
-
value.specVersion !== '1.
|
|
1796
|
+
value.specVersion !== '1.7' ||
|
|
796
1797
|
value.version !== 1 ||
|
|
797
1798
|
!/^urn:uuid:[a-fA-F0-9-]{36}$/.test(value.serialNumber ?? '') ||
|
|
798
1799
|
!Array.isArray(value.components) ||
|
|
@@ -801,17 +1802,30 @@ const parseCycloneDx = (value, label) => {
|
|
|
801
1802
|
!value.metadata ||
|
|
802
1803
|
typeof value.metadata !== 'object'
|
|
803
1804
|
) {
|
|
804
|
-
throw new Error(label + ' is not complete CycloneDX 1.
|
|
1805
|
+
throw new Error(label + ' is not complete CycloneDX 1.7.');
|
|
805
1806
|
}
|
|
806
1807
|
return value;
|
|
807
1808
|
};
|
|
1809
|
+
const isVersionlessSyftFileComponent = (component) =>
|
|
1810
|
+
component?.type === 'file' &&
|
|
1811
|
+
component.version === undefined &&
|
|
1812
|
+
component.purl === undefined &&
|
|
1813
|
+
typeof component.name === 'string' &&
|
|
1814
|
+
component.name.startsWith('/') &&
|
|
1815
|
+
component.name.length <= 4_096 &&
|
|
1816
|
+
Array.isArray(component.hashes) &&
|
|
1817
|
+
component.hashes.length === 2 &&
|
|
1818
|
+
component.hashes[0]?.alg === 'SHA-1' &&
|
|
1819
|
+
/^[a-f0-9]{40}$/.test(component.hashes[0]?.content ?? '') &&
|
|
1820
|
+
component.hashes[1]?.alg === 'SHA-256' &&
|
|
1821
|
+
/^[a-f0-9]{64}$/.test(component.hashes[1]?.content ?? '');
|
|
808
1822
|
const componentSetFor = (sbom) =>
|
|
809
1823
|
sbom.components
|
|
810
1824
|
.map((component) => ({
|
|
811
1825
|
name: component?.name,
|
|
812
1826
|
purl: component?.purl ?? '',
|
|
813
1827
|
type: component?.type,
|
|
814
|
-
version: component?.version,
|
|
1828
|
+
version: component?.version ?? '',
|
|
815
1829
|
}))
|
|
816
1830
|
.sort((left, right) =>
|
|
817
1831
|
JSON.stringify(left) < JSON.stringify(right)
|
|
@@ -874,15 +1888,19 @@ const npmClosureFor = (syftDocument, options = {}) => {
|
|
|
874
1888
|
}
|
|
875
1889
|
return unique;
|
|
876
1890
|
};
|
|
877
|
-
const deriveExpectedProductionClosure = (syft, input) => {
|
|
878
|
-
const temporary =
|
|
1891
|
+
const deriveExpectedProductionClosure = async (syft, input) => {
|
|
1892
|
+
const temporary = createReleaseToolTemporaryDirectory('ebk-container-closure-');
|
|
879
1893
|
const deployed = join(temporary, 'deployed');
|
|
880
1894
|
try {
|
|
881
1895
|
execute(
|
|
882
1896
|
'pnpm',
|
|
883
1897
|
[
|
|
1898
|
+
'--config.store-dir=' +
|
|
1899
|
+
safePath('.ebk/cache/pnpm-store', 'Workspace pnpm store'),
|
|
1900
|
+
'--config.inject-workspace-packages=true',
|
|
884
1901
|
'--filter',
|
|
885
1902
|
input.packageName,
|
|
1903
|
+
'--trust-lockfile',
|
|
886
1904
|
'deploy',
|
|
887
1905
|
'--prod',
|
|
888
1906
|
'--offline',
|
|
@@ -890,38 +1908,79 @@ const deriveExpectedProductionClosure = (syft, input) => {
|
|
|
890
1908
|
deployed,
|
|
891
1909
|
],
|
|
892
1910
|
'Independent production closure deployment',
|
|
893
|
-
{
|
|
1911
|
+
{
|
|
1912
|
+
env: { COREPACK_ENABLE_DOWNLOAD_PROMPT: '0' },
|
|
1913
|
+
failureDiagnostics: 'bounded-closure',
|
|
1914
|
+
},
|
|
894
1915
|
);
|
|
1916
|
+
await yieldToReleaseSignals();
|
|
895
1917
|
const raw = String(
|
|
896
1918
|
execute(
|
|
897
1919
|
syft.path,
|
|
898
1920
|
['scan', 'dir:' + deployed, '--output', 'syft-json'],
|
|
899
1921
|
'Independent production closure inventory',
|
|
900
|
-
{
|
|
1922
|
+
{
|
|
1923
|
+
env: { SYFT_CHECK_FOR_APP_UPDATE: 'false' },
|
|
1924
|
+
failureDiagnostics: 'bounded-closure',
|
|
1925
|
+
},
|
|
901
1926
|
),
|
|
902
1927
|
);
|
|
1928
|
+
await yieldToReleaseSignals();
|
|
903
1929
|
let inventory;
|
|
904
1930
|
try {
|
|
905
1931
|
inventory = JSON.parse(raw);
|
|
906
1932
|
} catch {
|
|
907
1933
|
throw new Error('Independent production closure inventory is invalid JSON.');
|
|
908
1934
|
}
|
|
1935
|
+
const sourceManifest = readJson(
|
|
1936
|
+
input.packageManifestPath,
|
|
1937
|
+
'Container package manifest',
|
|
1938
|
+
);
|
|
1939
|
+
const deployedManifest = readJson(
|
|
1940
|
+
join(deployed, 'package.json'),
|
|
1941
|
+
'Deployed container package manifest',
|
|
1942
|
+
);
|
|
1943
|
+
if (
|
|
1944
|
+
sourceManifest.name !== input.packageName ||
|
|
1945
|
+
typeof sourceManifest.version !== 'string' ||
|
|
1946
|
+
sourceManifest.version.length === 0 ||
|
|
1947
|
+
sourceManifest.version.length > 128 ||
|
|
1948
|
+
deployedManifest.name !== sourceManifest.name ||
|
|
1949
|
+
deployedManifest.version !== sourceManifest.version
|
|
1950
|
+
) {
|
|
1951
|
+
throw new Error(
|
|
1952
|
+
'Independent production closure changed the workload package identity.',
|
|
1953
|
+
);
|
|
1954
|
+
}
|
|
909
1955
|
const packages = npmClosureFor(inventory);
|
|
910
1956
|
if (
|
|
911
|
-
|
|
1957
|
+
packages.some(
|
|
912
1958
|
({ name, version }) =>
|
|
913
|
-
name ===
|
|
914
|
-
version === readJson(
|
|
915
|
-
input.packageManifestPath,
|
|
916
|
-
'Container package manifest',
|
|
917
|
-
).version,
|
|
1959
|
+
name === sourceManifest.name && version !== sourceManifest.version,
|
|
918
1960
|
)
|
|
919
1961
|
) {
|
|
920
|
-
throw new Error(
|
|
1962
|
+
throw new Error(
|
|
1963
|
+
'Independent production closure contains a conflicting workload package identity.',
|
|
1964
|
+
);
|
|
1965
|
+
}
|
|
1966
|
+
if (!packages.some(({ name }) => name === sourceManifest.name)) {
|
|
1967
|
+
packages.push({
|
|
1968
|
+
name: sourceManifest.name,
|
|
1969
|
+
version: sourceManifest.version,
|
|
1970
|
+
});
|
|
1971
|
+
packages.sort(
|
|
1972
|
+
(left, right) =>
|
|
1973
|
+
(left.name < right.name ? -1 : left.name > right.name ? 1 : 0) ||
|
|
1974
|
+
(left.version < right.version
|
|
1975
|
+
? -1
|
|
1976
|
+
: left.version > right.version
|
|
1977
|
+
? 1
|
|
1978
|
+
: 0),
|
|
1979
|
+
);
|
|
921
1980
|
}
|
|
922
1981
|
return { digest: supplyChainDigest(packages), packages };
|
|
923
1982
|
} finally {
|
|
924
|
-
|
|
1983
|
+
removeExactReleaseToolTemporaryDirectory(temporary);
|
|
925
1984
|
}
|
|
926
1985
|
};
|
|
927
1986
|
const validateRetainedSbom = (entry, label) => {
|
|
@@ -1210,13 +2269,26 @@ if (stage === 'development') {
|
|
|
1210
2269
|
process.env.EBK_STAGING_PROMOTION_EXPECTED_ACCOUNT_ID?.trim() ?? '';
|
|
1211
2270
|
const expectedSourceRegion =
|
|
1212
2271
|
process.env.EBK_STAGING_PROMOTION_REGION?.trim() ?? '';
|
|
2272
|
+
const deploymentProfile = process.env.EBK_DEPLOYMENT_PROFILE?.trim() ?? '';
|
|
2273
|
+
if (
|
|
2274
|
+
deploymentProfile !== 'starter' &&
|
|
2275
|
+
deploymentProfile !== 'production-isolated' &&
|
|
2276
|
+
deploymentProfile !== 'fully-isolated'
|
|
2277
|
+
) {
|
|
2278
|
+
throw new Error('Container promotion requires one exact deployment profile.');
|
|
2279
|
+
}
|
|
2280
|
+
const sameAccountPromotion = deploymentProfile === 'starter';
|
|
1213
2281
|
if (
|
|
1214
2282
|
!/^\\d{12}$/.test(expectedSourceAccountId) ||
|
|
1215
2283
|
!AWS_REGION_NAME.test(expectedSourceRegion) ||
|
|
1216
|
-
|
|
2284
|
+
(sameAccountPromotion
|
|
2285
|
+
? expectedSourceAccountId !== accountId || expectedSourceRegion !== region
|
|
2286
|
+
: expectedSourceAccountId === accountId)
|
|
1217
2287
|
) {
|
|
1218
2288
|
throw new Error(
|
|
1219
|
-
|
|
2289
|
+
sameAccountPromotion
|
|
2290
|
+
? 'Starter promotion requires the exact shared account and Region.'
|
|
2291
|
+
: 'Isolated promotion requires one independent staging account and exact Region.',
|
|
1220
2292
|
);
|
|
1221
2293
|
}
|
|
1222
2294
|
const sourceBindingPath = safePath(
|
|
@@ -1310,13 +2382,26 @@ if (mode === 'promote') {
|
|
|
1310
2382
|
process.env.EBK_STAGING_PROMOTION_EXPECTED_ACCOUNT_ID?.trim() ?? '';
|
|
1311
2383
|
const expectedSourceRegion =
|
|
1312
2384
|
process.env.EBK_STAGING_PROMOTION_REGION?.trim() ?? '';
|
|
2385
|
+
const deploymentProfile = process.env.EBK_DEPLOYMENT_PROFILE?.trim() ?? '';
|
|
2386
|
+
if (
|
|
2387
|
+
deploymentProfile !== 'starter' &&
|
|
2388
|
+
deploymentProfile !== 'production-isolated' &&
|
|
2389
|
+
deploymentProfile !== 'fully-isolated'
|
|
2390
|
+
) {
|
|
2391
|
+
throw new Error('OCI promotion requires one exact deployment profile.');
|
|
2392
|
+
}
|
|
2393
|
+
const sameAccountPromotion = deploymentProfile === 'starter';
|
|
1313
2394
|
if (
|
|
1314
2395
|
!/^\\d{12}$/.test(expectedSourceAccountId) ||
|
|
1315
2396
|
!AWS_REGION_NAME.test(expectedSourceRegion) ||
|
|
1316
|
-
|
|
2397
|
+
(sameAccountPromotion
|
|
2398
|
+
? expectedSourceAccountId !== accountId || expectedSourceRegion !== region
|
|
2399
|
+
: expectedSourceAccountId === accountId)
|
|
1317
2400
|
) {
|
|
1318
2401
|
throw new Error(
|
|
1319
|
-
|
|
2402
|
+
sameAccountPromotion
|
|
2403
|
+
? 'Starter OCI promotion requires the exact shared account and Region.'
|
|
2404
|
+
: 'Isolated OCI promotion requires one bounded independent staging account and exact Region.',
|
|
1320
2405
|
);
|
|
1321
2406
|
}
|
|
1322
2407
|
const sourceBindingInput =
|
|
@@ -1573,6 +2658,7 @@ if (mode === 'promote') {
|
|
|
1573
2658
|
slice,
|
|
1574
2659
|
'binding.json',
|
|
1575
2660
|
);
|
|
2661
|
+
await yieldToReleaseSignals();
|
|
1576
2662
|
immutableWrite(output, binding);
|
|
1577
2663
|
process.stdout.write(
|
|
1578
2664
|
JSON.stringify({
|
|
@@ -1585,30 +2671,41 @@ if (mode === 'promote') {
|
|
|
1585
2671
|
}) + '\\n',
|
|
1586
2672
|
);
|
|
1587
2673
|
} finally {
|
|
1588
|
-
|
|
2674
|
+
removeExactReleaseToolTemporaryDirectory(crane.temporary);
|
|
1589
2675
|
}
|
|
1590
2676
|
process.exit(0);
|
|
1591
2677
|
}
|
|
1592
2678
|
|
|
1593
2679
|
if (mode === 'platform') {
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
requestedPlatform !== 'linux/' + nativeArchitecture
|
|
1598
|
-
) {
|
|
1599
|
-
throw new Error(
|
|
1600
|
-
'Platform evidence must run on the matching native Linux architecture.',
|
|
1601
|
-
);
|
|
1602
|
-
}
|
|
1603
|
-
const syft = await acquireSyft();
|
|
1604
|
-
const crane = stage === 'development' ? await acquireCrane() : undefined;
|
|
2680
|
+
let syft;
|
|
2681
|
+
let crane;
|
|
2682
|
+
let platformError;
|
|
1605
2683
|
try {
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
);
|
|
2684
|
+
syft = await acquireSyft();
|
|
2685
|
+
crane =
|
|
2686
|
+
stage === 'development' || optionalCacheRoot('EBK_CONTAINER_BASELINE_ROOT') !== ''
|
|
2687
|
+
? await acquireCrane()
|
|
2688
|
+
: undefined;
|
|
2689
|
+
recoverReleaseBuilders();
|
|
2690
|
+
// Artifacts that share a package, recipe, and Dockerfile (the migration and
|
|
2691
|
+
// retirement jobs) resolve one identical production closure.
|
|
2692
|
+
const expectedClosures = new Map();
|
|
2693
|
+
const closuresByRecipe = new Map();
|
|
2694
|
+
for (const input of containerInputs) {
|
|
2695
|
+
const recipeKey =
|
|
2696
|
+
input.packageName +
|
|
2697
|
+
':' +
|
|
2698
|
+
input.buildRecipeDigest +
|
|
2699
|
+
':' +
|
|
2700
|
+
digestBytes(readFileSync(input.dockerfile));
|
|
2701
|
+
if (!closuresByRecipe.has(recipeKey)) {
|
|
2702
|
+
closuresByRecipe.set(
|
|
2703
|
+
recipeKey,
|
|
2704
|
+
await deriveExpectedProductionClosure(syft, input),
|
|
2705
|
+
);
|
|
2706
|
+
}
|
|
2707
|
+
expectedClosures.set(input.declared.id, closuresByRecipe.get(recipeKey));
|
|
2708
|
+
}
|
|
1612
2709
|
requireReviewedCheckout();
|
|
1613
2710
|
for (const input of containerInputs) requireCandidateAuthority(input);
|
|
1614
2711
|
login();
|
|
@@ -1639,7 +2736,24 @@ if (mode === 'platform') {
|
|
|
1639
2736
|
({ platform }) => platform === requestedPlatform,
|
|
1640
2737
|
)
|
|
1641
2738
|
: undefined;
|
|
1642
|
-
|
|
2739
|
+
const reusable =
|
|
2740
|
+
stage === 'development'
|
|
2741
|
+
? undefined
|
|
2742
|
+
: reusableBaselinePlatformRecord(
|
|
2743
|
+
input,
|
|
2744
|
+
requestedPlatform,
|
|
2745
|
+
architecture,
|
|
2746
|
+
repositoryName,
|
|
2747
|
+
expectedClosure,
|
|
2748
|
+
);
|
|
2749
|
+
if (reusable) {
|
|
2750
|
+
if (!crane) throw new Error('Unchanged artifact reuse requires the reviewed crane.');
|
|
2751
|
+
execute(
|
|
2752
|
+
crane.path,
|
|
2753
|
+
['copy', registry + '/' + repositoryName + '@' + reusable.imageDigest, taggedReference],
|
|
2754
|
+
'Unchanged artifact re-tag without rebuilding',
|
|
2755
|
+
);
|
|
2756
|
+
} else if (stage === 'development') {
|
|
1643
2757
|
if (!crane || !developmentSourcePlatform) {
|
|
1644
2758
|
throw new Error(
|
|
1645
2759
|
'Development retirement source platform is absent.',
|
|
@@ -1651,32 +2765,46 @@ if (mode === 'platform') {
|
|
|
1651
2765
|
'Exact staging retirement child promotion',
|
|
1652
2766
|
);
|
|
1653
2767
|
} else {
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
'
|
|
1676
|
-
|
|
2768
|
+
// One BuildKit builder serves every artifact of this invocation so
|
|
2769
|
+
// shared layers (pnpm fetch, filtered install) are reused across the
|
|
2770
|
+
// Slice's images; the platform-mode finally block removes it.
|
|
2771
|
+
const platformBuilder = ensureReleaseBuilder();
|
|
2772
|
+
const layerCache = buildxLayerCache(repositoryName, architecture);
|
|
2773
|
+
await executeObservedBuildx([
|
|
2774
|
+
'buildx',
|
|
2775
|
+
'build',
|
|
2776
|
+
'--builder',
|
|
2777
|
+
platformBuilder.name,
|
|
2778
|
+
...(packageRegistry
|
|
2779
|
+
? [
|
|
2780
|
+
'--build-arg',
|
|
2781
|
+
'NPM_CONFIG_REGISTRY=' + packageRegistry,
|
|
2782
|
+
]
|
|
2783
|
+
: []),
|
|
2784
|
+
...layerCache.arguments,
|
|
2785
|
+
'--file',
|
|
2786
|
+
relative(repositoryRoot, input.dockerfile),
|
|
2787
|
+
'--label',
|
|
2788
|
+
'org.opencontainers.image.revision=' + sourceCommit,
|
|
2789
|
+
'--label',
|
|
2790
|
+
'dev.empire-builder-kit.candidate-digest=' +
|
|
2791
|
+
input.candidateArtifactDigest,
|
|
2792
|
+
'--platform',
|
|
2793
|
+
requestedPlatform,
|
|
2794
|
+
'--provenance=false',
|
|
2795
|
+
'--push',
|
|
2796
|
+
'--sbom=false',
|
|
2797
|
+
'--tag',
|
|
2798
|
+
taggedReference,
|
|
2799
|
+
relative(repositoryRoot, input.context) || '.',
|
|
2800
|
+
], 'Native container build and push');
|
|
2801
|
+
layerCache.commit();
|
|
1677
2802
|
}
|
|
1678
2803
|
const imageDigest = exactImageByTag(repositoryName, tag);
|
|
1679
2804
|
const imageReference = registry + '/' + repositoryName + '@' + imageDigest;
|
|
2805
|
+
if (reusable && reusable.imageDigest !== imageDigest) {
|
|
2806
|
+
throw new Error('Unchanged artifact re-tag differs from the baseline image.');
|
|
2807
|
+
}
|
|
1680
2808
|
if (
|
|
1681
2809
|
stage === 'development' &&
|
|
1682
2810
|
developmentSourcePlatform?.imageDigest !== imageDigest
|
|
@@ -1752,39 +2880,43 @@ if (mode === 'platform') {
|
|
|
1752
2880
|
resolve(repositoryRoot, relativeRunProofPath),
|
|
1753
2881
|
serializedRunProof,
|
|
1754
2882
|
);
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
let closureInventory;
|
|
1764
|
-
try {
|
|
1765
|
-
closureInventory = JSON.parse(closureRaw);
|
|
1766
|
-
} catch {
|
|
1767
|
-
throw new Error('Exact application npm closure inventory is invalid JSON.');
|
|
1768
|
-
}
|
|
1769
|
-
const observedClosure = npmClosureFor(closureInventory, {
|
|
1770
|
-
applicationOnly: true,
|
|
1771
|
-
});
|
|
1772
|
-
if (
|
|
1773
|
-
JSON.stringify(observedClosure) !==
|
|
1774
|
-
JSON.stringify(expectedClosure.packages)
|
|
1775
|
-
) {
|
|
1776
|
-
throw new Error(
|
|
1777
|
-
'Container application npm closure differs from the independently deployed production closure.',
|
|
2883
|
+
if (!reusable) {
|
|
2884
|
+
const closureRaw = String(
|
|
2885
|
+
execute(
|
|
2886
|
+
syft.path,
|
|
2887
|
+
['scan', 'registry:' + imageReference, '--output', 'syft-json'],
|
|
2888
|
+
'Exact application npm closure inventory',
|
|
2889
|
+
{ env: { SYFT_CHECK_FOR_APP_UPDATE: 'false' } },
|
|
2890
|
+
),
|
|
1778
2891
|
);
|
|
2892
|
+
let closureInventory;
|
|
2893
|
+
try {
|
|
2894
|
+
closureInventory = JSON.parse(closureRaw);
|
|
2895
|
+
} catch {
|
|
2896
|
+
throw new Error('Exact application npm closure inventory is invalid JSON.');
|
|
2897
|
+
}
|
|
2898
|
+
const observedClosure = npmClosureFor(closureInventory, {
|
|
2899
|
+
applicationOnly: true,
|
|
2900
|
+
});
|
|
2901
|
+
if (
|
|
2902
|
+
JSON.stringify(observedClosure) !==
|
|
2903
|
+
JSON.stringify(expectedClosure.packages)
|
|
2904
|
+
) {
|
|
2905
|
+
throw new Error(
|
|
2906
|
+
'Container application npm closure differs from the independently deployed production closure.',
|
|
2907
|
+
);
|
|
2908
|
+
}
|
|
1779
2909
|
}
|
|
1780
|
-
const rawSbom =
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
2910
|
+
const rawSbom = reusable
|
|
2911
|
+
? reusable.sbomBytes.toString('utf8')
|
|
2912
|
+
: String(
|
|
2913
|
+
execute(
|
|
2914
|
+
syft.path,
|
|
2915
|
+
['scan', 'registry:' + imageReference, '--output', 'cyclonedx-json'],
|
|
2916
|
+
'Exact platform OCI SBOM',
|
|
2917
|
+
{ env: { SYFT_CHECK_FOR_APP_UPDATE: 'false' } },
|
|
2918
|
+
),
|
|
2919
|
+
);
|
|
1788
2920
|
let sbom;
|
|
1789
2921
|
try {
|
|
1790
2922
|
sbom = parseCycloneDx(
|
|
@@ -1796,14 +2928,20 @@ if (mode === 'platform') {
|
|
|
1796
2928
|
}
|
|
1797
2929
|
const componentSet = componentSetFor(sbom);
|
|
1798
2930
|
if (
|
|
1799
|
-
|
|
2931
|
+
sbom.components.some(
|
|
1800
2932
|
(component) =>
|
|
1801
|
-
typeof component
|
|
2933
|
+
typeof component?.name !== 'string' ||
|
|
2934
|
+
component.name.length === 0 ||
|
|
1802
2935
|
typeof component.type !== 'string' ||
|
|
1803
|
-
|
|
2936
|
+
component.type.length === 0 ||
|
|
2937
|
+
!(
|
|
2938
|
+
(typeof component.version === 'string' &&
|
|
2939
|
+
component.version.length > 0) ||
|
|
2940
|
+
isVersionlessSyftFileComponent(component)
|
|
2941
|
+
),
|
|
1804
2942
|
)
|
|
1805
2943
|
) {
|
|
1806
|
-
throw new Error('Platform SBOM contains invalid
|
|
2944
|
+
throw new Error('Platform SBOM contains an invalid component identity.');
|
|
1807
2945
|
}
|
|
1808
2946
|
const serializedSbom = JSON.stringify(sbom, null, 2) + '\\n';
|
|
1809
2947
|
const relativeSbomPath =
|
|
@@ -1842,6 +2980,7 @@ if (mode === 'platform') {
|
|
|
1842
2980
|
evidence,
|
|
1843
2981
|
);
|
|
1844
2982
|
}
|
|
2983
|
+
await yieldToReleaseSignals();
|
|
1845
2984
|
process.stdout.write(
|
|
1846
2985
|
JSON.stringify({
|
|
1847
2986
|
artifactCount: containerInputs.length,
|
|
@@ -1850,12 +2989,33 @@ if (mode === 'platform') {
|
|
|
1850
2989
|
stage,
|
|
1851
2990
|
}) + '\\n',
|
|
1852
2991
|
);
|
|
2992
|
+
} catch (error) {
|
|
2993
|
+
platformError = error;
|
|
1853
2994
|
} finally {
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
2995
|
+
const cleanupErrors = [];
|
|
2996
|
+
for (const cleanup of [
|
|
2997
|
+
() => cleanupReleaseBuilder(),
|
|
2998
|
+
...(syft
|
|
2999
|
+
? [() => removeExactReleaseToolTemporaryDirectory(syft.temporary)]
|
|
3000
|
+
: []),
|
|
3001
|
+
...(crane
|
|
3002
|
+
? [() => removeExactReleaseToolTemporaryDirectory(crane.temporary)]
|
|
3003
|
+
: []),
|
|
3004
|
+
]) {
|
|
3005
|
+
try {
|
|
3006
|
+
cleanup();
|
|
3007
|
+
} catch (error) {
|
|
3008
|
+
cleanupErrors.push(error);
|
|
3009
|
+
}
|
|
3010
|
+
}
|
|
3011
|
+
if (cleanupErrors.length > 0) {
|
|
3012
|
+
throw new AggregateError(
|
|
3013
|
+
[...(platformError ? [platformError] : []), ...cleanupErrors],
|
|
3014
|
+
'Container platform release and independent owned-resource cleanup failed.',
|
|
3015
|
+
);
|
|
1857
3016
|
}
|
|
1858
3017
|
}
|
|
3018
|
+
if (platformError) throw platformError;
|
|
1859
3019
|
process.exit(0);
|
|
1860
3020
|
}
|
|
1861
3021
|
|
|
@@ -2067,6 +3227,7 @@ const output = resolve(
|
|
|
2067
3227
|
slice,
|
|
2068
3228
|
'binding.json',
|
|
2069
3229
|
);
|
|
3230
|
+
await yieldToReleaseSignals();
|
|
2070
3231
|
immutableWrite(output, binding);
|
|
2071
3232
|
process.stdout.write(
|
|
2072
3233
|
JSON.stringify({
|
|
@@ -2079,7 +3240,7 @@ process.stdout.write(
|
|
|
2079
3240
|
);
|
|
2080
3241
|
} finally {
|
|
2081
3242
|
if (assemblyCrane) {
|
|
2082
|
-
|
|
3243
|
+
removeExactReleaseToolTemporaryDirectory(assemblyCrane.temporary);
|
|
2083
3244
|
}
|
|
2084
3245
|
}
|
|
2085
3246
|
`;
|