@go-to-k/cdkd 0.284.30 → 0.284.31
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/dist/{asg-provider-qMgJEhCM.js → asg-provider-DJvcY8lj.js} +2 -2
- package/dist/{asg-provider-qMgJEhCM.js.map → asg-provider-DJvcY8lj.js.map} +1 -1
- package/dist/cli.js +170 -96
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-BFgi0RJr.js → deploy-engine-DYdEWX-s.js} +16 -7
- package/dist/deploy-engine-DYdEWX-s.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-BFgi0RJr.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as endCommandInterruptScope, $t as createAssetRedirectResolver, A as MULTI_REGION_RECREATE_BLOCKED_TYPES, An as resolveUseCdkBootstrapAssets, At as maskSecretsInText, B as collectDeclaredOutputNames, Bn as canonicalizeRegion, Bt as INTRINSIC_KEYS, C as ccRoutedFinalSnapshotError, Cn as getLegacyStateBucketName, Ct as STATE_SOURCED_READBACK_RULES, D as unsupportedFinalSnapshotError, Dn as resolveSkipPrefix, Dt as errorCauseChain, E as refusesFinalSnapshot, En as resolveCaptureObservedState, Et as dynamicReferenceTokens, F as cyan, Fn as MIGRATE_TMP_PREFIX, Ft as s3BucketDualStackDomainName, G as stateKeySecretExposure, Gn as resolveBucketRegion, Gt as LockManager, H as exportAliasCollisionScrubWarning, Hn as AssemblyReader, Ht as withRetry, I as gray, In as findLargeInlineResources, It as s3BucketRegionalDomainName, J as clearOnUpdateRemoval, Jn as resetAwsClients, Jt as shouldRetainResource, K as IAMRoleProvider, Kn as AwsClients, Kt as S3StateBackend, L as green, Ln as uploadCfnTemplate, Lt as s3BucketWebsiteUrl, M as renderStatefulReason, Mn as warnDeprecatedNoPrefixCliFlag, Mt as scrubResourceRecord, N as formatResourceLine, Nn as CFN_TEMPLATE_BODY_LIMIT, Nt as s3BucketArn, O as makeCanonicalizePropertiesFn, On as resolveStateBucketWithDefault, Ot as isSingleDynamicReferenceToken, P as bold, Pn as CFN_TEMPLATE_URL_LIMIT, Pt as s3BucketDomainName, Q as beginCommandInterruptScope, Qt as buildAssetRedirectMap, R as red, Rn as expectedOwnerParam, Rt as applyRoleArnIfSet, S as buildFinalSnapshotIdentifier, Sn as getDefaultStateBucketName, Sr as markNonRetryable, St as STATE_SOURCED_CROSS_GENERATION_RULES, T as isFinalSnapshotError, Tn as resolveAutoAssetStorage, Tt as createSecretMasker, U as isExportAliasCollision, Un as processStackMessages, Ut as DagBuilder, V as collectPublishedOutputNames, Vn as derivePartitionAndUrlSuffix, Vt as describeTypeWithThrottleRetry, W as secretBearingStateKeyWarning, Wt as TemplateParser, X as findActionableSilentDrops, Xt as stringifyValue, Y as ProviderRegistry, Yn as setAwsClients, Yt as AssetPublisher, Z as findSilentDropProperties, Zn as CdkdError, Zt as WorkGraph, _ as maskingRetryLogger, _n as runDockerStreaming, _r as normalizeAwsError, _t as readConfigString, a as DeploymentEventsStore, an as BOOTSTRAP_MARKER_PREFIX, ar as MissingCdkCliError, at as isTerminationProtectionPropagationError, b as ATOMIC_FINAL_SNAPSHOT_TYPES, bn as Synthesizer, br as isRetryableTransientError, bt as requireConfigObject, c as planRollback, cn as parseBootstrapMarker, cr as ProvisioningError, ct as getAccountInfo, d as replayRollback, dn as validateContainerRepoName, dr as StackHasActiveImportsError, dt as normalizeAwsTagsToCfn, en as loadPublishableAssetManifest, er as DeployCancelledError, et as isInterruptedWaitError, f as updatePartialMessage, fn as buildDenyExternalAccessPolicy, fr as StackTerminationProtectionError, ft as resolveExplicitPhysicalId, g as withResourceDeadline, gn as runDockerForeground, gt as configStringRefusal, h as deleteSkipReason, hn as getDockerCmd, ht as configBooleanRefusal, i as DeploymentEventsReader, in as AssetModeResolver, it as disableInstanceApiTermination, j as isStatefulRecreateTargetSync, jn as stateBucketExistenceConfirmed, jt as redactSecretsForState, k as extractDeploymentEventError, kn as resolveStateBucketWithDefaultAndSource, kt as maskSecretsInError, l as producerRegionsFromState, ln as readBootstrapMarkerBody, lr as ResourceTimeoutError, lt as refStateLookupFromResource, m as UNSPECIFIED_SKIP_REASON, mn as formatDockerLoginError, mt as coerceCfnBoolean, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as escapeRegExp, nr as LocalMigrateError, nt as CloudControlProvider, o as classifyReplaySecretRegion, on as ensureAssetStorage, or as NestedStackChildDirectDestroyError, ot as IntrinsicFunctionResolver, p as updatePartialReason, pn as buildDockerImage, pt as assertRegionMatch, q as collectInlinePolicyNamesManagedBySiblings, qn as getAwsClients, qt as rebuildClientForBucketRegion, r as DeployEngine, rn as stripControlChars, rr as LocalStartServiceError, rt as slowCcOperationTimeoutMs, s as planFailedOps, sn as getBootstrapMarkerKey, sr as PartialFailureError, st as cfnRefValueFromPhysicalId, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as rewriteTemplateAssetReferences, tr as LocalInvokeBuildError$1, tt as startInterruptWatch, u as replayFailedOperations, un as validateAssetBucketName, ur as ResourceUpdateNotSupportedError, ut as WAFv2WebACLProvider, v as IMPLICIT_DELETE_DEPENDENCIES, vn as AssetManifestLoader, vr as withErrorHandling, vt as replayWarn, w as createPreDeleteFinalSnapshot, wn as resolveApp, wt as TEMPLATE_SOURCED_RULES, x as PRE_DELETE_SNAPSHOT_TYPES, xn as synthesisStatusMessage, xr as isThrottlingError, xt as requireConfigString, y as computeImplicitDeleteEdges, yn as getDockerImageBySourceHash, yr as isMarkedNonRetryable, yt as requireConfigArray, z as yellow, zn as PARTITION_TABLE, zt as DiffCalculator } from "./deploy-engine-
|
|
2
|
+
import { $ as endCommandInterruptScope, $t as createAssetRedirectResolver, A as MULTI_REGION_RECREATE_BLOCKED_TYPES, An as resolveUseCdkBootstrapAssets, At as maskSecretsInText, B as collectDeclaredOutputNames, Bn as canonicalizeRegion, Bt as INTRINSIC_KEYS, C as ccRoutedFinalSnapshotError, Cn as getLegacyStateBucketName, Ct as STATE_SOURCED_READBACK_RULES, D as unsupportedFinalSnapshotError, Dn as resolveSkipPrefix, Dt as errorCauseChain, E as refusesFinalSnapshot, En as resolveCaptureObservedState, Et as dynamicReferenceTokens, F as cyan, Fn as MIGRATE_TMP_PREFIX, Ft as s3BucketDualStackDomainName, G as stateKeySecretExposure, Gn as resolveBucketRegion, Gt as LockManager, H as exportAliasCollisionScrubWarning, Hn as AssemblyReader, Ht as withRetry, I as gray, In as findLargeInlineResources, It as s3BucketRegionalDomainName, J as clearOnUpdateRemoval, Jn as resetAwsClients, Jt as shouldRetainResource, K as IAMRoleProvider, Kn as AwsClients, Kt as S3StateBackend, L as green, Ln as uploadCfnTemplate, Lt as s3BucketWebsiteUrl, M as renderStatefulReason, Mn as warnDeprecatedNoPrefixCliFlag, Mt as scrubResourceRecord, N as formatResourceLine, Nn as CFN_TEMPLATE_BODY_LIMIT, Nt as s3BucketArn, O as makeCanonicalizePropertiesFn, On as resolveStateBucketWithDefault, Ot as isSingleDynamicReferenceToken, P as bold, Pn as CFN_TEMPLATE_URL_LIMIT, Pt as s3BucketDomainName, Q as beginCommandInterruptScope, Qt as buildAssetRedirectMap, R as red, Rn as expectedOwnerParam, Rt as applyRoleArnIfSet, S as buildFinalSnapshotIdentifier, Sn as getDefaultStateBucketName, Sr as markNonRetryable, St as STATE_SOURCED_CROSS_GENERATION_RULES, T as isFinalSnapshotError, Tn as resolveAutoAssetStorage, Tt as createSecretMasker, U as isExportAliasCollision, Un as processStackMessages, Ut as DagBuilder, V as collectPublishedOutputNames, Vn as derivePartitionAndUrlSuffix, Vt as describeTypeWithThrottleRetry, W as secretBearingStateKeyWarning, Wt as TemplateParser, X as findActionableSilentDrops, Xt as stringifyValue, Y as ProviderRegistry, Yn as setAwsClients, Yt as AssetPublisher, Z as findSilentDropProperties, Zn as CdkdError, Zt as WorkGraph, _ as maskingRetryLogger, _n as runDockerStreaming, _r as normalizeAwsError, _t as readConfigString, a as DeploymentEventsStore, an as BOOTSTRAP_MARKER_PREFIX, ar as MissingCdkCliError, at as isTerminationProtectionPropagationError, b as ATOMIC_FINAL_SNAPSHOT_TYPES, bn as Synthesizer, br as isRetryableTransientError, bt as requireConfigObject, c as planRollback, cn as parseBootstrapMarker, cr as ProvisioningError, ct as getAccountInfo, d as replayRollback, dn as validateContainerRepoName, dr as StackHasActiveImportsError, dt as normalizeAwsTagsToCfn, en as loadPublishableAssetManifest, er as DeployCancelledError, et as isInterruptedWaitError, f as updatePartialMessage, fn as buildDenyExternalAccessPolicy, fr as StackTerminationProtectionError, ft as resolveExplicitPhysicalId, g as withResourceDeadline, gn as runDockerForeground, gt as configStringRefusal, h as deleteSkipReason, hn as getDockerCmd, ht as configBooleanRefusal, i as DeploymentEventsReader, in as AssetModeResolver, it as disableInstanceApiTermination, j as isStatefulRecreateTargetSync, jn as stateBucketExistenceConfirmed, jt as redactSecretsForState, k as extractDeploymentEventError, kn as resolveStateBucketWithDefaultAndSource, kt as maskSecretsInError, l as producerRegionsFromState, ln as readBootstrapMarkerBody, lr as ResourceTimeoutError, lt as refStateLookupFromResource, m as UNSPECIFIED_SKIP_REASON, mn as formatDockerLoginError, mt as coerceCfnBoolean, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as escapeRegExp, nr as LocalMigrateError, nt as CloudControlProvider, o as classifyReplaySecretRegion, on as ensureAssetStorage, or as NestedStackChildDirectDestroyError, ot as IntrinsicFunctionResolver, p as updatePartialReason, pn as buildDockerImage, pt as assertRegionMatch, q as collectInlinePolicyNamesManagedBySiblings, qn as getAwsClients, qt as rebuildClientForBucketRegion, r as DeployEngine, rn as stripControlChars, rr as LocalStartServiceError, rt as slowCcOperationTimeoutMs, s as planFailedOps, sn as getBootstrapMarkerKey, sr as PartialFailureError, st as cfnRefValueFromPhysicalId, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as rewriteTemplateAssetReferences, tr as LocalInvokeBuildError$1, tt as startInterruptWatch, u as replayFailedOperations, un as validateAssetBucketName, ur as ResourceUpdateNotSupportedError, ut as WAFv2WebACLProvider, v as IMPLICIT_DELETE_DEPENDENCIES, vn as AssetManifestLoader, vr as withErrorHandling, vt as replayWarn, w as createPreDeleteFinalSnapshot, wn as resolveApp, wt as TEMPLATE_SOURCED_RULES, x as PRE_DELETE_SNAPSHOT_TYPES, xn as synthesisStatusMessage, xr as isThrottlingError, xt as requireConfigString, y as computeImplicitDeleteEdges, yn as getDockerImageBySourceHash, yr as isMarkedNonRetryable, yt as requireConfigArray, z as yellow, zn as PARTITION_TABLE, zt as DiffCalculator } from "./deploy-engine-DYdEWX-s.js";
|
|
3
3
|
import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as getCurrentStackName, h as withStackName, i as runStackBuffered, m as withSkipPrefix, n as getLogger, o as PATTERN_B_NAME_OPTIONS, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-zRrlbaQt.js";
|
|
4
|
-
import { t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as ASGProvider } from "./asg-provider-DJvcY8lj.js";
|
|
5
5
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
6
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
7
7
|
import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketEncryptionCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketOwnershipControlsCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketOwnershipControlsCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -77,6 +77,162 @@ import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
|
77
77
|
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
78
78
|
import { WebSocketServer } from "ws";
|
|
79
79
|
|
|
80
|
+
//#region src/cli/commands/state-file-keys.ts
|
|
81
|
+
/**
|
|
82
|
+
* Shared helpers for enumerating and describing cdkd state-bucket keys.
|
|
83
|
+
*
|
|
84
|
+
* Extracted from `bootstrap-destroy.ts` (issue #1010) so that `cdkd gc`
|
|
85
|
+
* (issue #1012) reuses the exact same whole-bucket state-file discovery
|
|
86
|
+
* instead of duplicating it — the "scan the WHOLE bucket, not just the
|
|
87
|
+
* default `cdkd/` prefix" rule was a review blocker once already (PR #1018)
|
|
88
|
+
* and must not drift between the two commands.
|
|
89
|
+
*/
|
|
90
|
+
/**
|
|
91
|
+
* Every cdkd state file ends with this suffix, regardless of the
|
|
92
|
+
* `--state-prefix` it was deployed under (`{prefix}/{stack}/{region}/state.json`,
|
|
93
|
+
* or legacy `{prefix}/{stack}/state.json`).
|
|
94
|
+
*/
|
|
95
|
+
const STATE_FILE_SUFFIX = "/state.json";
|
|
96
|
+
/**
|
|
97
|
+
* The prefix `cdkd bootstrap` writes under, and the one `gc` /
|
|
98
|
+
* `bootstrap --destroy` configure their own state backend with.
|
|
99
|
+
*
|
|
100
|
+
* Shared here rather than spelled per command for the same reason this module
|
|
101
|
+
* exists at all: `bootstrap-destroy.ts` and `gc.ts` each held their own copy,
|
|
102
|
+
* and it is now also the `knownPrefix` argument to {@link describeStateKey},
|
|
103
|
+
* where a copy that drifted from the backend's actual prefix would silently
|
|
104
|
+
* send every key back to the shape heuristic instead of the exact depth rule.
|
|
105
|
+
*
|
|
106
|
+
* Note this is only the DEFAULT. Other commands accept `--state-prefix`, so
|
|
107
|
+
* the whole-bucket listings deliberately do not scope to it.
|
|
108
|
+
*/
|
|
109
|
+
const DEFAULT_STATE_PREFIX = "cdkd";
|
|
110
|
+
/**
|
|
111
|
+
* Segments left under a known prefix for the legacy `{prefix}/{stack}` layout.
|
|
112
|
+
*
|
|
113
|
+
* The twin of `S3StateBackend`'s `LEGACY_KEY_DEPTH`, which counts the
|
|
114
|
+
* `state.json` segment as well; this one counts what remains after the prefix
|
|
115
|
+
* and the suffix are stripped, so it is that value minus one. Fenced against it
|
|
116
|
+
* by a unit test rather than imported, because a value import from
|
|
117
|
+
* `s3-state-backend.js` breaks the suites that mock that module.
|
|
118
|
+
*/
|
|
119
|
+
const LEGACY_SEGMENTS_UNDER_PREFIX = 1;
|
|
120
|
+
/**
|
|
121
|
+
* Every cdkd stack lock ends with this suffix — the lock lives next to the
|
|
122
|
+
* state file (`{prefix}/{stack}/{region}/lock.json`, or the legacy region-less
|
|
123
|
+
* `{prefix}/{stack}/lock.json` that `lock-manager.ts` still writes for a
|
|
124
|
+
* pre-v2 stack).
|
|
125
|
+
*/
|
|
126
|
+
const LOCK_FILE_SUFFIX = "/lock.json";
|
|
127
|
+
/**
|
|
128
|
+
* `us-east-1` / `ap-northeast-1` / `us-gov-west-1` — a region-shaped segment.
|
|
129
|
+
*
|
|
130
|
+
* The first token is `[a-z]{2}` OR the literal `eusc` (issue #2001). Every
|
|
131
|
+
* region AWS ships has a two-letter first token except the European Sovereign
|
|
132
|
+
* Cloud partition's, which is four — `eusc-de-east-1` — and the old
|
|
133
|
+
* `^[a-z]{2}` rejected that whole partition. Enumerated from
|
|
134
|
+
* `aws-cdk-lib/region-info` and `@aws-sdk/util-endpoints`: 46 REAL regions,
|
|
135
|
+
* first tokens all two letters or `eusc`, none three. (A count of 53 includes
|
|
136
|
+
* the `aws-global` / `aws-iso-*-global` PSEUDO-regions, whose first token is
|
|
137
|
+
* `aws`; they carry no trailing `-<digits>`, so this pattern rejects them and
|
|
138
|
+
* so did the old one — no behavioural difference, but at 53 the "two letters
|
|
139
|
+
* or eusc" claim is false as stated.)
|
|
140
|
+
*
|
|
141
|
+
* **The exception is enumerated rather than widened, and that is the whole
|
|
142
|
+
* design.** The obvious fix — relaxing the bound to `{2,4}` — was written
|
|
143
|
+
* first and review measured what it cost: `api-prod-1`, `demo-app-1`,
|
|
144
|
+
* `dev-api-1`, `core-api-1`, `test-stack-2` all become region-shaped under it.
|
|
145
|
+
* Those are idiomatic STACK names, and in the legacy region-less layout a
|
|
146
|
+
* stack name sits exactly where a region would, so the pattern would report
|
|
147
|
+
* the key's PREFIX as the stack for each of them — the same wrong answer this
|
|
148
|
+
* issue is about, arriving from the other side and at a far higher rate. A
|
|
149
|
+
* length class cannot separate `eusc` from `demo`; only naming the partition
|
|
150
|
+
* can.
|
|
151
|
+
*
|
|
152
|
+
* The cost of enumerating is that a future partition with a new non-two-letter
|
|
153
|
+
* token needs this line updated. That is the same failure this issue fixed, it
|
|
154
|
+
* is a one-token change, and it fails in the recoverable direction (a region
|
|
155
|
+
* read as a stack name) rather than corrupting ordinary stack names.
|
|
156
|
+
*
|
|
157
|
+
* It stays SHAPE-based and deliberately does not reuse `isClientSafeRegion`
|
|
158
|
+
* from `intrinsic-function-resolver.ts`: that predicate is charset-based (its
|
|
159
|
+
* job is to keep a value inside a hostname label, so it accepts anything
|
|
160
|
+
* lowercase-alphanumeric) and would classify almost every stack name as a
|
|
161
|
+
* region. This one has the opposite job — telling a region segment apart from
|
|
162
|
+
* a stack name in the same position — so it must constrain the shape.
|
|
163
|
+
*
|
|
164
|
+
* A two-letter STACK name like `db-prod-1` is still misread in the legacy
|
|
165
|
+
* layout; that is pre-existing (`main` does the same) and is what the
|
|
166
|
+
* `knownPrefix` depth rule below repairs for callers that can supply one.
|
|
167
|
+
*/
|
|
168
|
+
const REGION_SEGMENT = /^(?:[a-z]{2}|eusc)(-[a-z]+)+-\d+$/;
|
|
169
|
+
/**
|
|
170
|
+
* List every state file in the bucket — the WHOLE bucket, not just the
|
|
171
|
+
* default `cdkd/` prefix. Other commands accept `--state-prefix`, so live
|
|
172
|
+
* stack state may exist under ANY prefix in this bucket; scoping this
|
|
173
|
+
* listing to the default prefix would let reference scans and teardown
|
|
174
|
+
* guards silently miss those stacks and delete live data.
|
|
175
|
+
*/
|
|
176
|
+
async function listAllStateKeys(stateBackend) {
|
|
177
|
+
return (await stateBackend.listRawKeys("")).filter((k) => k.endsWith(STATE_FILE_SUFFIX));
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* List every stack lock file in the bucket — same whole-bucket rule as
|
|
181
|
+
* {@link listAllStateKeys}. A lock under ANY prefix means a deploy /
|
|
182
|
+
* destroy may be in flight for that stack.
|
|
183
|
+
*/
|
|
184
|
+
async function listAllLockKeys(stateBackend) {
|
|
185
|
+
return (await stateBackend.listRawKeys("")).filter((k) => k.endsWith(LOCK_FILE_SUFFIX));
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* `{prefix}/{stack}/{region}/state.json` → `stack (region)`; legacy
|
|
189
|
+
* `{prefix}/{stack}/state.json` → `stack`. Pass {@link LOCK_FILE_SUFFIX} to
|
|
190
|
+
* describe lock keys.
|
|
191
|
+
*
|
|
192
|
+
* The shape heuristic below cannot be made exact, and it errs in BOTH
|
|
193
|
+
* directions -- a region it fails to recognise is reported AS the stack name,
|
|
194
|
+
* while a region-shaped STACK name in the legacy layout makes it report the
|
|
195
|
+
* key's PREFIX as the stack. Neither is safe: `cdkd gc`'s corrupt-state abort
|
|
196
|
+
* turns whichever answer it gets into a `cdkd state show ...` recovery
|
|
197
|
+
* command, so a wrong one is handed to the user as a command to run.
|
|
198
|
+
*
|
|
199
|
+
* `knownPrefix` narrows it where a caller can: a key with exactly one segment
|
|
200
|
+
* under a prefix we know is the legacy `{stack}` layout by construction,
|
|
201
|
+
* whatever the stack is named, so no shape test is needed there at all. That
|
|
202
|
+
* repairs the pre-existing two-letter case (`cdkd/db-prod-1/state.json`, which
|
|
203
|
+
* `main` describes as `cdkd (db-prod-1)`).
|
|
204
|
+
*
|
|
205
|
+
* **It is a narrowing, not a safety net for the pattern above.** A first cut
|
|
206
|
+
* treated it as one — widening `REGION_SEGMENT` to `{2,4}` and relying on this
|
|
207
|
+
* rule to undo the damage — and review measured that the damage is repo-wide
|
|
208
|
+
* while this rule reaches only keys under the ONE prefix a caller names. The
|
|
209
|
+
* listing deliberately spans the whole bucket, so foreign and nested prefixes
|
|
210
|
+
* are in scope and unreachable from here; six shapes regressed against `main`
|
|
211
|
+
* that way. The pattern must be right on its own.
|
|
212
|
+
*
|
|
213
|
+
* **It deliberately does NOT claim the two-segment case.** A first cut did, on
|
|
214
|
+
* the reasoning that at that depth the last segment must be the region -- and
|
|
215
|
+
* review found `--state-prefix cdkd/team-a`, which nests inside the default
|
|
216
|
+
* prefix, so `cdkd/team-a/MyStack/state.json` is a LEGACY key two segments
|
|
217
|
+
* deep and would have been mis-split as `team-a (MyStack)` where `main` said
|
|
218
|
+
* `MyStack`. Depth cannot tell that apart from a real `{stack}/{region}`; only
|
|
219
|
+
* the shape of the tail can, so the heuristic keeps that case.
|
|
220
|
+
*/
|
|
221
|
+
function describeStateKey(key, suffix = STATE_FILE_SUFFIX, knownPrefix) {
|
|
222
|
+
const body = key.slice(0, -suffix.length);
|
|
223
|
+
const prefix = knownPrefix?.replace(/\/+$/, "");
|
|
224
|
+
if (prefix !== void 0 && prefix !== "" && key.startsWith(`${prefix}/`)) {
|
|
225
|
+
const rest = body.slice(prefix.length + 1).split("/");
|
|
226
|
+
if (rest.length === LEGACY_SEGMENTS_UNDER_PREFIX && rest[0]) return rest[0];
|
|
227
|
+
}
|
|
228
|
+
const segments = body.split("/");
|
|
229
|
+
const last = segments[segments.length - 1] ?? key;
|
|
230
|
+
const secondLast = segments[segments.length - 2];
|
|
231
|
+
if (secondLast !== void 0 && secondLast !== "" && REGION_SEGMENT.test(last)) return `${secondLast} (${last})`;
|
|
232
|
+
return last;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
//#endregion
|
|
80
236
|
//#region src/cli/options.ts
|
|
81
237
|
/**
|
|
82
238
|
* Parse context key=value pairs from CLI arguments into a Record
|
|
@@ -152,7 +308,7 @@ const appOptions = [new Option("-a, --app <command>", "CDK app command (e.g., \"
|
|
|
152
308
|
* --state-bucket is optional: falls back to CDKD_STATE_BUCKET env var,
|
|
153
309
|
* then cdk.json context.cdkd.stateBucket
|
|
154
310
|
*/
|
|
155
|
-
const stateOptions = [new Option("--state-bucket <bucket>", "S3 bucket for state storage. Falls back to CDKD_STATE_BUCKET env or cdk.json"), new Option("--state-prefix <prefix>", "S3 key prefix for state files").default(
|
|
311
|
+
const stateOptions = [new Option("--state-bucket <bucket>", "S3 bucket for state storage. Falls back to CDKD_STATE_BUCKET env or cdk.json"), new Option("--state-prefix <prefix>", "S3 key prefix for state files").default(DEFAULT_STATE_PREFIX)];
|
|
156
312
|
/**
|
|
157
313
|
* Stack options
|
|
158
314
|
*/
|
|
@@ -1025,91 +1181,9 @@ function regionNeedsReconciliation(effective) {
|
|
|
1025
1181
|
return effective.region !== LEGACY_DEFAULT_REGION;
|
|
1026
1182
|
}
|
|
1027
1183
|
|
|
1028
|
-
//#endregion
|
|
1029
|
-
//#region src/cli/commands/state-file-keys.ts
|
|
1030
|
-
/**
|
|
1031
|
-
* Shared helpers for enumerating and describing cdkd state-bucket keys.
|
|
1032
|
-
*
|
|
1033
|
-
* Extracted from `bootstrap-destroy.ts` (issue #1010) so that `cdkd gc`
|
|
1034
|
-
* (issue #1012) reuses the exact same whole-bucket state-file discovery
|
|
1035
|
-
* instead of duplicating it — the "scan the WHOLE bucket, not just the
|
|
1036
|
-
* default `cdkd/` prefix" rule was a review blocker once already (PR #1018)
|
|
1037
|
-
* and must not drift between the two commands.
|
|
1038
|
-
*/
|
|
1039
|
-
/**
|
|
1040
|
-
* Every cdkd state file ends with this suffix, regardless of the
|
|
1041
|
-
* `--state-prefix` it was deployed under (`{prefix}/{stack}/{region}/state.json`,
|
|
1042
|
-
* or legacy `{prefix}/{stack}/state.json`).
|
|
1043
|
-
*/
|
|
1044
|
-
const STATE_FILE_SUFFIX = "/state.json";
|
|
1045
|
-
/**
|
|
1046
|
-
* Every cdkd stack lock ends with this suffix — the lock lives next to the
|
|
1047
|
-
* state file (`{prefix}/{stack}/{region}/lock.json`).
|
|
1048
|
-
*/
|
|
1049
|
-
const LOCK_FILE_SUFFIX = "/lock.json";
|
|
1050
|
-
/** `us-east-1` / `ap-northeast-1` / `us-gov-west-1` — a region-shaped segment. */
|
|
1051
|
-
const REGION_SEGMENT = /^[a-z]{2}(-[a-z]+)+-\d+$/;
|
|
1052
|
-
/**
|
|
1053
|
-
* List every state file in the bucket — the WHOLE bucket, not just the
|
|
1054
|
-
* default `cdkd/` prefix. Other commands accept `--state-prefix`, so live
|
|
1055
|
-
* stack state may exist under ANY prefix in this bucket; scoping this
|
|
1056
|
-
* listing to the default prefix would let reference scans and teardown
|
|
1057
|
-
* guards silently miss those stacks and delete live data.
|
|
1058
|
-
*/
|
|
1059
|
-
async function listAllStateKeys(stateBackend) {
|
|
1060
|
-
return (await stateBackend.listRawKeys("")).filter((k) => k.endsWith(STATE_FILE_SUFFIX));
|
|
1061
|
-
}
|
|
1062
|
-
/**
|
|
1063
|
-
* List every stack lock file in the bucket — same whole-bucket rule as
|
|
1064
|
-
* {@link listAllStateKeys}. A lock under ANY prefix means a deploy /
|
|
1065
|
-
* destroy may be in flight for that stack.
|
|
1066
|
-
*/
|
|
1067
|
-
async function listAllLockKeys(stateBackend) {
|
|
1068
|
-
return (await stateBackend.listRawKeys("")).filter((k) => k.endsWith(LOCK_FILE_SUFFIX));
|
|
1069
|
-
}
|
|
1070
|
-
/**
|
|
1071
|
-
* `{prefix}/{stack}/{region}/state.json` → `stack (region)`; legacy
|
|
1072
|
-
* `{prefix}/{stack}/state.json` → `stack`. The prefix is arbitrary (custom
|
|
1073
|
-
* `--state-prefix` values included), so the stack/region pair is derived
|
|
1074
|
-
* from the key's TAIL: the last segment is treated as a region only when
|
|
1075
|
-
* it is region-shaped. Pass {@link LOCK_FILE_SUFFIX} to describe lock keys.
|
|
1076
|
-
*/
|
|
1077
|
-
function describeStateKey(key, suffix = STATE_FILE_SUFFIX) {
|
|
1078
|
-
const segments = key.slice(0, -suffix.length).split("/");
|
|
1079
|
-
const last = segments[segments.length - 1] ?? key;
|
|
1080
|
-
const secondLast = segments[segments.length - 2];
|
|
1081
|
-
if (secondLast !== void 0 && REGION_SEGMENT.test(last)) return `${secondLast} (${last})`;
|
|
1082
|
-
return last;
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
1184
|
//#endregion
|
|
1086
1185
|
//#region src/cli/commands/bootstrap-destroy.ts
|
|
1087
1186
|
/**
|
|
1088
|
-
* `cdkd bootstrap --destroy` — teardown of cdkd-created account resources
|
|
1089
|
-
* (issue #1010, the reverse of `cdkd bootstrap` / `ensureAssetStorage`).
|
|
1090
|
-
*
|
|
1091
|
-
* Scope per invocation is ONE region's asset storage (`--region`, same
|
|
1092
|
-
* option the create side uses): empty + delete the asset bucket, force-
|
|
1093
|
-
* delete the container-asset ECR repo, then delete the region's bootstrap
|
|
1094
|
-
* marker LAST — the mirror image of the create side's marker-written-last
|
|
1095
|
-
* ordering, so a crash mid-teardown leaves the region still consistently
|
|
1096
|
-
* opted in (marker present → deploys keep hard-erroring at the missing
|
|
1097
|
-
* resources with a re-bootstrap hint, never a silent legacy fallback).
|
|
1098
|
-
*
|
|
1099
|
-
* The asset bucket / repo NAMES are read from the region's bootstrap
|
|
1100
|
-
* marker, NOT recomputed from the naming convention — the marker is the
|
|
1101
|
-
* source of truth for names (design §4.1), which keeps this teardown
|
|
1102
|
-
* compatible with custom asset-storage names (issue #1011).
|
|
1103
|
-
*
|
|
1104
|
-
* The state bucket is NOT deleted by default: it is the account's source
|
|
1105
|
-
* of truth. `--include-state-bucket` opts in, and even then the deletion
|
|
1106
|
-
* is refused while ANY stack state exists in the bucket or any OTHER
|
|
1107
|
-
* region is still opted in to asset storage (deleting their markers would
|
|
1108
|
-
* silently flip those regions back to legacy mode).
|
|
1109
|
-
*/
|
|
1110
|
-
/** S3 key prefix for state files — same fixed value the create side uses. */
|
|
1111
|
-
const STATE_PREFIX = "cdkd";
|
|
1112
|
-
/**
|
|
1113
1187
|
* Interactive confirmation for the teardown. Follows the repo's
|
|
1114
1188
|
* destructive-prompt convention (see `recreate-confirm-prompt.ts`, the
|
|
1115
1189
|
* same pattern family as `confirm-prompt.ts`): print the deletion plan as
|
|
@@ -1157,7 +1231,7 @@ async function scanStateReferences(stateBackend, names) {
|
|
|
1157
1231
|
for (const key of stateKeys) {
|
|
1158
1232
|
const body = await stateBackend.getRawObject(key);
|
|
1159
1233
|
if (body === null) continue;
|
|
1160
|
-
if (names.some((name) => body.includes(name))) referencing.push(describeStateKey(key));
|
|
1234
|
+
if (names.some((name) => body.includes(name))) referencing.push(describeStateKey(key, STATE_FILE_SUFFIX, DEFAULT_STATE_PREFIX));
|
|
1161
1235
|
}
|
|
1162
1236
|
return referencing;
|
|
1163
1237
|
}
|
|
@@ -1332,7 +1406,7 @@ async function bootstrapDestroyCommand(options) {
|
|
|
1332
1406
|
...options.profile && { profile: options.profile }
|
|
1333
1407
|
}), {
|
|
1334
1408
|
bucket: bucketName,
|
|
1335
|
-
prefix:
|
|
1409
|
+
prefix: DEFAULT_STATE_PREFIX
|
|
1336
1410
|
}, {
|
|
1337
1411
|
region: effective.region,
|
|
1338
1412
|
...options.profile && { profile: options.profile }
|
|
@@ -1361,7 +1435,7 @@ async function bootstrapDestroyCommand(options) {
|
|
|
1361
1435
|
});
|
|
1362
1436
|
const stateBackend = new S3StateBackend(markerS3Client, {
|
|
1363
1437
|
bucket: bucketName,
|
|
1364
|
-
prefix:
|
|
1438
|
+
prefix: DEFAULT_STATE_PREFIX
|
|
1365
1439
|
}, {
|
|
1366
1440
|
region,
|
|
1367
1441
|
...options.profile && { profile: options.profile }
|
|
@@ -1413,7 +1487,7 @@ async function bootstrapDestroyCommand(options) {
|
|
|
1413
1487
|
if (options.includeStateBucket) {
|
|
1414
1488
|
const stateKeys = await listAllStateKeys(stateBackend);
|
|
1415
1489
|
if (stateKeys.length > 0) {
|
|
1416
|
-
const listing = stateKeys.map((k) => ` - ${describeStateKey(k)} [${k}]`).join("\n");
|
|
1490
|
+
const listing = stateKeys.map((k) => ` - ${describeStateKey(k, STATE_FILE_SUFFIX, DEFAULT_STATE_PREFIX)} [${k}]`).join("\n");
|
|
1417
1491
|
throw new CdkdError(`Refusing to delete state bucket '${bucketName}': ${stateKeys.length} stack(s) still have state in it:\n${listing}\nDestroy every stack ('cdkd destroy' / 'cdkd state destroy') before deleting the state bucket.`, "STATE_BUCKET_NOT_EMPTY");
|
|
1418
1492
|
}
|
|
1419
1493
|
if (markerSiblings.otherRegions.length > 0) throw new CdkdError(`Refusing to delete state bucket '${bucketName}': region(s) ${markerSiblings.otherRegions.join(", ")} are still opted in to cdkd asset storage (their bootstrap markers live in this bucket). Run 'cdkd bootstrap --destroy --region <r>' for each first.`, "STATE_BUCKET_HOLDS_MARKERS");
|
|
@@ -1509,7 +1583,7 @@ async function bootstrapCommand(options) {
|
|
|
1509
1583
|
...options.profile && { profile: options.profile }
|
|
1510
1584
|
}), {
|
|
1511
1585
|
bucket: bucketName,
|
|
1512
|
-
prefix:
|
|
1586
|
+
prefix: DEFAULT_STATE_PREFIX
|
|
1513
1587
|
}, {
|
|
1514
1588
|
region: effective.region,
|
|
1515
1589
|
...options.profile && { profile: options.profile }
|
|
@@ -1610,7 +1684,7 @@ async function bootstrapCommand(options) {
|
|
|
1610
1684
|
});
|
|
1611
1685
|
const stateBackend = new S3StateBackend(markerS3Client, {
|
|
1612
1686
|
bucket: bucketName,
|
|
1613
|
-
prefix:
|
|
1687
|
+
prefix: DEFAULT_STATE_PREFIX
|
|
1614
1688
|
}, {
|
|
1615
1689
|
region,
|
|
1616
1690
|
...options.profile && { profile: options.profile }
|
|
@@ -73031,7 +73105,7 @@ async function scanReferencedAssets(stateBackend, marker, logger) {
|
|
|
73031
73105
|
try {
|
|
73032
73106
|
parsed = JSON.parse(body);
|
|
73033
73107
|
} catch (error) {
|
|
73034
|
-
const described = describeStateKey(key);
|
|
73108
|
+
const described = describeStateKey(key, STATE_FILE_SUFFIX, DEFAULT_STATE_PREFIX);
|
|
73035
73109
|
const regionMatch = /^(\S+) \((\S+)\)$/.exec(described);
|
|
73036
73110
|
throw new CdkdError(`State file '${key}' is not valid JSON — aborting: gc must know every referenced asset before deleting anything, and this file's references are unreadable. Repair or remove the corrupt state file ('${regionMatch ? `cdkd state show ${regionMatch[1]} --stack-region ${regionMatch[2]}` : `cdkd state show ${described}`}' to inspect), then re-run.`, "GC_STATE_UNREADABLE", error);
|
|
73037
73111
|
}
|
|
@@ -73236,7 +73310,7 @@ async function gcCommand(options) {
|
|
|
73236
73310
|
});
|
|
73237
73311
|
const stateBackend = new S3StateBackend(markerS3Client, {
|
|
73238
73312
|
bucket: bucketName,
|
|
73239
|
-
prefix:
|
|
73313
|
+
prefix: DEFAULT_STATE_PREFIX
|
|
73240
73314
|
}, {
|
|
73241
73315
|
region: effective.region,
|
|
73242
73316
|
...options.profile && { profile: options.profile }
|
|
@@ -73282,7 +73356,7 @@ async function gcCommand(options) {
|
|
|
73282
73356
|
const marker = parseBootstrapMarker(markerBody, resolvedMarkerKey);
|
|
73283
73357
|
const lockKeys = await listAllLockKeys(stateBackend);
|
|
73284
73358
|
if (lockKeys.length > 0) {
|
|
73285
|
-
const listing = lockKeys.map((k) => ` - ${describeStateKey(k, LOCK_FILE_SUFFIX)} [${k}]`).join("\n");
|
|
73359
|
+
const listing = lockKeys.map((k) => ` - ${describeStateKey(k, LOCK_FILE_SUFFIX, DEFAULT_STATE_PREFIX)} [${k}]`).join("\n");
|
|
73286
73360
|
throw new CdkdError(`Refusing to gc while ${lockKeys.length} stack(s) hold an active lock (a deploy in flight may have published assets whose state write has not landed yet):\n${listing}\nWait for the operation(s) to finish — or clear a stale lock with 'cdkd force-unlock <stack>' — then re-run 'cdkd gc'.`, "GC_LOCKED");
|
|
73287
73361
|
}
|
|
73288
73362
|
const refs = await scanReferencedAssets(stateBackend, marker, logger);
|
|
@@ -85084,7 +85158,7 @@ async function promptConfirm(message) {
|
|
|
85084
85158
|
* callers get the standard exit-code-2 routing.
|
|
85085
85159
|
*/
|
|
85086
85160
|
function createMigrateCommand() {
|
|
85087
|
-
return new Command("migrate").description("Adopt a plain (non-CDK) CloudFormation stack into a cdkd-managed CDK app. Generates new CDK code via upstream `cdk migrate`, builds a logical-ID mapping between the source CFn template and the synth template, writes cdkd state, and (optionally) retires the source CFn stack. AWS resources are never modified.").argument("[stack]", "Source CFn stack name. Alias for --from-cfn-stack.").addOption(new Option("--from-cfn-stack <name>", "Source CloudFormation stack name to adopt. Required (or pass positionally).")).addOption(new Option("--output-dir <dir>", "Directory to write the generated CDK app to. Defaults to <cwd>/<CfnStackName>.")).addOption(new Option("--language <choice>", "Generated code language. v1: typescript only.").choices(["typescript"]).default("typescript")).addOption(new Option("--region <region>", "AWS region. Defaults to AWS_REGION env / profile.")).addOption(new Option("--account <id>", "AWS account ID. Auto-detected via STS when omitted.")).addOption(new Option("--retire-cfn-stack", "After cdkd state is written, inject DeletionPolicy=Retain on every resource in the source CFn stack and DeleteStack. AWS resources stay; the CFn stack record is gone. Off by default.").default(false)).addOption(new Option("--filter <key=value>", "Pass-through to `cdk migrate --filter` for resource subsetting. Repeatable.").argParser((value, previous) => [...previous ?? [], value]).default([])).addOption(new Option("--skip-install", "Skip `npm install` after codegen.").default(false)).addOption(new Option("--skip-synth", "Skip `cdk synth` (does NOT write cdkd state). Mutually exclusive with --retire-cfn-stack.").default(false)).addOption(new Option("--dry-run", "Print the import plan without writing state or retiring the CFn stack. Mutually exclusive with --retire-cfn-stack.").default(false)).addOption(new Option("-y, --yes", "Auto-confirm the import + retirement prompts.").default(false)).addOption(new Option("--cdk-bin <path>", "Override the `cdk` binary path.")).addOption(new Option("--resource-mapping <file>", `Path to a JSON file of {sourceLogicalId: synthLogicalId} overrides. Same shape as the auto-written ${RESOURCE_MAPPING_FILENAME}.`)).addOption(new Option("--state-bucket <name>", "cdkd state bucket. Defaults to cdkd-state-<accountId>.")).addOption(new Option("--state-prefix <prefix>", "cdkd state prefix inside the bucket.").default(
|
|
85161
|
+
return new Command("migrate").description("Adopt a plain (non-CDK) CloudFormation stack into a cdkd-managed CDK app. Generates new CDK code via upstream `cdk migrate`, builds a logical-ID mapping between the source CFn template and the synth template, writes cdkd state, and (optionally) retires the source CFn stack. AWS resources are never modified.").argument("[stack]", "Source CFn stack name. Alias for --from-cfn-stack.").addOption(new Option("--from-cfn-stack <name>", "Source CloudFormation stack name to adopt. Required (or pass positionally).")).addOption(new Option("--output-dir <dir>", "Directory to write the generated CDK app to. Defaults to <cwd>/<CfnStackName>.")).addOption(new Option("--language <choice>", "Generated code language. v1: typescript only.").choices(["typescript"]).default("typescript")).addOption(new Option("--region <region>", "AWS region. Defaults to AWS_REGION env / profile.")).addOption(new Option("--account <id>", "AWS account ID. Auto-detected via STS when omitted.")).addOption(new Option("--retire-cfn-stack", "After cdkd state is written, inject DeletionPolicy=Retain on every resource in the source CFn stack and DeleteStack. AWS resources stay; the CFn stack record is gone. Off by default.").default(false)).addOption(new Option("--filter <key=value>", "Pass-through to `cdk migrate --filter` for resource subsetting. Repeatable.").argParser((value, previous) => [...previous ?? [], value]).default([])).addOption(new Option("--skip-install", "Skip `npm install` after codegen.").default(false)).addOption(new Option("--skip-synth", "Skip `cdk synth` (does NOT write cdkd state). Mutually exclusive with --retire-cfn-stack.").default(false)).addOption(new Option("--dry-run", "Print the import plan without writing state or retiring the CFn stack. Mutually exclusive with --retire-cfn-stack.").default(false)).addOption(new Option("-y, --yes", "Auto-confirm the import + retirement prompts.").default(false)).addOption(new Option("--cdk-bin <path>", "Override the `cdk` binary path.")).addOption(new Option("--resource-mapping <file>", `Path to a JSON file of {sourceLogicalId: synthLogicalId} overrides. Same shape as the auto-written ${RESOURCE_MAPPING_FILENAME}.`)).addOption(new Option("--state-bucket <name>", "cdkd state bucket. Defaults to cdkd-state-<accountId>.")).addOption(new Option("--state-prefix <prefix>", "cdkd state prefix inside the bucket.").default(DEFAULT_STATE_PREFIX)).addOption(new Option("--profile <name>", "AWS profile name.")).addOption(new Option("--role-arn <arn>", "IAM role to assume before any AWS call.")).addOption(new Option("--verbose", "Enable debug-level logging.").default(false)).action(withErrorHandling(migrateCommandAction));
|
|
85088
85162
|
}
|
|
85089
85163
|
|
|
85090
85164
|
//#endregion
|
|
@@ -85107,7 +85181,7 @@ function createMigrateCommand() {
|
|
|
85107
85181
|
*/
|
|
85108
85182
|
function buildProgram() {
|
|
85109
85183
|
const program = new Command();
|
|
85110
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.
|
|
85184
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.31");
|
|
85111
85185
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
85112
85186
|
const { profile } = actionCommand.optsWithGlobals();
|
|
85113
85187
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|