@empire-builder-kit/nx 1.0.0-rc.2 → 1.0.0-rc.21
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/executors.json +10 -0
- package/package.json +4 -4
- package/src/cli/output.js +1 -1
- package/src/developer-agent/model.js +4 -4
- package/src/developer-agent/model.js.map +1 -1
- package/src/developer-agent/operation-registry.js +67 -10
- package/src/developer-agent/operation-registry.js.map +1 -1
- package/src/developer-agent/planning.js +7 -9
- package/src/developer-agent/planning.js.map +1 -1
- package/src/developer-agent/workspace-inspection.js +12 -0
- package/src/developer-agent/workspace-inspection.js.map +1 -1
- package/src/executors/blueprint-trust/blueprint-trust.js +6 -0
- package/src/executors/blueprint-trust/blueprint-trust.js.map +1 -1
- package/src/executors/deployment-plan/deployment-plan.js +5 -0
- package/src/executors/deployment-plan/deployment-plan.js.map +1 -1
- package/src/executors/foundation-preflight/foundation-preflight.d.ts +2 -0
- package/src/executors/foundation-preflight/foundation-preflight.js +6 -2
- package/src/executors/foundation-preflight/foundation-preflight.js.map +1 -1
- package/src/executors/foundation-preflight/schema.json +10 -0
- package/src/executors/foundation-profile-preflight/foundation-profile-preflight.d.ts +2 -0
- package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js +2 -1
- package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js.map +1 -1
- package/src/executors/foundation-profile-preflight/schema.json +19 -3
- package/src/executors/github-bootstrap/github-bootstrap.js +1 -0
- package/src/executors/github-bootstrap/github-bootstrap.js.map +1 -1
- package/src/executors/infrastructure-plan/infrastructure-plan.d.ts +229 -5
- package/src/executors/infrastructure-plan/infrastructure-plan.js +2298 -175
- package/src/executors/infrastructure-plan/infrastructure-plan.js.map +1 -1
- package/src/executors/infrastructure-plan/schema.json +16 -0
- package/src/executors/local-deploy/fixtures/local-deploy-state-machine.mjs +53 -0
- package/src/executors/local-deploy/local-approval.d.ts +23 -0
- package/src/executors/local-deploy/local-approval.js +43 -0
- package/src/executors/local-deploy/local-approval.js.map +1 -0
- package/src/executors/local-deploy/local-deploy.d.ts +69 -0
- package/src/executors/local-deploy/local-deploy.js +911 -0
- package/src/executors/local-deploy/local-deploy.js.map +1 -0
- package/src/executors/local-deploy/schema.json +42 -0
- package/src/executors/router-waf-rehearsal/router-waf-rehearsal.d.ts +3 -0
- package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js +94 -0
- package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js.map +1 -0
- package/src/executors/router-waf-rehearsal/schema.json +9 -0
- package/src/executors/secret-metadata/schema.json +5 -0
- package/src/executors/secret-metadata/secret-metadata.d.ts +12 -1
- package/src/executors/secret-metadata/secret-metadata.js +68 -16
- package/src/executors/secret-metadata/secret-metadata.js.map +1 -1
- 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/logging-preflight.js.map +1 -1
- package/src/executors/sst-lifecycle/run-sst-command.d.ts +8 -1
- package/src/executors/sst-lifecycle/run-sst-command.js +144 -12
- package/src/executors/sst-lifecycle/run-sst-command.js.map +1 -1
- package/src/executors/sst-lifecycle/schema.json +13 -1
- package/src/executors/sst-lifecycle/sst-environment-source.d.ts +25 -0
- package/src/executors/sst-lifecycle/sst-environment-source.js +213 -0
- package/src/executors/sst-lifecycle/sst-environment-source.js.map +1 -0
- package/src/foundation/index.d.ts +2 -0
- package/src/foundation/index.js +2 -0
- package/src/foundation/index.js.map +1 -1
- package/src/foundation/logging-preflight.js +1 -1
- package/src/foundation/logging-preflight.js.map +1 -1
- package/src/foundation/logging.d.ts +3 -1
- package/src/foundation/logging.js +1 -1
- package/src/foundation/logging.js.map +1 -1
- package/src/foundation/partial-state.d.ts +10 -0
- package/src/foundation/partial-state.js +21 -0
- package/src/foundation/partial-state.js.map +1 -0
- package/src/foundation/router-waf.d.ts +98 -0
- package/src/foundation/router-waf.js +401 -0
- package/src/foundation/router-waf.js.map +1 -0
- package/src/generators/function/function.js +2 -1
- package/src/generators/function/function.js.map +1 -1
- package/src/generators/post-generation-install.d.ts +1 -0
- package/src/generators/post-generation-install.js +134 -67
- package/src/generators/post-generation-install.js.map +1 -1
- package/src/generators/preset/generator.js +23 -3
- package/src/generators/preset/generator.js.map +1 -1
- package/src/generators/preset/schema.d.ts +7 -0
- package/src/generators/preset/schema.json +47 -0
- package/src/generators/preset/tool-versions.d.ts +5 -5
- package/src/generators/preset/tool-versions.js +4 -4
- 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-acceptance.js.map +1 -0
- package/src/generators/preset/workspace-files-developer-agent.d.ts +2 -0
- package/src/generators/preset/workspace-files-developer-agent.js +25 -11
- package/src/generators/preset/workspace-files-developer-agent.js.map +1 -1
- package/src/generators/preset/workspace-files-foundation-workload-roles.js +5 -2
- package/src/generators/preset/workspace-files-foundation-workload-roles.js.map +1 -1
- package/src/generators/preset/workspace-files-performance-load.js +2 -1
- package/src/generators/preset/workspace-files-performance-load.js.map +1 -1
- package/src/generators/preset/workspace-files-workload-role-baseline.js +254 -42
- package/src/generators/preset/workspace-files-workload-role-baseline.js.map +1 -1
- package/src/generators/preset/workspace-files.d.ts +8 -4
- package/src/generators/preset/workspace-files.js +1039 -68
- package/src/generators/preset/workspace-files.js.map +1 -1
- package/src/generators/repository-policy/repository-policy.js +65 -3
- package/src/generators/repository-policy/repository-policy.js.map +1 -1
- package/src/generators/repository-policy/schema.d.ts +6 -0
- package/src/generators/repository-policy/schema.json +46 -0
- package/src/generators/slice/built-in-plan-api-files.js +2 -1
- package/src/generators/slice/built-in-plan-api-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js +19 -1
- package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-data-files.js +16 -5
- package/src/generators/slice/built-in-plan-data-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-data-retention-job-files.js +10 -6
- package/src/generators/slice/built-in-plan-data-retention-job-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-database-lifecycle-files.js +25 -10
- package/src/generators/slice/built-in-plan-database-lifecycle-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-foundation-files.js +2 -3
- package/src/generators/slice/built-in-plan-foundation-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-infrastructure-files.js +364 -29
- package/src/generators/slice/built-in-plan-infrastructure-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-infrastructure-safety-files.js +67 -11
- package/src/generators/slice/built-in-plan-infrastructure-safety-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-metadata-files.js +1 -0
- package/src/generators/slice/built-in-plan-metadata-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-operations-files.js +442 -5
- package/src/generators/slice/built-in-plan-operations-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-performance-files.d.ts +2 -1
- package/src/generators/slice/built-in-plan-performance-files.js +41 -47
- package/src/generators/slice/built-in-plan-performance-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-project-files.js +14 -1
- package/src/generators/slice/built-in-plan-project-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-release-files.js +235 -56
- package/src/generators/slice/built-in-plan-release-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-testing-files.js +92 -5
- package/src/generators/slice/built-in-plan-testing-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-web-files.js +13 -0
- package/src/generators/slice/built-in-plan-web-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-workload-role-files.js +5 -2
- package/src/generators/slice/built-in-plan-workload-role-files.js.map +1 -1
- package/src/generators/slice/built-in-plan.d.ts +2 -1
- package/src/generators/slice/built-in-plan.js +8 -5
- package/src/generators/slice/built-in-plan.js.map +1 -1
- package/src/generators/slice/slice.js +11 -3
- package/src/generators/slice/slice.js.map +1 -1
- package/src/generators/upgrade/upgrade.js +35 -0
- package/src/generators/upgrade/upgrade.js.map +1 -1
- package/src/generators/workload/frozen-container.d.ts +3 -2
- package/src/generators/workload/frozen-container.js +6 -4
- package/src/generators/workload/frozen-container.js.map +1 -1
- package/src/generators/workload/workload.js +2 -2
- package/src/generators/workload/workload.js.map +1 -1
- package/src/git-policy/github-bootstrap.d.ts +3 -1
- package/src/git-policy/github-bootstrap.js +22 -5
- package/src/git-policy/github-bootstrap.js.map +1 -1
- package/src/git-policy/routing.d.ts +2 -0
- package/src/git-policy/routing.js +12 -1
- package/src/git-policy/routing.js.map +1 -1
- package/src/infrastructure-change/local-capability.d.ts +73 -0
- package/src/infrastructure-change/local-capability.js +114 -0
- package/src/infrastructure-change/local-capability.js.map +1 -0
- package/src/infrastructure-change/model.d.ts +25 -4
- package/src/infrastructure-change/model.js +1 -0
- package/src/infrastructure-change/model.js.map +1 -1
- package/src/infrastructure-change/normalize.d.ts +4 -0
- package/src/infrastructure-change/normalize.js +47 -7
- package/src/infrastructure-change/normalize.js.map +1 -1
- package/src/infrastructure-change/plan.d.ts +30 -3
- package/src/infrastructure-change/plan.js +323 -13
- package/src/infrastructure-change/plan.js.map +1 -1
- package/src/infrastructure-change/risk.d.ts +3 -1
- package/src/infrastructure-change/risk.js +96 -16
- package/src/infrastructure-change/risk.js.map +1 -1
- package/src/local-dev/environment-inspector.d.ts +1 -0
- package/src/local-dev/environment-inspector.js +21 -9
- package/src/local-dev/environment-inspector.js.map +1 -1
- package/src/local-dev/private-state.d.ts +2 -0
- package/src/local-dev/private-state.js +3 -1
- package/src/local-dev/private-state.js.map +1 -1
- package/src/performance/cost-controls.d.ts +5 -3
- package/src/performance/cost-controls.js +30 -24
- package/src/performance/cost-controls.js.map +1 -1
- package/src/performance/model.d.ts +1 -1
- package/src/performance/model.js +0 -2
- package/src/performance/model.js.map +1 -1
- package/src/performance/test-fixtures.js +0 -2
- package/src/performance/test-fixtures.js.map +1 -1
- package/src/release/source-authority.d.ts +1 -0
- package/src/release/source-authority.js +136 -9
- package/src/release/source-authority.js.map +1 -1
- package/src/repository-policy/deployment-plan.d.ts +4 -1
- package/src/repository-policy/deployment-plan.js +8 -0
- package/src/repository-policy/deployment-plan.js.map +1 -1
- package/src/repository-policy/foundation-profile.js +1 -2
- package/src/repository-policy/foundation-profile.js.map +1 -1
- package/src/repository-policy/policy.d.ts +25 -1
- package/src/repository-policy/policy.js +145 -4
- package/src/repository-policy/policy.js.map +1 -1
- package/src/schemas/performance-policy-v1.json +0 -2
- package/src/schemas/repository-policy-v1.json +41 -0
- package/src/secrets/operator.js +22 -4
- package/src/secrets/operator.js.map +1 -1
- package/src/secrets/verification.js +9 -1
- package/src/secrets/verification.js.map +1 -1
- package/src/sst-environment-source-format.d.ts +3 -0
- package/src/sst-environment-source-format.js +179 -0
- package/src/sst-environment-source-format.js.map +1 -0
- package/src/supply-chain/blueprint-trust-git.js +72 -21
- package/src/supply-chain/blueprint-trust-git.js.map +1 -1
- package/src/supply-chain/blueprint-verification-authority.js +55 -3
- package/src/supply-chain/blueprint-verification-authority.js.map +1 -1
- package/src/supply-chain/blueprint-verification-execution.js +17 -2
- package/src/supply-chain/blueprint-verification-execution.js.map +1 -1
- package/src/supply-chain/build-output.d.ts +7 -1
- package/src/supply-chain/build-output.js +160 -20
- package/src/supply-chain/build-output.js.map +1 -1
- package/src/supply-chain/generated-command.js +131 -43
- package/src/supply-chain/generated-command.js.map +1 -1
- package/src/supply-chain/generated-container-release-command.js +187 -38
- package/src/supply-chain/generated-container-release-command.js.map +1 -1
- package/src/supply-chain/generated-ecr-collector-command.js +12 -2
- package/src/supply-chain/generated-ecr-collector-command.js.map +1 -1
- package/src/supply-chain/generated-scanner-command.js +114 -54
- package/src/supply-chain/generated-scanner-command.js.map +1 -1
- package/src/supply-chain/inventory.d.ts +17 -0
- package/src/supply-chain/inventory.js +114 -3
- package/src/supply-chain/inventory.js.map +1 -1
- package/src/supply-chain/pnpm-toolchain-authority.d.ts +4 -3
- package/src/supply-chain/pnpm-toolchain-authority.js +20 -34
- package/src/supply-chain/pnpm-toolchain-authority.js.map +1 -1
- package/src/supply-chain/policy.js +4 -1
- package/src/supply-chain/policy.js.map +1 -1
- package/src/supply-chain/sbom.d.ts +1 -1
- package/src/supply-chain/sbom.js +30 -5
- package/src/supply-chain/sbom.js.map +1 -1
- package/src/supply-chain/scanner.js +10 -1
- package/src/supply-chain/scanner.js.map +1 -1
- package/src/workload-roles/account-baseline.d.ts +11 -0
- package/src/workload-roles/account-baseline.js +253 -16
- package/src/workload-roles/account-baseline.js.map +1 -1
- package/src/workload-roles/authority.d.ts +3 -1
- package/src/workload-roles/authority.js +48 -2
- package/src/workload-roles/authority.js.map +1 -1
- package/src/workload-roles/aws-s3-state.js +27 -0
- package/src/workload-roles/aws-s3-state.js.map +1 -1
- package/src/workload-roles/boundary.d.ts +2 -1
- package/src/workload-roles/boundary.js +9 -2
- package/src/workload-roles/boundary.js.map +1 -1
- package/src/workload-roles/handler.js +52 -24
- package/src/workload-roles/handler.js.map +1 -1
- package/src/workload-roles/injection.js +1 -1
- package/src/workload-roles/injection.js.map +1 -1
- package/src/workload-roles/provisioner.js +12 -4
- package/src/workload-roles/provisioner.js.map +1 -1
- package/src/workload-roles/registration.d.ts +1 -0
- package/src/workload-roles/registration.js +5 -0
- package/src/workload-roles/registration.js.map +1 -1
- package/src/workload-roles/stage-boundary.js +1 -1
- package/src/workload-roles/stage-boundary.js.map +1 -1
- package/src/workload-roles/standard-slice.js +1 -0
- package/src/workload-roles/standard-slice.js.map +1 -1
|
@@ -0,0 +1,911 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.prepareLocalSstBackend = prepareLocalSstBackend;
|
|
4
|
+
exports.localPlanSummaryDigest = localPlanSummaryDigest;
|
|
5
|
+
exports.runLocalDeploy = runLocalDeploy;
|
|
6
|
+
const node_crypto_1 = require("node:crypto");
|
|
7
|
+
const node_child_process_1 = require("node:child_process");
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_os_1 = require("node:os");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
11
|
+
const client_sts_1 = require("@aws-sdk/client-sts");
|
|
12
|
+
const client_ssm_1 = require("@aws-sdk/client-ssm");
|
|
13
|
+
const credential_providers_1 = require("@aws-sdk/credential-providers");
|
|
14
|
+
const devkit_1 = require("@nx/devkit");
|
|
15
|
+
const index_js_1 = require("../../infrastructure-change/index.js");
|
|
16
|
+
const local_capability_js_1 = require("../../infrastructure-change/local-capability.js");
|
|
17
|
+
const index_js_2 = require("../../repository-policy/index.js");
|
|
18
|
+
const source_authority_js_1 = require("../../release/source-authority.js");
|
|
19
|
+
const private_state_js_1 = require("../../local-dev/private-state.js");
|
|
20
|
+
const infrastructure_plan_js_1 = require("../infrastructure-plan/infrastructure-plan.js");
|
|
21
|
+
const sst_environment_source_js_1 = require("../sst-lifecycle/sst-environment-source.js");
|
|
22
|
+
const local_approval_js_1 = require("./local-approval.js");
|
|
23
|
+
const SHA = /^[a-f0-9]{40}$/;
|
|
24
|
+
const SHA256 = /^[a-f0-9]{64}$/;
|
|
25
|
+
const MAX_PLAN_BYTES = 2 * 1024 * 1024;
|
|
26
|
+
const ROLE = /^arn:(aws|aws-us-gov|aws-cn):iam::\d{12}:role\/[A-Za-z0-9+=,.@_/-]{1,512}$/;
|
|
27
|
+
const AMBIENT_CREDENTIAL_KEYS = [
|
|
28
|
+
'AWS_ACCESS_KEY_ID',
|
|
29
|
+
'AWS_SECRET_ACCESS_KEY',
|
|
30
|
+
'AWS_SESSION_TOKEN',
|
|
31
|
+
'AWS_SECURITY_TOKEN',
|
|
32
|
+
'AWS_ROLE_ARN',
|
|
33
|
+
'AWS_ROLE_SESSION_NAME',
|
|
34
|
+
'AWS_WEB_IDENTITY_TOKEN_FILE',
|
|
35
|
+
'AWS_CONTAINER_AUTHORIZATION_TOKEN',
|
|
36
|
+
'AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE',
|
|
37
|
+
'AWS_CONTAINER_CREDENTIALS_FULL_URI',
|
|
38
|
+
'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI',
|
|
39
|
+
];
|
|
40
|
+
const PROTECTED_PLAN_KEYS = [
|
|
41
|
+
'EBK_LOCAL_MUTATION_CAPABILITY_NONCE',
|
|
42
|
+
'EBK_LOCAL_MUTATION_CAPABILITY_PATH',
|
|
43
|
+
'EBK_LOCAL_PLAN_APPROVER_ARN',
|
|
44
|
+
'EBK_LOCAL_PLAN_MATERIAL_DIGEST',
|
|
45
|
+
'EBK_LOCAL_PLAN_SUMMARY_DIGEST',
|
|
46
|
+
'AWS_PLAN_GENERATED_OBJECT_ARNS_JSON',
|
|
47
|
+
'AWS_PLAN_GENERATED_PARAMETER_ARNS_JSON',
|
|
48
|
+
'AWS_PLAN_GENERATED_SECRET_ARNS_JSON',
|
|
49
|
+
'AWS_PLAN_PROVIDER_REFRESH_POLICY_JSON',
|
|
50
|
+
'AWS_PLAN_STATE_KMS_KEY_ARN',
|
|
51
|
+
'EBK_RELEASE_SOURCE_COMMIT',
|
|
52
|
+
];
|
|
53
|
+
function projectEvidencePath(context, project, name) {
|
|
54
|
+
const configured = context.projectsConfigurations?.projects[project]?.root;
|
|
55
|
+
if (!configured) {
|
|
56
|
+
throw new Error(`Local deployment references unknown project ${project}.`);
|
|
57
|
+
}
|
|
58
|
+
return (0, node_path_1.join)((0, node_path_1.isAbsolute)(configured) ? configured : (0, node_path_1.join)(context.root, configured), '.ebk', 'evidence', name);
|
|
59
|
+
}
|
|
60
|
+
function writeLocalMutationCapability(input) {
|
|
61
|
+
const nonce = (0, node_crypto_1.randomBytes)(32).toString('hex');
|
|
62
|
+
const directory = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'ebk-local-mutation-'));
|
|
63
|
+
(0, private_state_js_1.ensurePrivateDirectory)(directory);
|
|
64
|
+
const path = (0, node_path_1.join)(directory, 'capability.json');
|
|
65
|
+
const planContents = (0, node_fs_1.readFileSync)(projectEvidencePath(input.context, input.project, 'infrastructure-plan.json'), 'utf8');
|
|
66
|
+
const authorizationContents = (0, node_fs_1.readFileSync)(projectEvidencePath(input.context, input.project, 'infrastructure-plan-authorization.json'), 'utf8');
|
|
67
|
+
const capability = (0, local_capability_js_1.createLocalMutationCapability)({
|
|
68
|
+
actorArn: input.actorArn,
|
|
69
|
+
authorizationContents,
|
|
70
|
+
deploymentProfile: input.deploymentProfile,
|
|
71
|
+
deploymentSourceCommit: input.deploymentSourceCommit,
|
|
72
|
+
deploymentSourceTree: input.deploymentSourceTree,
|
|
73
|
+
expectedRoleArn: input.expectedRoleArn,
|
|
74
|
+
issuedAt: input.issuedAt,
|
|
75
|
+
nonce,
|
|
76
|
+
plan: input.plan,
|
|
77
|
+
planContents,
|
|
78
|
+
project: input.project,
|
|
79
|
+
releaseSourceTree: input.releaseSourceTree,
|
|
80
|
+
});
|
|
81
|
+
(0, private_state_js_1.atomicWritePrivateFile)(path, `${JSON.stringify(capability, null, 2)}\n`);
|
|
82
|
+
return { directory, nonce, path };
|
|
83
|
+
}
|
|
84
|
+
function digest(value) {
|
|
85
|
+
return (0, node_crypto_1.createHash)('sha256').update(value, 'utf8').digest('hex');
|
|
86
|
+
}
|
|
87
|
+
function ssmParameterAuthority(value, expectedName) {
|
|
88
|
+
const parameter = record(value, expectedName);
|
|
89
|
+
if (parameter['Name'] !== expectedName ||
|
|
90
|
+
parameter['Type'] !== 'SecureString' ||
|
|
91
|
+
typeof parameter['Version'] !== 'number' ||
|
|
92
|
+
!Number.isSafeInteger(parameter['Version']) ||
|
|
93
|
+
parameter['Version'] < 1) {
|
|
94
|
+
throw new Error(`Local SST backend parameter ${expectedName} is missing or malformed.`);
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
name: expectedName,
|
|
98
|
+
type: parameter['Type'],
|
|
99
|
+
version: parameter['Version'],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/** @internal */
|
|
103
|
+
async function prepareLocalSstBackend(input, dependencies = {}) {
|
|
104
|
+
if (input.applications.length === 0 ||
|
|
105
|
+
new Set(input.applications).size !== input.applications.length ||
|
|
106
|
+
input.applications.some((application) => !/^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/.test(application))) {
|
|
107
|
+
throw new Error('Local SST backend requires a unique exact application inventory.');
|
|
108
|
+
}
|
|
109
|
+
const client = new client_ssm_1.SSMClient({
|
|
110
|
+
region: input.region,
|
|
111
|
+
...(input.profile
|
|
112
|
+
? { credentials: (0, credential_providers_1.fromIni)({ profile: input.profile }) }
|
|
113
|
+
: {}),
|
|
114
|
+
});
|
|
115
|
+
const send = dependencies.send ??
|
|
116
|
+
(async (command) => client.send(command));
|
|
117
|
+
const bootstrapResponse = await send(new client_ssm_1.GetParameterCommand({ Name: '/sst/bootstrap' }));
|
|
118
|
+
const bootstrapParameter = bootstrapResponse.Parameter;
|
|
119
|
+
let bootstrap;
|
|
120
|
+
try {
|
|
121
|
+
bootstrap = record(JSON.parse(bootstrapParameter?.Value ?? ''), 'Local SST bootstrap value');
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
throw new Error('Local deployment requires an existing exact SST v5 account bootstrap.');
|
|
125
|
+
}
|
|
126
|
+
const exactBootstrapKeys = [
|
|
127
|
+
'appsyncHttp',
|
|
128
|
+
'appsyncRealtime',
|
|
129
|
+
'asset',
|
|
130
|
+
'assetEcrRegistryId',
|
|
131
|
+
'assetEcrUrl',
|
|
132
|
+
'state',
|
|
133
|
+
'version',
|
|
134
|
+
];
|
|
135
|
+
const state = bootstrap['state'];
|
|
136
|
+
if (bootstrapParameter?.Name !== '/sst/bootstrap' ||
|
|
137
|
+
bootstrapParameter.Type !== 'String' ||
|
|
138
|
+
typeof bootstrapParameter.Version !== 'number' ||
|
|
139
|
+
!Number.isSafeInteger(bootstrapParameter.Version) ||
|
|
140
|
+
bootstrapParameter.Version < 1 ||
|
|
141
|
+
Object.keys(bootstrap).sort().join(',') !== exactBootstrapKeys.join(',') ||
|
|
142
|
+
bootstrap['version'] !== 5 ||
|
|
143
|
+
typeof state !== 'string' ||
|
|
144
|
+
!/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/.test(state) ||
|
|
145
|
+
typeof bootstrap['asset'] !== 'string' ||
|
|
146
|
+
!/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/.test(bootstrap['asset']) ||
|
|
147
|
+
bootstrap['assetEcrRegistryId'] !== input.account ||
|
|
148
|
+
bootstrap['assetEcrUrl'] !==
|
|
149
|
+
`${input.account}.dkr.ecr.${input.region}.amazonaws.com/sst-asset` ||
|
|
150
|
+
typeof bootstrap['appsyncHttp'] !== 'string' ||
|
|
151
|
+
typeof bootstrap['appsyncRealtime'] !== 'string') {
|
|
152
|
+
throw new Error('Local deployment requires an existing exact SST v5 account bootstrap.');
|
|
153
|
+
}
|
|
154
|
+
const parameterAuthority = new Map();
|
|
155
|
+
const missingApplications = [];
|
|
156
|
+
const createdApplications = [];
|
|
157
|
+
for (const application of input.applications) {
|
|
158
|
+
const name = `/sst/passphrase/${application}/${input.stage}`;
|
|
159
|
+
try {
|
|
160
|
+
const observed = await send(new client_ssm_1.GetParameterCommand({ Name: name }));
|
|
161
|
+
parameterAuthority.set(application, ssmParameterAuthority(observed.Parameter, name));
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
if (!(error instanceof Error) || error.name !== 'ParameterNotFound') {
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
missingApplications.push(application);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
for (const application of missingApplications) {
|
|
171
|
+
const name = `/sst/passphrase/${application}/${input.stage}`;
|
|
172
|
+
await send(new client_ssm_1.PutParameterCommand({
|
|
173
|
+
Description: `SST state passphrase for ${application}/${input.stage}`,
|
|
174
|
+
Name: name,
|
|
175
|
+
Overwrite: false,
|
|
176
|
+
Type: 'SecureString',
|
|
177
|
+
Value: dependencies.passphrase?.() ?? (0, node_crypto_1.randomBytes)(32).toString('base64'),
|
|
178
|
+
}));
|
|
179
|
+
createdApplications.push(application);
|
|
180
|
+
const observed = await send(new client_ssm_1.GetParameterCommand({ Name: name }));
|
|
181
|
+
parameterAuthority.set(application, ssmParameterAuthority(observed.Parameter, name));
|
|
182
|
+
}
|
|
183
|
+
const parameters = input.applications.map((application) => {
|
|
184
|
+
const authority = parameterAuthority.get(application);
|
|
185
|
+
if (!authority) {
|
|
186
|
+
throw new Error(`Local SST backend parameter authority is absent for ${application}.`);
|
|
187
|
+
}
|
|
188
|
+
return authority;
|
|
189
|
+
});
|
|
190
|
+
return {
|
|
191
|
+
authorityDigest: digest(JSON.stringify({
|
|
192
|
+
applications: [...input.applications],
|
|
193
|
+
bootstrap: {
|
|
194
|
+
name: bootstrapParameter.Name,
|
|
195
|
+
type: bootstrapParameter.Type,
|
|
196
|
+
version: bootstrapParameter.Version,
|
|
197
|
+
},
|
|
198
|
+
parameters,
|
|
199
|
+
region: input.region,
|
|
200
|
+
stage: input.stage,
|
|
201
|
+
state,
|
|
202
|
+
})),
|
|
203
|
+
createdApplications,
|
|
204
|
+
stateBucketArn: `arn:aws:s3:::${state}`,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function git(root, args, run) {
|
|
208
|
+
const result = run('git', args, {
|
|
209
|
+
cwd: root,
|
|
210
|
+
encoding: 'utf8',
|
|
211
|
+
env: process.env,
|
|
212
|
+
maxBuffer: 1024 * 1024,
|
|
213
|
+
shell: false,
|
|
214
|
+
});
|
|
215
|
+
if (result.status !== 0) {
|
|
216
|
+
throw new Error(`Local deployment Git preflight failed: ${String(result.stderr).trim()}`);
|
|
217
|
+
}
|
|
218
|
+
return String(result.stdout).trim();
|
|
219
|
+
}
|
|
220
|
+
function gitRaw(root, args, run) {
|
|
221
|
+
const result = run('git', args, {
|
|
222
|
+
cwd: root,
|
|
223
|
+
encoding: 'utf8',
|
|
224
|
+
env: process.env,
|
|
225
|
+
maxBuffer: 1024 * 1024,
|
|
226
|
+
shell: false,
|
|
227
|
+
});
|
|
228
|
+
if (result.status !== 0 || String(result.stderr) !== '') {
|
|
229
|
+
throw new Error(`Local deployment Git reconciliation failed: ${String(result.stderr).trim()}`);
|
|
230
|
+
}
|
|
231
|
+
return String(result.stdout);
|
|
232
|
+
}
|
|
233
|
+
function toolOwnedSourceSnapshots(context, projects) {
|
|
234
|
+
const workspaceRoot = (0, node_fs_1.realpathSync)(context.root);
|
|
235
|
+
return projects.map((project) => {
|
|
236
|
+
const configured = context.projectsConfigurations?.projects[project]?.root;
|
|
237
|
+
if (!configured) {
|
|
238
|
+
throw new Error(`Local deployment tool-owned source references unknown project ${project}.`);
|
|
239
|
+
}
|
|
240
|
+
return (0, sst_environment_source_js_1.captureSstEnvironmentSourceTree)({
|
|
241
|
+
projectRoot: (0, node_path_1.isAbsolute)(configured)
|
|
242
|
+
? configured
|
|
243
|
+
: (0, node_path_1.join)(workspaceRoot, configured),
|
|
244
|
+
workspaceRoot,
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
function nulSeparatedPaths(value) {
|
|
249
|
+
return value.split('\0').filter((entry) => entry !== '');
|
|
250
|
+
}
|
|
251
|
+
function reconcileToolOwnedSource(root, snapshots, run) {
|
|
252
|
+
for (const snapshot of snapshots) {
|
|
253
|
+
(0, sst_environment_source_js_1.reconcileSstEnvironmentSourceTree)(snapshot);
|
|
254
|
+
}
|
|
255
|
+
const remaining = [
|
|
256
|
+
...nulSeparatedPaths(gitRaw(root, ['diff', 'HEAD', '--name-only', '--no-renames', '-z'], run)),
|
|
257
|
+
...nulSeparatedPaths(gitRaw(root, ['ls-files', '--others', '--exclude-standard', '-z'], run)),
|
|
258
|
+
];
|
|
259
|
+
if (remaining.length > 0) {
|
|
260
|
+
throw new Error(`Local deployment child changed source outside exact tool-owned files: ${remaining.sort().join(', ')}.`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function projectInventory(context, stage, phase, action) {
|
|
264
|
+
const projects = Object.entries(context.projectsConfigurations?.projects ?? {}).map(([name, configuration]) => ({
|
|
265
|
+
name,
|
|
266
|
+
tags: configuration.tags,
|
|
267
|
+
targets: configuration.targets,
|
|
268
|
+
}));
|
|
269
|
+
const policy = (0, index_js_2.readRepositoryOperatingPolicy)(context.root);
|
|
270
|
+
const foundations = projects
|
|
271
|
+
.filter(({ tags }) => tags?.includes('scope:foundation'))
|
|
272
|
+
.map(({ name }) => name);
|
|
273
|
+
if (foundations.length !== 1) {
|
|
274
|
+
throw new Error('Local deployment requires exactly one Foundation project.');
|
|
275
|
+
}
|
|
276
|
+
if (phase === 'foundation-bootstrap') {
|
|
277
|
+
return foundations;
|
|
278
|
+
}
|
|
279
|
+
if (stage !== 'development') {
|
|
280
|
+
return (0, index_js_2.createProtectedDeploymentPlan)(policy, {
|
|
281
|
+
action,
|
|
282
|
+
projects,
|
|
283
|
+
stage,
|
|
284
|
+
}).orderedProjects;
|
|
285
|
+
}
|
|
286
|
+
const slices = projects
|
|
287
|
+
.filter(({ tags }) => tags?.includes('type:slice'))
|
|
288
|
+
.map(({ name }) => name)
|
|
289
|
+
.sort();
|
|
290
|
+
return [foundations[0], ...slices];
|
|
291
|
+
}
|
|
292
|
+
function runNx(context, project, target, forwarded, environment, run) {
|
|
293
|
+
const result = run(process.execPath, [
|
|
294
|
+
(0, node_path_1.join)(context.root, 'node_modules', 'nx', 'dist', 'bin', 'nx.js'),
|
|
295
|
+
'run',
|
|
296
|
+
`${project}:${target}`,
|
|
297
|
+
'--',
|
|
298
|
+
...forwarded,
|
|
299
|
+
], {
|
|
300
|
+
cwd: context.root,
|
|
301
|
+
encoding: 'utf8',
|
|
302
|
+
env: environment,
|
|
303
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
304
|
+
shell: false,
|
|
305
|
+
stdio: 'inherit',
|
|
306
|
+
});
|
|
307
|
+
if (result.status !== 0) {
|
|
308
|
+
throw new Error(`Local ${target} failed for ${project}.`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function runNxWithToolOwnedSourceReconciliation(context, project, target, forwarded, environment, snapshots, run, acceptExecutionFailure) {
|
|
312
|
+
let executionError;
|
|
313
|
+
try {
|
|
314
|
+
runNx(context, project, target, forwarded, environment, run);
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
executionError = error;
|
|
318
|
+
}
|
|
319
|
+
let reconciliationError;
|
|
320
|
+
try {
|
|
321
|
+
reconcileToolOwnedSource(context.root, snapshots, run);
|
|
322
|
+
}
|
|
323
|
+
catch (error) {
|
|
324
|
+
reconciliationError = error;
|
|
325
|
+
}
|
|
326
|
+
if (executionError !== undefined && reconciliationError !== undefined) {
|
|
327
|
+
throw new Error(`Local ${target} and tool-owned source reconciliation both failed for ${project}. Execution: ${executionError instanceof Error ? executionError.message : String(executionError)} Reconciliation: ${reconciliationError instanceof Error ? reconciliationError.message : String(reconciliationError)}`);
|
|
328
|
+
}
|
|
329
|
+
if (reconciliationError !== undefined)
|
|
330
|
+
throw reconciliationError;
|
|
331
|
+
if (executionError !== undefined) {
|
|
332
|
+
let accepted = false;
|
|
333
|
+
try {
|
|
334
|
+
accepted = acceptExecutionFailure?.() === true;
|
|
335
|
+
}
|
|
336
|
+
catch {
|
|
337
|
+
accepted = false;
|
|
338
|
+
}
|
|
339
|
+
if (!accepted)
|
|
340
|
+
throw executionError;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function record(value, label) {
|
|
344
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
345
|
+
throw new Error(`${label} must be a JSON object.`);
|
|
346
|
+
}
|
|
347
|
+
return value;
|
|
348
|
+
}
|
|
349
|
+
function retainedPlan(context, project, deploymentSourceCommit, account, region, stage, action) {
|
|
350
|
+
const configured = context.projectsConfigurations?.projects[project]?.root;
|
|
351
|
+
if (!configured) {
|
|
352
|
+
throw new Error(`Local deployment references unknown project ${project}.`);
|
|
353
|
+
}
|
|
354
|
+
const workspaceRoot = (0, node_fs_1.realpathSync)(context.root);
|
|
355
|
+
const projectRoot = (0, node_fs_1.realpathSync)((0, node_path_1.isAbsolute)(configured) ? configured : (0, node_path_1.join)(workspaceRoot, configured));
|
|
356
|
+
const projectRelative = (0, node_path_1.relative)(workspaceRoot, projectRoot);
|
|
357
|
+
if (projectRelative === '..' ||
|
|
358
|
+
projectRelative.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`) ||
|
|
359
|
+
(0, node_path_1.isAbsolute)(projectRelative)) {
|
|
360
|
+
throw new Error(`Local deployment project ${project} escapes the workspace.`);
|
|
361
|
+
}
|
|
362
|
+
const path = (0, node_path_1.join)(projectRoot, '.ebk', 'evidence', 'infrastructure-plan.json');
|
|
363
|
+
const before = (0, node_fs_1.lstatSync)(path, { bigint: true });
|
|
364
|
+
if (!before.isFile() ||
|
|
365
|
+
before.nlink !== 1n ||
|
|
366
|
+
before.size > BigInt(MAX_PLAN_BYTES)) {
|
|
367
|
+
throw new Error(`Local deployment plan for ${project} is not one bounded regular file.`);
|
|
368
|
+
}
|
|
369
|
+
const bytes = (0, node_fs_1.readFileSync)(path);
|
|
370
|
+
const after = (0, node_fs_1.lstatSync)(path, { bigint: true });
|
|
371
|
+
if (before.dev !== after.dev ||
|
|
372
|
+
before.ino !== after.ino ||
|
|
373
|
+
before.mode !== after.mode ||
|
|
374
|
+
before.nlink !== after.nlink ||
|
|
375
|
+
before.size !== after.size ||
|
|
376
|
+
before.mtimeNs !== after.mtimeNs ||
|
|
377
|
+
before.ctimeNs !== after.ctimeNs ||
|
|
378
|
+
BigInt(bytes.byteLength) !== after.size) {
|
|
379
|
+
throw new Error(`Local deployment plan for ${project} changed while it was read.`);
|
|
380
|
+
}
|
|
381
|
+
let parsed;
|
|
382
|
+
try {
|
|
383
|
+
parsed = record(JSON.parse(bytes.toString('utf8')), `${project} plan`);
|
|
384
|
+
}
|
|
385
|
+
catch {
|
|
386
|
+
throw new Error(`Local deployment plan for ${project} is invalid JSON.`);
|
|
387
|
+
}
|
|
388
|
+
const plan = parsed;
|
|
389
|
+
if (plan.kind !== index_js_1.INFRASTRUCTURE_PLAN_KIND ||
|
|
390
|
+
plan.schemaVersion !== index_js_1.INFRASTRUCTURE_CHANGE_SCHEMA_VERSION ||
|
|
391
|
+
plan.action !== action ||
|
|
392
|
+
!SHA.test(plan.sourceCommit) ||
|
|
393
|
+
plan.target?.accountFingerprint !== digest(account) ||
|
|
394
|
+
plan.target?.region !== region ||
|
|
395
|
+
plan.target?.stage !== stage ||
|
|
396
|
+
!Array.isArray(plan.projects) ||
|
|
397
|
+
plan.projects.length !== 1 ||
|
|
398
|
+
plan.projects[0] !== project ||
|
|
399
|
+
!SHA256.test(plan.materialDigest) ||
|
|
400
|
+
(0, index_js_1.computeMaterialPlanDigest)(plan) !== plan.materialDigest) {
|
|
401
|
+
throw new Error(`Local deployment plan for ${project} is not the exact current deployment authority.`);
|
|
402
|
+
}
|
|
403
|
+
if (plan.sourceCommit !== deploymentSourceCommit) {
|
|
404
|
+
try {
|
|
405
|
+
(0, source_authority_js_1.validateReviewedReleaseCheckout)({
|
|
406
|
+
deploymentSourceCommit,
|
|
407
|
+
releaseSourceCommit: plan.sourceCommit,
|
|
408
|
+
repositoryRoot: context.root,
|
|
409
|
+
reviewedSourceCommit: deploymentSourceCommit,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
catch {
|
|
413
|
+
throw new Error(`Local deployment plan for ${project} is not bound through one exact approval-only source transition.`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return plan;
|
|
417
|
+
}
|
|
418
|
+
function hasExactPendingLocalAuthorizationRefusal(context, project, plan) {
|
|
419
|
+
const path = projectEvidencePath(context, project, 'infrastructure-plan-authorization.json');
|
|
420
|
+
try {
|
|
421
|
+
const before = (0, node_fs_1.lstatSync)(path, { bigint: true });
|
|
422
|
+
if (!before.isFile() ||
|
|
423
|
+
before.nlink !== 1n ||
|
|
424
|
+
before.size > BigInt(MAX_PLAN_BYTES)) {
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
const bytes = (0, node_fs_1.readFileSync)(path);
|
|
428
|
+
const after = (0, node_fs_1.lstatSync)(path, { bigint: true });
|
|
429
|
+
if (before.dev !== after.dev ||
|
|
430
|
+
before.ino !== after.ino ||
|
|
431
|
+
before.mode !== after.mode ||
|
|
432
|
+
before.nlink !== after.nlink ||
|
|
433
|
+
before.size !== after.size ||
|
|
434
|
+
before.mtimeNs !== after.mtimeNs ||
|
|
435
|
+
before.ctimeNs !== after.ctimeNs ||
|
|
436
|
+
BigInt(bytes.byteLength) !== after.size) {
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
const authorization = record(JSON.parse(bytes.toString('utf8')), `${project} authorization`);
|
|
440
|
+
return (authorization.kind === 'ebk-infrastructure-authorization' &&
|
|
441
|
+
authorization.schemaVersion === index_js_1.INFRASTRUCTURE_CHANGE_SCHEMA_VERSION &&
|
|
442
|
+
authorization.action === 'deploy' &&
|
|
443
|
+
authorization.approval === undefined &&
|
|
444
|
+
authorization.localGovernance === undefined &&
|
|
445
|
+
authorization.governanceEvidenceDigests === undefined &&
|
|
446
|
+
authorization.plannedMaterialDigest === plan.materialDigest &&
|
|
447
|
+
authorization.recomputedMaterialDigest === plan.materialDigest &&
|
|
448
|
+
Array.isArray(authorization.sourceChangesSincePlan) &&
|
|
449
|
+
authorization.sourceChangesSincePlan.length === 0 &&
|
|
450
|
+
authorization.validation.allowed === false &&
|
|
451
|
+
authorization.validation.outcome === 'plan-risk-blocked' &&
|
|
452
|
+
authorization.validation.findings.length === 1 &&
|
|
453
|
+
authorization.validation.findings[0]?.code === 'approval-missing');
|
|
454
|
+
}
|
|
455
|
+
catch {
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
/** @internal */
|
|
460
|
+
function localPlanSummaryDigest(input) {
|
|
461
|
+
const action = input.action ?? 'deploy';
|
|
462
|
+
if ((action !== 'deploy' && action !== 'refresh') ||
|
|
463
|
+
!/^\d{12}$/.test(input.account) ||
|
|
464
|
+
!SHA.test(input.sourceCommit) ||
|
|
465
|
+
!/^[a-z]{2}(?:-gov)?-[a-z]+-\d$/.test(input.region) ||
|
|
466
|
+
input.orderedProjects.length === 0 ||
|
|
467
|
+
new Set(input.orderedProjects).size !== input.orderedProjects.length ||
|
|
468
|
+
Object.keys(input.plans).length !== input.orderedProjects.length) {
|
|
469
|
+
throw new Error('Local deployment plan summary input is invalid.');
|
|
470
|
+
}
|
|
471
|
+
const plans = input.orderedProjects.map((project) => {
|
|
472
|
+
const plan = input.plans[project];
|
|
473
|
+
if (!plan ||
|
|
474
|
+
plan.action !== action ||
|
|
475
|
+
plan.projects.length !== 1 ||
|
|
476
|
+
plan.projects[0] !== project ||
|
|
477
|
+
!SHA256.test(plan.materialDigest)) {
|
|
478
|
+
throw new Error(`Local deployment plan summary omitted exact project ${project}.`);
|
|
479
|
+
}
|
|
480
|
+
return { materialDigest: plan.materialDigest, project };
|
|
481
|
+
});
|
|
482
|
+
return digest(JSON.stringify({
|
|
483
|
+
accountFingerprint: digest(input.account),
|
|
484
|
+
action,
|
|
485
|
+
orderedProjects: [...input.orderedProjects],
|
|
486
|
+
phase: input.phase,
|
|
487
|
+
plans,
|
|
488
|
+
region: input.region,
|
|
489
|
+
sourceCommit: input.sourceCommit,
|
|
490
|
+
stage: input.stage,
|
|
491
|
+
}));
|
|
492
|
+
}
|
|
493
|
+
function retainEvidence(root, evidence) {
|
|
494
|
+
const directory = (0, node_path_1.join)(root, '.ebk', 'evidence', 'local-deployment');
|
|
495
|
+
(0, node_fs_1.mkdirSync)(directory, { recursive: true, mode: 0o700 });
|
|
496
|
+
(0, node_fs_1.chmodSync)(directory, 0o700);
|
|
497
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(directory, 'latest.json'), `${JSON.stringify(evidence, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
498
|
+
}
|
|
499
|
+
const REFRESH_FRAMEWORK_TAG_OBSERVATION_PATHS = new Map([
|
|
500
|
+
['/inputs/tagsAll/ebk:framework-version', true],
|
|
501
|
+
['/outputs/tags/ebk:framework-version', false],
|
|
502
|
+
['/outputs/tagsAll/ebk:framework-version', false],
|
|
503
|
+
]);
|
|
504
|
+
function isFrameworkTagProviderObservation(resource) {
|
|
505
|
+
if (resource.operation !== 'update' ||
|
|
506
|
+
resource.replacement ||
|
|
507
|
+
resource.resourceType !== 'aws:ssm/parameter:Parameter' ||
|
|
508
|
+
resource.changedProperties.length !==
|
|
509
|
+
REFRESH_FRAMEWORK_TAG_OBSERVATION_PATHS.size) {
|
|
510
|
+
return false;
|
|
511
|
+
}
|
|
512
|
+
const observed = new Set();
|
|
513
|
+
for (const property of resource.changedProperties) {
|
|
514
|
+
const expectedInputDiff = REFRESH_FRAMEWORK_TAG_OBSERVATION_PATHS.get(property.path);
|
|
515
|
+
if (expectedInputDiff === undefined ||
|
|
516
|
+
property.inputDiff !== expectedInputDiff ||
|
|
517
|
+
property.kind !== 'update' ||
|
|
518
|
+
property.sensitive ||
|
|
519
|
+
observed.has(property.path)) {
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
observed.add(property.path);
|
|
523
|
+
}
|
|
524
|
+
return observed.size === REFRESH_FRAMEWORK_TAG_OBSERVATION_PATHS.size;
|
|
525
|
+
}
|
|
526
|
+
function isSemanticallyConvergedRefresh(plan) {
|
|
527
|
+
if (plan.action !== 'refresh' ||
|
|
528
|
+
plan.state.locked ||
|
|
529
|
+
!plan.diff.complete ||
|
|
530
|
+
plan.diff.failures.length > 0) {
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
if (plan.diff.resources.length === 0) {
|
|
534
|
+
return plan.driftStatus === 'no-drift';
|
|
535
|
+
}
|
|
536
|
+
if (plan.driftStatus !== 'drift-detected' ||
|
|
537
|
+
!plan.diff.resources.every(isFrameworkTagProviderObservation) ||
|
|
538
|
+
plan.requiredApprovals.some((approval) => ![
|
|
539
|
+
'deployment-review',
|
|
540
|
+
'drift-decision-and-state-recovery',
|
|
541
|
+
'point-of-no-return',
|
|
542
|
+
].includes(approval))) {
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
const observedResourceIds = new Set(plan.diff.resources.map(({ logicalId }) => logicalId));
|
|
546
|
+
return plan.resourceFindings.every((finding) => (finding.resourceId === undefined &&
|
|
547
|
+
finding.class === 'stateful-or-destructive' &&
|
|
548
|
+
finding.code === 'protected-state-refresh' &&
|
|
549
|
+
finding.disposition === 'block') ||
|
|
550
|
+
(finding.resourceId !== undefined &&
|
|
551
|
+
observedResourceIds.has(finding.resourceId) &&
|
|
552
|
+
finding.class === 'in-place-reviewed' &&
|
|
553
|
+
finding.code === 'in-place-update' &&
|
|
554
|
+
finding.disposition === 'review'));
|
|
555
|
+
}
|
|
556
|
+
function isSemanticallyConvergedDeploy(plan) {
|
|
557
|
+
if (plan.action !== 'deploy' ||
|
|
558
|
+
plan.state.locked !== false ||
|
|
559
|
+
!plan.diff.complete ||
|
|
560
|
+
plan.diff.failures.length > 0) {
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
if (plan.diff.resources.length === 0) {
|
|
564
|
+
return (plan.aggregateRisk === 'no-change' &&
|
|
565
|
+
plan.driftStatus === 'no-drift' &&
|
|
566
|
+
plan.requiredApprovals.length === 0 &&
|
|
567
|
+
plan.resourceFindings.length === 0);
|
|
568
|
+
}
|
|
569
|
+
if (plan.aggregateRisk !== 'in-place-reviewed' ||
|
|
570
|
+
(plan.driftStatus !== 'no-drift' &&
|
|
571
|
+
plan.driftStatus !== 'drift-detected') ||
|
|
572
|
+
plan.requiredApprovals.length !== 1 ||
|
|
573
|
+
plan.requiredApprovals[0] !== 'deployment-review') {
|
|
574
|
+
return false;
|
|
575
|
+
}
|
|
576
|
+
const providerReads = new Map();
|
|
577
|
+
for (const resource of plan.diff.resources) {
|
|
578
|
+
const retainedResourceType = providerReads.get(resource.logicalId);
|
|
579
|
+
if (resource.operation !== 'read' ||
|
|
580
|
+
resource.providerOperation !== 'read' ||
|
|
581
|
+
resource.replacement ||
|
|
582
|
+
resource.changedProperties.length > 0 ||
|
|
583
|
+
(retainedResourceType !== undefined &&
|
|
584
|
+
retainedResourceType !== resource.resourceType)) {
|
|
585
|
+
return false;
|
|
586
|
+
}
|
|
587
|
+
providerReads.set(resource.logicalId, resource.resourceType);
|
|
588
|
+
}
|
|
589
|
+
if (plan.resourceFindings.length !== providerReads.size) {
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
const providerReadFindings = new Set();
|
|
593
|
+
for (const finding of plan.resourceFindings) {
|
|
594
|
+
if (finding.resourceId === undefined ||
|
|
595
|
+
!providerReads.has(finding.resourceId) ||
|
|
596
|
+
finding.class !== 'in-place-reviewed' ||
|
|
597
|
+
finding.code !== 'provider-read' ||
|
|
598
|
+
finding.disposition !== 'review' ||
|
|
599
|
+
providerReadFindings.has(finding.resourceId)) {
|
|
600
|
+
return false;
|
|
601
|
+
}
|
|
602
|
+
providerReadFindings.add(finding.resourceId);
|
|
603
|
+
}
|
|
604
|
+
return providerReadFindings.size === providerReads.size;
|
|
605
|
+
}
|
|
606
|
+
function isFreshInvocationPlan(plan, invocationStartedAt, observedAt) {
|
|
607
|
+
const started = Date.parse(invocationStartedAt);
|
|
608
|
+
const generated = Date.parse(plan.generatedAt);
|
|
609
|
+
const observed = observedAt.getTime();
|
|
610
|
+
const expires = Date.parse(plan.expiresAt);
|
|
611
|
+
return (Number.isFinite(started) &&
|
|
612
|
+
Number.isFinite(generated) &&
|
|
613
|
+
Number.isFinite(observed) &&
|
|
614
|
+
Number.isFinite(expires) &&
|
|
615
|
+
generated >= started &&
|
|
616
|
+
generated <= observed &&
|
|
617
|
+
expires > observed);
|
|
618
|
+
}
|
|
619
|
+
async function runLocalDeploy(options, context, dependencies = {}) {
|
|
620
|
+
const run = dependencies.run ?? node_child_process_1.spawnSync;
|
|
621
|
+
if (process.env.GITHUB_ACTIONS === 'true') {
|
|
622
|
+
throw new Error('Local deployment is not a GitHub Actions authority mode.');
|
|
623
|
+
}
|
|
624
|
+
const policy = (0, index_js_2.readRepositoryOperatingPolicy)(context.root);
|
|
625
|
+
const action = options.action ?? 'deploy';
|
|
626
|
+
if (action !== 'deploy' && action !== 'refresh') {
|
|
627
|
+
throw new Error('Local infrastructure action is invalid.');
|
|
628
|
+
}
|
|
629
|
+
const phase = options.phase ?? 'full-stage';
|
|
630
|
+
if (phase !== 'foundation-bootstrap' && phase !== 'full-stage') {
|
|
631
|
+
throw new Error('Local deployment phase is invalid.');
|
|
632
|
+
}
|
|
633
|
+
if ((0, index_js_2.requiresGithubDeployment)(policy.deploymentProfile, options.stage)) {
|
|
634
|
+
throw new Error(`The ${policy.deploymentProfile} deployment profile requires GitHub deployment authority for ${options.stage}.`);
|
|
635
|
+
}
|
|
636
|
+
const accounts = (0, index_js_2.configuredDeploymentAccountTopology)(policy.deploymentProfile, policy.deploymentAccounts);
|
|
637
|
+
const region = (0, index_js_2.configuredDeploymentRegion)(policy.deploymentRegion);
|
|
638
|
+
const expectedAccount = accounts[options.stage];
|
|
639
|
+
if (!ROLE.test(options.expectedRoleArn)) {
|
|
640
|
+
throw new Error('Local deployment requires one exact expected IAM role ARN.');
|
|
641
|
+
}
|
|
642
|
+
const status = git(context.root, ['status', '--porcelain=v1', '--untracked-files=all'], run);
|
|
643
|
+
if (status !== '') {
|
|
644
|
+
throw new Error('Local deployment requires a completely clean source tree.');
|
|
645
|
+
}
|
|
646
|
+
const sourceCommit = git(context.root, ['rev-parse', 'HEAD'], run);
|
|
647
|
+
const sourceTree = git(context.root, ['rev-parse', 'HEAD^{tree}'], run);
|
|
648
|
+
if (!SHA.test(sourceCommit) || !SHA.test(sourceTree)) {
|
|
649
|
+
throw new Error('Local deployment could not bind one immutable Git source.');
|
|
650
|
+
}
|
|
651
|
+
const expectedConfirmation = (0, infrastructure_plan_js_1.localProductionConfirmation)({
|
|
652
|
+
action,
|
|
653
|
+
accountId: expectedAccount,
|
|
654
|
+
region,
|
|
655
|
+
sourceCommit,
|
|
656
|
+
});
|
|
657
|
+
if (options.stage === 'production' &&
|
|
658
|
+
options.confirmation !== expectedConfirmation) {
|
|
659
|
+
throw new Error(`Local production ${action} requires --confirmation=${expectedConfirmation}.`);
|
|
660
|
+
}
|
|
661
|
+
const caller = await (dependencies.caller ??
|
|
662
|
+
(async (targetRegion) => new client_sts_1.STSClient({
|
|
663
|
+
region: targetRegion,
|
|
664
|
+
...(options.profile
|
|
665
|
+
? { credentials: (0, credential_providers_1.fromIni)({ profile: options.profile }) }
|
|
666
|
+
: {}),
|
|
667
|
+
}).send(new client_sts_1.GetCallerIdentityCommand({}))))(region);
|
|
668
|
+
const account = caller.Account?.trim() ?? '';
|
|
669
|
+
const arn = caller.Arn?.trim() ?? '';
|
|
670
|
+
if (account !== expectedAccount ||
|
|
671
|
+
!(0, infrastructure_plan_js_1.matchesExpectedAssumedRoleArn)(expectedAccount, arn, options.expectedRoleArn)) {
|
|
672
|
+
throw new Error('Local AWS caller does not match the committed account and exact temporary assumed role.');
|
|
673
|
+
}
|
|
674
|
+
const orderedProjects = projectInventory(context, options.stage, phase, action);
|
|
675
|
+
const sourceSnapshots = toolOwnedSourceSnapshots(context, orderedProjects);
|
|
676
|
+
const startedAt = (dependencies.now ?? (() => new Date()))().toISOString();
|
|
677
|
+
let backend;
|
|
678
|
+
let result = 'failed';
|
|
679
|
+
let planDigests = {};
|
|
680
|
+
let planSummaryDigest = null;
|
|
681
|
+
let deployNoOpProjects = [];
|
|
682
|
+
const mutatedProjects = [];
|
|
683
|
+
let operatorAcknowledgementDigest = null;
|
|
684
|
+
let replanRequiredProject = null;
|
|
685
|
+
let refreshNoOpProjects = [];
|
|
686
|
+
try {
|
|
687
|
+
backend = await (dependencies.backend ?? prepareLocalSstBackend)({
|
|
688
|
+
account: expectedAccount,
|
|
689
|
+
applications: orderedProjects,
|
|
690
|
+
...(options.profile ? { profile: options.profile } : {}),
|
|
691
|
+
region,
|
|
692
|
+
stage: options.stage,
|
|
693
|
+
});
|
|
694
|
+
const environment = {
|
|
695
|
+
...process.env,
|
|
696
|
+
AWS_EXPECTED_ACCOUNT_ID: expectedAccount,
|
|
697
|
+
AWS_PLAN_ROLE_ARN: options.expectedRoleArn,
|
|
698
|
+
AWS_PLAN_SST_APPS_JSON: JSON.stringify(orderedProjects),
|
|
699
|
+
AWS_PLAN_STATE_BUCKET_ARN: backend.stateBucketArn,
|
|
700
|
+
...(options.profile ? { AWS_PROFILE: options.profile } : {}),
|
|
701
|
+
AWS_REGION: region,
|
|
702
|
+
// Local deployment's reviewed clean Git commit is the one stage-wide
|
|
703
|
+
// workload-role candidate shared by Foundation and every Slice. Never
|
|
704
|
+
// accept an ambient candidate identity from the caller.
|
|
705
|
+
EBK_ACTION: action,
|
|
706
|
+
EBK_CANDIDATE_IDENTITY: sourceCommit,
|
|
707
|
+
EBK_LOCAL_DEPLOYMENT_MODE: 'federated',
|
|
708
|
+
EBK_LOCAL_SOURCE_TREE: sourceTree,
|
|
709
|
+
EBK_SOURCE_COMMIT: sourceCommit,
|
|
710
|
+
EBK_STAGE: options.stage,
|
|
711
|
+
ORDERED_PROJECTS: orderedProjects.join(','),
|
|
712
|
+
};
|
|
713
|
+
for (const key of PROTECTED_PLAN_KEYS)
|
|
714
|
+
delete environment[key];
|
|
715
|
+
if (options.profile) {
|
|
716
|
+
for (const key of AMBIENT_CREDENTIAL_KEYS)
|
|
717
|
+
delete environment[key];
|
|
718
|
+
}
|
|
719
|
+
const common = [
|
|
720
|
+
`--stage=${options.stage}`,
|
|
721
|
+
`--expectedRoleArn=${options.expectedRoleArn}`,
|
|
722
|
+
`--region=${region}`,
|
|
723
|
+
...(options.profile ? [`--profile=${options.profile}`] : []),
|
|
724
|
+
];
|
|
725
|
+
for (const project of orderedProjects) {
|
|
726
|
+
runNxWithToolOwnedSourceReconciliation(context, project, 'infrastructure-plan', [`--action=${action}`, ...common], environment, sourceSnapshots, run, action === 'deploy'
|
|
727
|
+
? () => {
|
|
728
|
+
const plan = retainedPlan(context, project, sourceCommit, expectedAccount, region, options.stage, action);
|
|
729
|
+
const accepted = isFreshInvocationPlan(plan, startedAt, (dependencies.now ?? (() => new Date()))()) &&
|
|
730
|
+
isSemanticallyConvergedDeploy(plan) &&
|
|
731
|
+
hasExactPendingLocalAuthorizationRefusal(context, project, plan);
|
|
732
|
+
if (accepted) {
|
|
733
|
+
devkit_1.logger.info(`Local infrastructure-plan for ${project} returned a conservative non-success after emitting one fresh provider-read-only semantic convergence record.`);
|
|
734
|
+
}
|
|
735
|
+
return accepted;
|
|
736
|
+
}
|
|
737
|
+
: undefined);
|
|
738
|
+
}
|
|
739
|
+
const plans = Object.fromEntries(orderedProjects.map((project) => [
|
|
740
|
+
project,
|
|
741
|
+
retainedPlan(context, project, sourceCommit, expectedAccount, region, options.stage, action),
|
|
742
|
+
]));
|
|
743
|
+
planDigests = Object.fromEntries(orderedProjects.map((project) => [
|
|
744
|
+
project,
|
|
745
|
+
plans[project].materialDigest,
|
|
746
|
+
]));
|
|
747
|
+
planSummaryDigest = localPlanSummaryDigest({
|
|
748
|
+
account: expectedAccount,
|
|
749
|
+
action,
|
|
750
|
+
orderedProjects,
|
|
751
|
+
phase,
|
|
752
|
+
plans,
|
|
753
|
+
region,
|
|
754
|
+
sourceCommit,
|
|
755
|
+
stage: options.stage,
|
|
756
|
+
});
|
|
757
|
+
const approvalEnvironment = {
|
|
758
|
+
...environment,
|
|
759
|
+
EBK_LOCAL_PLAN_APPROVER_ARN: arn,
|
|
760
|
+
EBK_LOCAL_PLAN_SUMMARY_DIGEST: planSummaryDigest,
|
|
761
|
+
};
|
|
762
|
+
const planEnvironment = (project) => plans[project].requiredApprovals.length > 0
|
|
763
|
+
? {
|
|
764
|
+
...approvalEnvironment,
|
|
765
|
+
EBK_LOCAL_PLAN_MATERIAL_DIGEST: plans[project].materialDigest,
|
|
766
|
+
}
|
|
767
|
+
: environment;
|
|
768
|
+
deployNoOpProjects =
|
|
769
|
+
action === 'deploy'
|
|
770
|
+
? orderedProjects.filter((project) => isSemanticallyConvergedDeploy(plans[project]))
|
|
771
|
+
: [];
|
|
772
|
+
refreshNoOpProjects =
|
|
773
|
+
action === 'refresh'
|
|
774
|
+
? orderedProjects.filter((project) => isSemanticallyConvergedRefresh(plans[project]))
|
|
775
|
+
: [];
|
|
776
|
+
const mutationNoOpProjectSet = new Set([
|
|
777
|
+
...deployNoOpProjects,
|
|
778
|
+
...refreshNoOpProjects,
|
|
779
|
+
]);
|
|
780
|
+
const materialProjectIndex = orderedProjects.findIndex((project) => !mutationNoOpProjectSet.has(project));
|
|
781
|
+
if (materialProjectIndex >= 0) {
|
|
782
|
+
const project = orderedProjects[materialProjectIndex];
|
|
783
|
+
const plan = plans[project];
|
|
784
|
+
const challenge = `approve:${action}:${options.stage}:${project}:${plan.materialDigest}`;
|
|
785
|
+
devkit_1.logger.info(`[ebk-local-deployment-review]\n${JSON.stringify({
|
|
786
|
+
action,
|
|
787
|
+
challenge,
|
|
788
|
+
firstMaterialProject: project,
|
|
789
|
+
orderedProjects,
|
|
790
|
+
plan: {
|
|
791
|
+
aggregateRisk: plan.aggregateRisk,
|
|
792
|
+
diff: plan.diff,
|
|
793
|
+
materialDigest: plan.materialDigest,
|
|
794
|
+
requiredApprovals: plan.requiredApprovals,
|
|
795
|
+
resourceFindings: plan.resourceFindings,
|
|
796
|
+
stateDigest: plan.state.digest,
|
|
797
|
+
},
|
|
798
|
+
planSummaryDigest,
|
|
799
|
+
sourceCommit,
|
|
800
|
+
stage: options.stage,
|
|
801
|
+
}, null, 2)}`);
|
|
802
|
+
let acknowledged = false;
|
|
803
|
+
try {
|
|
804
|
+
acknowledged = await (dependencies.approve ?? (0, local_approval_js_1.createTtyLocalMutationApprover)())({
|
|
805
|
+
action,
|
|
806
|
+
challenge,
|
|
807
|
+
planDigest: plan.materialDigest,
|
|
808
|
+
project,
|
|
809
|
+
stage: options.stage,
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
catch (error) {
|
|
813
|
+
if (error instanceof local_approval_js_1.LocalMutationApprovalUnavailableError) {
|
|
814
|
+
result = 'review-required';
|
|
815
|
+
}
|
|
816
|
+
throw error;
|
|
817
|
+
}
|
|
818
|
+
if (!acknowledged) {
|
|
819
|
+
result = 'review-required';
|
|
820
|
+
throw new Error(`Local ${action} acknowledgement was declined for ${project}.`);
|
|
821
|
+
}
|
|
822
|
+
operatorAcknowledgementDigest = digest(challenge);
|
|
823
|
+
const issuedAt = (dependencies.now ?? (() => new Date()))();
|
|
824
|
+
(0, infrastructure_plan_js_1.authorizeRetainedLocalInfrastructurePlan)({
|
|
825
|
+
context: { ...context, projectName: project },
|
|
826
|
+
environment: planEnvironment(project),
|
|
827
|
+
now: issuedAt,
|
|
828
|
+
plan,
|
|
829
|
+
});
|
|
830
|
+
if (retainedPlan(context, project, sourceCommit, expectedAccount, region, options.stage, action).materialDigest !== planDigests[project]) {
|
|
831
|
+
throw new Error(`Local deployment plan for ${project} changed before invocation-scoped authority was established.`);
|
|
832
|
+
}
|
|
833
|
+
const capability = writeLocalMutationCapability({
|
|
834
|
+
actorArn: arn,
|
|
835
|
+
context,
|
|
836
|
+
deploymentProfile: policy.deploymentProfile,
|
|
837
|
+
deploymentSourceCommit: sourceCommit,
|
|
838
|
+
deploymentSourceTree: sourceTree,
|
|
839
|
+
expectedRoleArn: options.expectedRoleArn,
|
|
840
|
+
issuedAt,
|
|
841
|
+
plan,
|
|
842
|
+
project,
|
|
843
|
+
releaseSourceTree: git(context.root, ['rev-parse', `${plan.sourceCommit}^{tree}`], run),
|
|
844
|
+
});
|
|
845
|
+
try {
|
|
846
|
+
runNxWithToolOwnedSourceReconciliation(context, project, action, [
|
|
847
|
+
...common,
|
|
848
|
+
...(options.stage === 'production'
|
|
849
|
+
? [`--localProductionConfirmation=${expectedConfirmation}`]
|
|
850
|
+
: []),
|
|
851
|
+
], {
|
|
852
|
+
...environment,
|
|
853
|
+
EBK_LOCAL_MUTATION_CAPABILITY_NONCE: capability.nonce,
|
|
854
|
+
EBK_LOCAL_MUTATION_CAPABILITY_PATH: capability.path,
|
|
855
|
+
}, sourceSnapshots, run);
|
|
856
|
+
}
|
|
857
|
+
finally {
|
|
858
|
+
(0, node_fs_1.rmSync)(capability.directory, { force: true, recursive: true });
|
|
859
|
+
}
|
|
860
|
+
mutatedProjects.push(project);
|
|
861
|
+
result = 'replan-required';
|
|
862
|
+
replanRequiredProject = orderedProjects[materialProjectIndex + 1] ?? null;
|
|
863
|
+
throw new Error(replanRequiredProject
|
|
864
|
+
? `Local deployment applied ${project} and requires a new complete-stage plan before ${replanRequiredProject}. Rerun and acknowledge the new current plan before any further mutation.`
|
|
865
|
+
: `Local deployment applied ${project} and requires a new complete-stage plan before accepting stage convergence. Rerun and require every current project to be a semantic no-op.`);
|
|
866
|
+
}
|
|
867
|
+
result = 'succeeded';
|
|
868
|
+
return { success: true };
|
|
869
|
+
}
|
|
870
|
+
finally {
|
|
871
|
+
retainEvidence(context.root, {
|
|
872
|
+
account: expectedAccount,
|
|
873
|
+
action,
|
|
874
|
+
actorArnDigest: digest(arn),
|
|
875
|
+
backendAuthorityDigest: backend?.authorityDigest ?? null,
|
|
876
|
+
backendCreatedApplications: backend
|
|
877
|
+
? [...backend.createdApplications]
|
|
878
|
+
: [],
|
|
879
|
+
completedAt: (dependencies.now ?? (() => new Date()))().toISOString(),
|
|
880
|
+
deployNoOpProjects,
|
|
881
|
+
kind: 'ebk-local-deployment-evidence',
|
|
882
|
+
mutatedProjects,
|
|
883
|
+
orderedProjects,
|
|
884
|
+
operatorAcknowledgementDigest,
|
|
885
|
+
planDigests,
|
|
886
|
+
planSummaryDigest,
|
|
887
|
+
phase,
|
|
888
|
+
profile: policy.deploymentProfile,
|
|
889
|
+
replanRequiredProject,
|
|
890
|
+
refreshNoOpProjects,
|
|
891
|
+
region,
|
|
892
|
+
result,
|
|
893
|
+
schemaVersion: 1,
|
|
894
|
+
sourceCommit,
|
|
895
|
+
sourceDigest: digest(`${sourceCommit}:${sourceTree}`),
|
|
896
|
+
stage: options.stage,
|
|
897
|
+
startedAt,
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
const executor = async (options, context) => {
|
|
902
|
+
try {
|
|
903
|
+
return await runLocalDeploy(options, context);
|
|
904
|
+
}
|
|
905
|
+
catch (error) {
|
|
906
|
+
devkit_1.logger.error(error instanceof Error ? error.message : String(error));
|
|
907
|
+
return { success: false };
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
exports.default = executor;
|
|
911
|
+
//# sourceMappingURL=local-deploy.js.map
|