@empire-builder-kit/nx 1.0.0-rc.31 → 1.0.0-rc.32
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/package.json +7 -3
- package/src/aws/identity.js +1 -1
- package/src/aws/identity.js.map +1 -1
- package/src/aws-authority/stage-authority.d.ts +2 -0
- package/src/aws-authority/stage-authority.js +152 -20
- package/src/aws-authority/stage-authority.js.map +1 -1
- package/src/cli/main.d.ts +2 -0
- package/src/cli/main.js +11 -0
- package/src/cli/main.js.map +1 -1
- package/src/developer-agent/workspace-inspection.js +11 -2
- package/src/developer-agent/workspace-inspection.js.map +1 -1
- package/src/executors/github-bootstrap/github-bootstrap.d.ts +1 -0
- package/src/executors/github-bootstrap/github-bootstrap.js +1 -0
- package/src/executors/github-bootstrap/github-bootstrap.js.map +1 -1
- package/src/executors/github-bootstrap/schema.json +7 -1
- package/src/executors/infrastructure-plan/infrastructure-plan.d.ts +2 -0
- package/src/executors/infrastructure-plan/infrastructure-plan.js +18 -1
- package/src/executors/infrastructure-plan/infrastructure-plan.js.map +1 -1
- package/src/executors/local-deploy/local-deploy.d.ts +2 -0
- package/src/executors/local-deploy/local-deploy.js +17 -0
- package/src/executors/local-deploy/local-deploy.js.map +1 -1
- package/src/executors/router-waf-rehearsal/router-waf-rehearsal.d.ts +2 -0
- package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js +2 -0
- package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js.map +1 -1
- package/src/executors/secret-initialize/secret-initialize.d.ts +2 -0
- package/src/executors/secret-initialize/secret-initialize.js +14 -0
- package/src/executors/secret-initialize/secret-initialize.js.map +1 -1
- package/src/executors/secret-metadata/secret-metadata.d.ts +2 -0
- package/src/executors/secret-metadata/secret-metadata.js +14 -0
- package/src/executors/secret-metadata/secret-metadata.js.map +1 -1
- package/src/executors/sst-lifecycle/run-sst-command.d.ts +2 -0
- package/src/executors/sst-lifecycle/run-sst-command.js +17 -2
- package/src/executors/sst-lifecycle/run-sst-command.js.map +1 -1
- package/src/foundation/provision-logical-database.js +2 -0
- package/src/foundation/provision-logical-database.js.map +1 -1
- package/src/generators/preset/generator.js +6 -1
- package/src/generators/preset/generator.js.map +1 -1
- package/src/generators/preset/schema.d.ts +3 -0
- package/src/generators/preset/schema.json +5 -5
- package/src/generators/preset/workspace-files-performance-load.js +21 -12
- package/src/generators/preset/workspace-files-performance-load.js.map +1 -1
- package/src/generators/preset/workspace-files-stage-authority.d.ts +2 -0
- package/src/generators/preset/workspace-files-stage-authority.js +264 -60
- package/src/generators/preset/workspace-files-stage-authority.js.map +1 -1
- package/src/generators/preset/workspace-files-thin-deploy.d.ts +6 -1
- package/src/generators/preset/workspace-files-thin-deploy.js +102 -36
- package/src/generators/preset/workspace-files-thin-deploy.js.map +1 -1
- package/src/generators/preset/workspace-files-thin-docs.js +26 -10
- package/src/generators/preset/workspace-files-thin-docs.js.map +1 -1
- package/src/generators/preset/workspace-files.d.ts +1 -0
- package/src/generators/preset/workspace-files.js +111 -493
- package/src/generators/preset/workspace-files.js.map +1 -1
- package/src/generators/repository-policy/repository-policy.js +149 -19
- package/src/generators/repository-policy/repository-policy.js.map +1 -1
- package/src/generators/repository-policy/schema.d.ts +2 -0
- package/src/generators/repository-policy/schema.json +11 -1
- package/src/generators/repository-policy/tree-policy.d.ts +3 -0
- package/src/generators/repository-policy/tree-policy.js +25 -0
- package/src/generators/repository-policy/tree-policy.js.map +1 -0
- package/src/generators/slice/built-in-plan-async-work-files.js +41 -0
- package/src/generators/slice/built-in-plan-async-work-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js +2 -0
- package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js +19 -0
- package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-data-retention-launcher-files.js +21 -3
- package/src/generators/slice/built-in-plan-data-retention-launcher-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js +20 -1
- package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-operations-files.js +22 -2
- package/src/generators/slice/built-in-plan-operations-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-performance-files.js +60 -0
- package/src/generators/slice/built-in-plan-performance-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-secret-runtime-files.js +13 -0
- package/src/generators/slice/built-in-plan-secret-runtime-files.js.map +1 -1
- package/src/generators/slice/built-in-plan-testing-files.js +20 -0
- package/src/generators/slice/built-in-plan-testing-files.js.map +1 -1
- package/src/generators/slice/slice.js +3 -3
- package/src/generators/slice/slice.js.map +1 -1
- package/src/generators/upgrade/upgrade.js +25 -28
- package/src/generators/upgrade/upgrade.js.map +1 -1
- package/src/generators/workload/performance-registration.js +4 -8
- package/src/generators/workload/performance-registration.js.map +1 -1
- package/src/git-policy/github-bootstrap.d.ts +10 -2
- package/src/git-policy/github-bootstrap.js +109 -13
- package/src/git-policy/github-bootstrap.js.map +1 -1
- package/src/index.d.ts +1 -0
- package/src/index.js +1 -0
- package/src/index.js.map +1 -1
- package/src/local-dev/tls.js +5 -1
- package/src/local-dev/tls.js.map +1 -1
- package/src/repository-policy/policy.d.ts +16 -1
- package/src/repository-policy/policy.js +226 -12
- package/src/repository-policy/policy.js.map +1 -1
- package/src/schemas/repository-policy-v1.json +46 -2
- package/src/secrets/adapters.d.ts +2 -1
- package/src/secrets/adapters.js +12 -2
- package/src/secrets/adapters.js.map +1 -1
- package/src/supply-chain/generated-container-release-command.js +41 -18
- package/src/supply-chain/generated-container-release-command.js.map +1 -1
- package/src/workspace-deployment/identity.d.ts +3 -0
- package/src/workspace-deployment/identity.js +22 -0
- package/src/workspace-deployment/identity.js.map +1 -0
- package/src/workspace-deployment/index.d.ts +3 -0
- package/src/workspace-deployment/index.js +7 -0
- package/src/workspace-deployment/index.js.map +1 -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-evidence.js.map +1 -0
- package/src/workspace-deployment/migration.d.ts +27 -0
- package/src/workspace-deployment/migration.js +109 -0
- package/src/workspace-deployment/migration.js.map +1 -0
- package/src/workspace-deployment/ownership.d.ts +81 -0
- package/src/workspace-deployment/ownership.js +381 -0
- package/src/workspace-deployment/ownership.js.map +1 -0
|
@@ -18,18 +18,36 @@ pnpm nx run <workspace>:stage-authority-render -- \\
|
|
|
18
18
|
\`\`\`
|
|
19
19
|
|
|
20
20
|
Locally admitted stages require at least one reviewed
|
|
21
|
-
\`--aws-principal-arn\`. Stages that require protected GitHub deployment
|
|
22
|
-
one exact \`--github-repository\` environment trust and reject local AWS
|
|
23
|
-
principals.
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
\`--aws-principal-arn\`. Stages that require only protected GitHub deployment
|
|
22
|
+
require one exact \`--github-repository\` environment trust and reject local AWS
|
|
23
|
+
principals. Production-isolated staging requires both the local principal and
|
|
24
|
+
the protected GitHub environment trust: local staging remains supported, while
|
|
25
|
+
the protected \`develop\` deployment produces the receipt consumed by production
|
|
26
|
+
promotion. The \`starter\` profile may add that exact GitHub trust to either of
|
|
27
|
+
its locally admitted stages because its GitHub paved road is optional. Fully
|
|
28
|
+
isolated development rejects GitHub trust.
|
|
26
29
|
|
|
27
|
-
GitHub trust is scoped to the repository's exact stage environment
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
\`--github-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
GitHub trust is scoped to the repository's exact stage environment and always
|
|
31
|
+
uses GitHub's immutable owner/repository-ID OIDC subject customization. Every
|
|
32
|
+
\`--github-repository\` must be accompanied by both
|
|
33
|
+
\`--github-owner-id=<numeric-id>\` and
|
|
34
|
+
\`--github-repository-id=<numeric-id>\`; a partial or name-only subject is
|
|
35
|
+
rejected. Obtain the canonical slug and IDs from the GitHub bootstrap report,
|
|
36
|
+
but do not enable immutable subjects yet for an existing repository. First apply the rendered
|
|
37
|
+
\`ownership-template.json\` as the owner-claim stack named in
|
|
38
|
+
\`ownership-manifest.json\`. CloudFormation creates the retained
|
|
39
|
+
\`/ebk/<stage>/workspace-owner\` record atomically. Then apply
|
|
40
|
+
\`template.json\` as the authority stack shown in \`manifest.json\`; its
|
|
41
|
+
CloudFormation Rule reads the exact owner before any IAM change. A second
|
|
42
|
+
workspace therefore cannot address the same account, Region, and stage tuple,
|
|
43
|
+
while a clone retaining the committed deployment ID reuses both stacks.
|
|
44
|
+
Read back and simulate the effective immutable-subject trust, then apply the
|
|
45
|
+
GitHub bootstrap change that configures the repository's default immutable OIDC
|
|
46
|
+
subject. This AWS-trust-first order prevents an existing protected deployment
|
|
47
|
+
from being stranded between the subject-format and trust-policy changes.
|
|
48
|
+
Existing deployed workspaces must first commit the explicit migration evidence
|
|
49
|
+
described in \`docs/workspace-deployment-ownership.md\`; never claim an existing
|
|
50
|
+
tuple by editing policy directly. Read back the deployed template and policies
|
|
33
51
|
and compare their canonical digests before any application mutation. Simulate
|
|
34
52
|
representative permitted lifecycle calls and denied account, trust,
|
|
35
53
|
boundary-removal, cross-stage, cross-account, and cross-Region calls against
|
|
@@ -90,14 +108,28 @@ The role may inspect secret metadata. SST 4.17.1's generated Foundation Aurora
|
|
|
90
108
|
credential is the one value exception: the temporary deployer may put and read
|
|
91
109
|
that exact stage/account/Region-bound secret version so the pinned provider can
|
|
92
110
|
create and reconcile it. The reserved name must also carry the exact
|
|
93
|
-
\`ebk:stage\` tag.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
111
|
+
\`ebk:stage\` tag. \`GetSecretValue\` and \`PutSecretValue\` remain denied for
|
|
112
|
+
operator-secret and all other Slice containers.
|
|
113
|
+
Generated source creates Slice containers inside the deterministic
|
|
114
|
+
\`ebk/<stage>/*\` namespace. The temporary deployer's \`UpdateSecret\` authority
|
|
115
|
+
is denied outside that namespace and the exact generated Foundation
|
|
116
|
+
provider-secret identity. Imported, externally owned or shared, unmanaged,
|
|
117
|
+
other-stage, cross-account, and cross-Region containers remain outside this
|
|
118
|
+
update authority. The namespace is reserved for EBK-owned, stage-exclusive
|
|
119
|
+
containers, including explicitly approved cross-Slice use; imported or
|
|
120
|
+
externally owned containers must use a name outside it. The namespace contract
|
|
121
|
+
covers generated database, machine-client, generated-value, and EBK-owned
|
|
122
|
+
operator containers without adding a new IAM exception for every provider
|
|
123
|
+
resource type. It does not admit
|
|
124
|
+
\`GetSecretValue\` or \`PutSecretValue\` for Slice containers. Because
|
|
125
|
+
\`CreateSecret\` and \`UpdateSecret\` can optionally carry a value, IAM cannot
|
|
126
|
+
distinguish a metadata-only call; reviewed generated source, temporary
|
|
127
|
+
credentials, the stage namespace, and CloudTrail are the honest controls for
|
|
128
|
+
those provider APIs.
|
|
98
129
|
SSM value reads are limited to the stage's Foundation configuration, SST 4.17.1
|
|
99
|
-
bootstrap record, stage-qualified SST passphrases,
|
|
100
|
-
reference
|
|
130
|
+
bootstrap record, stage-qualified SST passphrases, SST's VPC private-key
|
|
131
|
+
reference, and the exact retained workspace-owner record. The deployment role
|
|
132
|
+
can read but cannot change or delete that owner. Foundation removal enumerates Product Slice registration names and
|
|
101
133
|
metadata without reading their values. Recursive and historical SSM value reads
|
|
102
134
|
are denied. These controls,
|
|
103
135
|
the exact trust, temporary credentials, requested Region, known account/stage
|
|
@@ -116,8 +148,67 @@ bootstrap state and assets. Tag and name denies are defense in depth, not a hard
|
|
|
116
148
|
IAM boundary between same-account deployment roles. Choose
|
|
117
149
|
\`production-isolated\` when production requires an AWS-enforced deployment
|
|
118
150
|
blast-radius boundary from development and staging.
|
|
151
|
+
`,
|
|
152
|
+
'docs/workspace-deployment-ownership.md': `# Workspace deployment ownership
|
|
153
|
+
|
|
154
|
+
Every generated workspace commits one stable, random \`workspaceDeploymentId\`
|
|
155
|
+
and \`workspaceDeploymentProvenance: { kind: "fresh", schemaVersion: 1 }\`.
|
|
156
|
+
That identity belongs to the workspace, not to a checkout: clones with the same
|
|
157
|
+
committed policy are the same deployment owner.
|
|
158
|
+
|
|
159
|
+
Before authority or SST lifecycle work, each active account/Region/canonical
|
|
160
|
+
stage tuple must contain exactly one retained String parameter at
|
|
161
|
+
\`/ebk/<stage>/workspace-owner\`. The value binds the tuple to the generated
|
|
162
|
+
deployment ID; the current workspace name remains diagnostic metadata and may
|
|
163
|
+
be renamed without transferring authority. The ownership claim is deliberately
|
|
164
|
+
exclusive and retained. Two workspaces do not cohabit one tuple, and removing
|
|
165
|
+
an application stack does not release the claim.
|
|
166
|
+
|
|
167
|
+
Render stage authority with \`pnpm nx run ${options.name}:stage-authority-render --
|
|
168
|
+
--stage=<stage> ...\`. Apply the emitted \`ownership-template.json\` as the exact
|
|
169
|
+
claim stack in \`ownership-manifest.json\` before applying the authority template.
|
|
170
|
+
CloudFormation's fixed-name SSM create is the atomic claim. The authority
|
|
171
|
+
template accepts only that exact parameter path and fails its Rule before any
|
|
172
|
+
IAM change unless its value is the exact committed owner. The deployment role
|
|
173
|
+
may read the owner but is explicitly denied every owner mutation.
|
|
174
|
+
|
|
175
|
+
For a read-only report, run
|
|
176
|
+
\`node tools/stage-authority-render.mjs --stage=<stage> --ownership-doctor=true
|
|
177
|
+
--profile=<temporary-profile>\`. It reports \`owned\`, \`unclaimed\`, or \`conflict\`;
|
|
178
|
+
a conflict identifies the committed owner without changing AWS.
|
|
179
|
+
|
|
180
|
+
## Existing prerelease deployments
|
|
181
|
+
|
|
182
|
+
An existing policy without the required deployment identity provenance is fail
|
|
183
|
+
closed. Inventory
|
|
184
|
+
each configured tuple read-only and commit bounded evidence under
|
|
185
|
+
\`docs/adoption/\`. The evidence must bind the exact prior policy SHA-256,
|
|
186
|
+
workspace name, proposed \`ebkws-<32 lowercase hex>\` ID, and every configured
|
|
187
|
+
tuple. The ID, exact Region, and every active-stage account must be committed in
|
|
188
|
+
the same migration change; an empty interim topology cannot reserve future
|
|
189
|
+
coordinates. Each tuple declares \`empty\` with no resource digests or
|
|
190
|
+
\`existing-workspace\` with sorted SHA-256 digests of its bounded resource
|
|
191
|
+
identity evidence. Then run the repository-policy generator with both
|
|
192
|
+
\`--workspaceDeploymentId\` and
|
|
193
|
+
\`--workspaceDeploymentMigrationEvidence\`. Report-only mode shows the exact
|
|
194
|
+
result with \`workspaceDeploymentProvenance.kind: "migrated"\`; \`--apply\`
|
|
195
|
+
commits it. Every later policy read revalidates the retained evidence bytes,
|
|
196
|
+
workspace identity, the syntactically valid diagnostic name recorded at migration,
|
|
197
|
+
and exact active stage/account/Region tuple set. The current package name may be
|
|
198
|
+
renamed without renewing ownership evidence because authority is bound to the
|
|
199
|
+
immutable deployment ID rather than that diagnostic label.
|
|
200
|
+
A profile, account, or Region change for a migrated workspace requires a new
|
|
201
|
+
versioned evidence path whose previous-policy digest binds the current policy;
|
|
202
|
+
unrelated policy changes retain the existing binding.
|
|
203
|
+
|
|
204
|
+
This migration is an explicit ownership assertion, not automatic discovery.
|
|
205
|
+
Never use it to adopt a tuple whose resources belong to another workspace. An
|
|
206
|
+
occupied but unclaimed tuple requires reviewed evidence that the existing
|
|
207
|
+
resources belong to this workspace. There is no implicit compatibility claim,
|
|
208
|
+
ownership transfer, or force-adoption path.
|
|
119
209
|
`,
|
|
120
210
|
'tools/stage-authority-render.mjs': `import {
|
|
211
|
+
assertBoundStageAuthorityTemplateFitsBodyLimit,
|
|
121
212
|
canonicalStageAuthorityTemplate,
|
|
122
213
|
createStageAuthorityPlan,
|
|
123
214
|
} from '@empire-builder-kit/nx/aws-authority';
|
|
@@ -125,9 +216,15 @@ import {
|
|
|
125
216
|
configuredDeploymentAccountForStage,
|
|
126
217
|
configuredDeploymentRegion,
|
|
127
218
|
isActiveDeploymentStage,
|
|
219
|
+
readRepositoryOperatingPolicy,
|
|
128
220
|
requiresGithubDeployment,
|
|
129
|
-
validateRepositoryOperatingPolicy,
|
|
130
221
|
} from '@empire-builder-kit/nx/repository-policy';
|
|
222
|
+
import {
|
|
223
|
+
bindStageAuthorityTemplateToWorkspaceOwner,
|
|
224
|
+
canonicalWorkspaceStageOwner,
|
|
225
|
+
createWorkspaceStageOwnershipClaimPlan,
|
|
226
|
+
inspectWorkspaceStageOwnership,
|
|
227
|
+
} from '@empire-builder-kit/nx/workspace-deployment';
|
|
131
228
|
import { createHash } from 'node:crypto';
|
|
132
229
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
133
230
|
import { join } from 'node:path';
|
|
@@ -150,19 +247,22 @@ const stage = values.get('stage');
|
|
|
150
247
|
if (!['development', 'staging', 'production'].includes(stage)) {
|
|
151
248
|
throw new Error('--stage must be development, staging, or production.');
|
|
152
249
|
}
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
250
|
+
const policy = readRepositoryOperatingPolicy(process.cwd());
|
|
251
|
+
const rootPackage = JSON.parse(readFileSync('package.json', 'utf8'));
|
|
252
|
+
const workspaceName = rootPackage?.name;
|
|
253
|
+
if (
|
|
254
|
+
typeof workspaceName !== 'string' ||
|
|
255
|
+
workspaceName.length > 214 ||
|
|
256
|
+
!/^(?:@[a-z0-9][a-z0-9._~-]*\\/)?[a-z0-9][a-z0-9._~-]*$/.test(workspaceName)
|
|
257
|
+
) {
|
|
258
|
+
throw new Error('Workspace ownership requires the current generated root package name.');
|
|
161
259
|
}
|
|
162
|
-
const policy = policyResult.value;
|
|
163
260
|
if (policy.deploymentProfile !== '${options.deploymentProfile}') {
|
|
164
261
|
throw new Error('Generated and committed deployment profiles differ.');
|
|
165
262
|
}
|
|
263
|
+
if (policy.workspaceDeploymentId !== '${options.workspaceDeploymentId}') {
|
|
264
|
+
throw new Error('Generated and committed workspace deployment identities differ.');
|
|
265
|
+
}
|
|
166
266
|
if (!isActiveDeploymentStage(policy.deploymentProfile, stage)) {
|
|
167
267
|
throw new Error('Requested stage is inactive for the committed deployment profile.');
|
|
168
268
|
}
|
|
@@ -172,11 +272,66 @@ const accountId = configuredDeploymentAccountForStage(
|
|
|
172
272
|
stage,
|
|
173
273
|
);
|
|
174
274
|
const region = configuredDeploymentRegion(policy.deploymentRegion);
|
|
275
|
+
if (/^(?:us-iso-|us-isob-|eu-isoe-|us-isof-)/.test(region)) {
|
|
276
|
+
throw new Error(
|
|
277
|
+
'The committed Region belongs to an unsupported AWS partition. Generated authority supports aws, aws-cn, and aws-us-gov only.',
|
|
278
|
+
);
|
|
279
|
+
}
|
|
175
280
|
const partition = region.startsWith('cn-')
|
|
176
281
|
? 'aws-cn'
|
|
177
282
|
: region.startsWith('us-gov-')
|
|
178
283
|
? 'aws-us-gov'
|
|
179
284
|
: 'aws';
|
|
285
|
+
const ownershipTarget = {
|
|
286
|
+
accountId,
|
|
287
|
+
region,
|
|
288
|
+
stage,
|
|
289
|
+
workspaceDeploymentId: policy.workspaceDeploymentId,
|
|
290
|
+
workspaceName,
|
|
291
|
+
};
|
|
292
|
+
const ownershipDoctor = values.get('ownership-doctor');
|
|
293
|
+
if (ownershipDoctor !== undefined && ownershipDoctor !== 'true') {
|
|
294
|
+
throw new Error('--ownership-doctor must be exactly true when supplied.');
|
|
295
|
+
}
|
|
296
|
+
if (ownershipDoctor === 'true') {
|
|
297
|
+
if (principals.length > 0 || values.has('github-repository')) {
|
|
298
|
+
throw new Error('Workspace ownership doctor does not accept authority trust inputs.');
|
|
299
|
+
}
|
|
300
|
+
const profile = values.get('profile');
|
|
301
|
+
const [{ GetCallerIdentityCommand, STSClient }, { fromIni }] = await Promise.all([
|
|
302
|
+
import('@aws-sdk/client-sts'),
|
|
303
|
+
import('@aws-sdk/credential-providers'),
|
|
304
|
+
]);
|
|
305
|
+
const credentials = profile ? fromIni({ profile }) : undefined;
|
|
306
|
+
const identity = await new STSClient({
|
|
307
|
+
...(credentials ? { credentials } : {}),
|
|
308
|
+
region,
|
|
309
|
+
}).send(new GetCallerIdentityCommand({}));
|
|
310
|
+
if (identity.Account !== accountId) {
|
|
311
|
+
throw new Error('Workspace ownership doctor caller differs from the committed stage account.');
|
|
312
|
+
}
|
|
313
|
+
const inspection = await inspectWorkspaceStageOwnership(
|
|
314
|
+
ownershipTarget,
|
|
315
|
+
profile ? { profile } : {},
|
|
316
|
+
);
|
|
317
|
+
process.stdout.write(JSON.stringify({
|
|
318
|
+
accountId,
|
|
319
|
+
expectedOwner: inspection.expectedOwner,
|
|
320
|
+
kind: 'ebk-workspace-stage-owner-doctor',
|
|
321
|
+
parameterArn: inspection.parameterArn,
|
|
322
|
+
region,
|
|
323
|
+
...(inspection.status === 'owned'
|
|
324
|
+
? { owner: inspection.expectedOwner }
|
|
325
|
+
: inspection.status === 'conflict' && inspection.actualOwner
|
|
326
|
+
? { owner: inspection.actualOwner }
|
|
327
|
+
: {}),
|
|
328
|
+
...(inspection.status === 'conflict' ? { reason: inspection.reason } : {}),
|
|
329
|
+
schemaVersion: 1,
|
|
330
|
+
stage,
|
|
331
|
+
status: inspection.status,
|
|
332
|
+
}, null, 2) + '\\n');
|
|
333
|
+
if (inspection.status !== 'owned') process.exitCode = 1;
|
|
334
|
+
} else {
|
|
180
335
|
const repository = values.get('github-repository');
|
|
181
336
|
if (repository && !/^[A-Za-z0-9_.-]+[/][A-Za-z0-9_.-]+$/.test(repository)) {
|
|
182
337
|
throw new Error('--github-repository must be one exact owner/repository slug.');
|
|
@@ -184,8 +339,10 @@ if (repository && !/^[A-Za-z0-9_.-]+[/][A-Za-z0-9_.-]+$/.test(repository)) {
|
|
|
184
339
|
const githubOwnerId = values.get('github-owner-id');
|
|
185
340
|
const githubRepositoryId = values.get('github-repository-id');
|
|
186
341
|
const githubId = /^[1-9][0-9]{0,19}$/;
|
|
187
|
-
if (
|
|
188
|
-
throw new Error(
|
|
342
|
+
if (repository && (!githubOwnerId || !githubRepositoryId)) {
|
|
343
|
+
throw new Error(
|
|
344
|
+
'--github-repository requires both --github-owner-id and --github-repository-id.',
|
|
345
|
+
);
|
|
189
346
|
}
|
|
190
347
|
if ((githubOwnerId || githubRepositoryId) && !repository) {
|
|
191
348
|
throw new Error('Immutable GitHub IDs require --github-repository.');
|
|
@@ -197,36 +354,35 @@ if (
|
|
|
197
354
|
throw new Error('Immutable GitHub owner and repository IDs must be positive bounded integers.');
|
|
198
355
|
}
|
|
199
356
|
const githubOnly = requiresGithubDeployment(policy.deploymentProfile, stage);
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
357
|
+
const productionIsolatedStaging =
|
|
358
|
+
policy.deploymentProfile === 'production-isolated' && stage === 'staging';
|
|
359
|
+
const localRequired = !githubOnly;
|
|
360
|
+
const githubRequired = githubOnly || productionIsolatedStaging;
|
|
361
|
+
const githubAllowed = githubRequired || policy.deploymentProfile === 'starter';
|
|
362
|
+
if (localRequired && principals.length === 0) {
|
|
363
|
+
throw new Error(
|
|
364
|
+
'Locally admitted stages require at least one reviewed --aws-principal-arn.',
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
if (!localRequired && principals.length > 0) {
|
|
368
|
+
throw new Error(
|
|
369
|
+
'Protected GitHub deployment stages reject local --aws-principal-arn trust.',
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
if (githubRequired && !repository) {
|
|
373
|
+
throw new Error(
|
|
374
|
+
'This stage requires protected GitHub deployment and one exact --github-repository trust.',
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
if (repository && !githubAllowed) {
|
|
378
|
+
throw new Error(
|
|
379
|
+
'This locally admitted isolated-profile stage does not permit GitHub trust.',
|
|
380
|
+
);
|
|
223
381
|
}
|
|
224
382
|
const [githubOwner, githubRepository] = repository?.split('/') ?? [];
|
|
225
383
|
const githubSubject = repository
|
|
226
384
|
? 'repo:' +
|
|
227
|
-
|
|
228
|
-
? githubOwner + '@' + githubOwnerId + '/' + githubRepository + '@' + githubRepositoryId
|
|
229
|
-
: repository) +
|
|
385
|
+
githubOwner + '@' + githubOwnerId + '/' + githubRepository + '@' + githubRepositoryId +
|
|
230
386
|
':environment:' + stage
|
|
231
387
|
: undefined;
|
|
232
388
|
const plan = createStageAuthorityPlan({
|
|
@@ -245,16 +401,52 @@ const plan = createStageAuthorityPlan({
|
|
|
245
401
|
partition,
|
|
246
402
|
stage,
|
|
247
403
|
});
|
|
404
|
+
const ownership = createWorkspaceStageOwnershipClaimPlan(ownershipTarget);
|
|
405
|
+
const authorityTemplate = bindStageAuthorityTemplateToWorkspaceOwner(
|
|
406
|
+
plan.template,
|
|
407
|
+
ownershipTarget,
|
|
408
|
+
);
|
|
409
|
+
assertBoundStageAuthorityTemplateFitsBodyLimit(authorityTemplate);
|
|
410
|
+
const template = canonicalStageAuthorityTemplate(authorityTemplate);
|
|
248
411
|
const directory = join('.ebk', 'authority', stage);
|
|
249
412
|
mkdirSync(directory, { mode: 0o700, recursive: true });
|
|
250
|
-
const template = canonicalStageAuthorityTemplate(plan.template);
|
|
251
413
|
const templatePath = join(directory, 'template.json');
|
|
252
414
|
const manifestPath = join(directory, 'manifest.json');
|
|
415
|
+
const ownershipTemplate = canonicalStageAuthorityTemplate(ownership.template);
|
|
416
|
+
const ownershipTemplatePath = join(directory, 'ownership-template.json');
|
|
417
|
+
const ownershipManifestPath = join(directory, 'ownership-manifest.json');
|
|
253
418
|
writeFileSync(templatePath, template, { encoding: 'utf8', mode: 0o600 });
|
|
419
|
+
writeFileSync(ownershipTemplatePath, ownershipTemplate, {
|
|
420
|
+
encoding: 'utf8',
|
|
421
|
+
mode: 0o600,
|
|
422
|
+
});
|
|
423
|
+
const ownershipTemplateSha256 = createHash('sha256')
|
|
424
|
+
.update(ownershipTemplate)
|
|
425
|
+
.digest('hex');
|
|
426
|
+
const ownershipManifest = {
|
|
427
|
+
accountId,
|
|
428
|
+
digest: ownershipTemplateSha256,
|
|
429
|
+
kind: 'ebk-workspace-stage-owner-claim',
|
|
430
|
+
parameterArn: ownership.parameterArn,
|
|
431
|
+
parameterName: ownership.parameterName,
|
|
432
|
+
region,
|
|
433
|
+
schemaVersion: 1,
|
|
434
|
+
stackName: ownership.stackName,
|
|
435
|
+
stage,
|
|
436
|
+
templateSha256: ownershipTemplateSha256,
|
|
437
|
+
workspaceDeploymentId: policy.workspaceDeploymentId,
|
|
438
|
+
workspaceName,
|
|
439
|
+
};
|
|
440
|
+
writeFileSync(
|
|
441
|
+
ownershipManifestPath,
|
|
442
|
+
JSON.stringify(ownershipManifest, null, 2) + '\\n',
|
|
443
|
+
{ encoding: 'utf8', mode: 0o600 },
|
|
444
|
+
);
|
|
445
|
+
const templateSha256 = createHash('sha256').update(template).digest('hex');
|
|
254
446
|
const manifest = {
|
|
255
447
|
accountId,
|
|
256
448
|
deploymentRoleArn: plan.identity.deploymentRoleArn,
|
|
257
|
-
digest:
|
|
449
|
+
digest: templateSha256,
|
|
258
450
|
kind: 'ebk-stage-authority-render',
|
|
259
451
|
limitations: plan.limitations,
|
|
260
452
|
profile: policy.deploymentProfile,
|
|
@@ -262,13 +454,25 @@ const manifest = {
|
|
|
262
454
|
schemaVersion: 1,
|
|
263
455
|
stackName: plan.identity.stackName,
|
|
264
456
|
stage,
|
|
265
|
-
templateSha256
|
|
457
|
+
templateSha256,
|
|
458
|
+
workspaceDeploymentId: policy.workspaceDeploymentId,
|
|
459
|
+
workspaceName,
|
|
460
|
+
workspaceOwner: canonicalWorkspaceStageOwner(ownershipTarget),
|
|
461
|
+
workspaceOwnerClaimStackName: ownership.stackName,
|
|
462
|
+
workspaceOwnerManifestPath: ownershipManifestPath,
|
|
266
463
|
};
|
|
267
464
|
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\\n', {
|
|
268
465
|
encoding: 'utf8',
|
|
269
466
|
mode: 0o600,
|
|
270
467
|
});
|
|
271
|
-
process.stdout.write(JSON.stringify({
|
|
468
|
+
process.stdout.write(JSON.stringify({
|
|
469
|
+
manifestPath,
|
|
470
|
+
...manifest,
|
|
471
|
+
ownershipManifestPath,
|
|
472
|
+
ownershipTemplatePath,
|
|
473
|
+
templatePath,
|
|
474
|
+
}) + '\\n');
|
|
475
|
+
}
|
|
272
476
|
`,
|
|
273
477
|
};
|
|
274
478
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-files-stage-authority.js","sourceRoot":"","sources":["../../../../../../packages/nx/src/generators/preset/workspace-files-stage-authority.ts"],"names":[],"mappings":";;AAEA,
|
|
1
|
+
{"version":3,"file":"workspace-files-stage-authority.js","sourceRoot":"","sources":["../../../../../../packages/nx/src/generators/preset/workspace-files-stage-authority.ts"],"names":[],"mappings":";;AAEA,gFA8dC;AA9dD,SAAgB,kCAAkC,CAAC,OAIlD;IACC,OAAO;QACL,oCAAoC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiJzC;QACG,wCAAwC,EAAE;;;;;;;;;;;;;;;4CAeF,OAAO,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0CvD;QACG,kCAAkC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCAkDJ,OAAO,CAAC,iBAAiB;;;wCAGrB,OAAO,CAAC,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqNpE;KACE,CAAC;AACJ,CAAC"}
|
|
@@ -10,7 +10,6 @@ export interface ThinDeployActionPins {
|
|
|
10
10
|
}
|
|
11
11
|
export interface ThinDeployToolVersions {
|
|
12
12
|
node: string;
|
|
13
|
-
pnpm: string;
|
|
14
13
|
sst: string;
|
|
15
14
|
}
|
|
16
15
|
export interface ThinDeployWorkflowOptions {
|
|
@@ -24,6 +23,12 @@ export interface ThinDeployWorkflowFilePlan {
|
|
|
24
23
|
managedPaths: readonly [typeof THIN_DEPLOY_WORKFLOW_PATH];
|
|
25
24
|
}
|
|
26
25
|
export declare function renderThinDeployVerifyCaller(profile: DeploymentProfile): string;
|
|
26
|
+
/** @internal Exported for executable source-contract tests. */
|
|
27
|
+
export declare function exactStageDeployerGuardScript(operation: string, accountVariable: string): string;
|
|
28
|
+
/** @internal Shared verbatim by every generated protected workflow. */
|
|
29
|
+
export declare function canonicalStageAuthorityDerivationScript(): string;
|
|
30
|
+
/** @internal Exported for executable source-contract tests. */
|
|
31
|
+
export declare function exactStagingPromotionPullGuardScript(): string;
|
|
27
32
|
/** @internal Shared verbatim with the generated workflow and executable tests. */
|
|
28
33
|
export declare function stagingPromotionReceiptValidationScript(): string;
|
|
29
34
|
/** @internal Shared verbatim with the generated workflow and executable tests. */
|