@go-to-k/cdkd 0.235.0 → 0.236.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -4
- package/dist/{asg-provider-Dd9Xze-T.js → asg-provider-Dbl1_mwT.js} +3 -2
- package/dist/{asg-provider-Dd9Xze-T.js.map → asg-provider-Dbl1_mwT.js.map} +1 -1
- package/dist/{aws-clients-pjPwZz1r.js → aws-clients-B15NAPbL.js} +1 -1
- package/dist/{aws-clients-pjPwZz1r.js.map → aws-clients-B15NAPbL.js.map} +1 -1
- package/dist/cli.js +372 -14
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-Cbi5IzWV.js → deploy-engine-DqAIXGA_.js} +65 -11
- package/dist/deploy-engine-DqAIXGA_.js.map +1 -0
- package/dist/{ec2-termination-protection-uvVnteGl.js → ec2-termination-protection-q-WGWgxa.js} +3 -827
- package/dist/ec2-termination-protection-q-WGWgxa.js.map +1 -0
- package/dist/expected-bucket-owner-BjEfUnkA.js +87 -0
- package/dist/expected-bucket-owner-BjEfUnkA.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/logger-BzO-joNR.js +828 -0
- package/dist/logger-BzO-joNR.js.map +1 -0
- package/package.json +1 -1
- package/dist/deploy-engine-Cbi5IzWV.js.map +0 -1
- package/dist/ec2-termination-protection-uvVnteGl.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { a as
|
|
4
|
-
import {
|
|
5
|
-
import { t as
|
|
2
|
+
import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as withSkipPrefix, i as runStackBuffered, n as getLogger, o as PATTERN_B_NAME_OPTIONS, p as withStackName, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-BzO-joNR.js";
|
|
3
|
+
import { A as withErrorHandling, C as StackHasActiveImportsError, S as ResourceUpdateNotSupportedError, _ as MissingCdkCliError, a as normalizeAwsTagsToCfn, b as ProvisioningError, f as LocalInvokeBuildError$1, i as matchesCdkPath, k as normalizeAwsError, l as CdkdError, m as LocalStartServiceError, n as isTerminationProtectionPropagationError, o as resolveExplicitPhysicalId, p as LocalMigrateError, r as CDK_PATH_TAG, s as assertRegionMatch, t as disableInstanceApiTermination, v as NestedStackChildDirectDestroyError, w as StackTerminationProtectionError, x as ResourceTimeoutError, y as PartialFailureError } from "./ec2-termination-protection-q-WGWgxa.js";
|
|
4
|
+
import { a as setAwsClients, i as resetAwsClients, r as getAwsClients, t as AwsClients } from "./aws-clients-B15NAPbL.js";
|
|
5
|
+
import { $ as runDockerStreaming, A as DiffCalculator, B as buildAssetRedirectMap, C as findActionableSilentDrops, D as refStateLookupFromResource, E as cfnRefValueFromPhysicalId, F as rebuildClientForBucketRegion, G as BOOTSTRAP_MARKER_PREFIX, H as loadPublishableAssetManifest, I as shouldRetainResource, J as parseBootstrapMarker, K as ensureAssetStorage, L as AssetPublisher, M as TemplateParser, N as LockManager, O as WAFv2WebACLProvider, P as S3StateBackend, Q as runDockerForeground, R as stringifyValue, S as ProviderRegistry, T as IntrinsicFunctionResolver, U as rewriteTemplateAssetReferences, V as createAssetRedirectResolver, W as AssetModeResolver, X as formatDockerLoginError, Y as buildDockerImage, Z as getDockerCmd, _ as green, _t as findLargeInlineResources, a as withRetry, at as getLegacyStateBucketName, b as IAMRoleProvider, c as computeImplicitDeleteEdges, ct as resolveCaptureObservedState, d as isStatefulRecreateTargetSync, dt as resolveStateBucketWithDefaultAndSource, et as AssetManifestLoader, f as renderStatefulReason, ft as resolveUseCdkBootstrapAssets, g as gray, gt as MIGRATE_TMP_PREFIX, h as cyan, ht as CFN_TEMPLATE_URL_LIMIT, i as withResourceDeadline, it as getDefaultStateBucketName, j as DagBuilder, k as applyRoleArnIfSet, l as extractDeploymentEventError, lt as resolveSkipPrefix, m as bold, mt as CFN_TEMPLATE_BODY_LIMIT, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as Synthesizer, o as isRetryableTransientError, ot as resolveApp, p as formatResourceLine, pt as warnDeprecatedNoPrefixCliFlag, q as getBootstrapMarkerKey, r as DeployEngine, rt as synthesisStatusMessage, s as IMPLICIT_DELETE_DEPENDENCIES, st as resolveAutoAssetStorage, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as getDockerImageBySourceHash, u as MULTI_REGION_RECREATE_BLOCKED_TYPES, ut as resolveStateBucketWithDefault, v as red, vt as uploadCfnTemplate, w as CloudControlProvider, x as collectInlinePolicyNamesManagedBySiblings, xt as resolveBucketRegion, y as yellow, yt as AssemblyReader, z as WorkGraph } from "./deploy-engine-DqAIXGA_.js";
|
|
6
|
+
import { t as expectedOwnerParam } from "./expected-bucket-owner-BjEfUnkA.js";
|
|
7
|
+
import { t as ASGProvider } from "./asg-provider-Dbl1_mwT.js";
|
|
6
8
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
9
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
8
10
|
import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListBucketsCommand, ListDirectoryBucketsCommand, 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";
|
|
@@ -38,9 +40,9 @@ import graphlib from "graphlib";
|
|
|
38
40
|
import { AddTagsToResourceCommand as AddTagsToResourceCommand$1, CreateDBClusterCommand, CreateDBInstanceCommand, CreateDBProxyCommand, CreateDBProxyEndpointCommand, CreateDBSubnetGroupCommand, DBProxyEndpointNotFoundFault, DBProxyNotFoundFault, DBProxyTargetGroupNotFoundFault, DBProxyTargetNotFoundFault, DeleteDBClusterCommand, DeleteDBInstanceCommand, DeleteDBProxyCommand, DeleteDBProxyEndpointCommand, DeleteDBSubnetGroupCommand, DeregisterDBProxyTargetsCommand, DescribeDBClustersCommand, DescribeDBInstancesCommand, DescribeDBProxiesCommand, DescribeDBProxyEndpointsCommand, DescribeDBProxyTargetGroupsCommand, DescribeDBProxyTargetsCommand, DescribeDBSubnetGroupsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$8, ModifyDBClusterCommand, ModifyDBInstanceCommand, ModifyDBProxyCommand, ModifyDBProxyEndpointCommand, ModifyDBProxyTargetGroupCommand, ModifyDBSubnetGroupCommand, RDSClient, RegisterDBProxyTargetsCommand, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$1 } from "@aws-sdk/client-rds";
|
|
39
41
|
import { AddTagsToResourceCommand as AddTagsToResourceCommand$2, CreateCacheClusterCommand, CreateCacheSubnetGroupCommand, DeleteCacheClusterCommand, DeleteCacheSubnetGroupCommand, DescribeCacheClustersCommand, DescribeCacheSubnetGroupsCommand, ElastiCacheClient, ListTagsForResourceCommand as ListTagsForResourceCommand$9, ModifyCacheClusterCommand, ModifyCacheSubnetGroupCommand, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$2 } from "@aws-sdk/client-elasticache";
|
|
40
42
|
import { Command, Option } from "commander";
|
|
41
|
-
import { writeFileSync as writeFileSync$1 } from "fs";
|
|
42
43
|
import * as readline$1 from "node:readline/promises";
|
|
43
44
|
import readline from "node:readline/promises";
|
|
45
|
+
import { writeFileSync as writeFileSync$1 } from "fs";
|
|
44
46
|
import * as zlib from "node:zlib";
|
|
45
47
|
import { ApplicationAutoScalingClient, DeleteScalingPolicyCommand, DeregisterScalableTargetCommand, DescribeScalableTargetsCommand, DescribeScalingPoliciesCommand, PutScalingPolicyCommand, RegisterScalableTargetCommand } from "@aws-sdk/client-application-auto-scaling";
|
|
46
48
|
import { ApiGatewayV2Client, CreateApiCommand, CreateAuthorizerCommand as CreateAuthorizerCommand$1, CreateIntegrationCommand, CreateRouteCommand as CreateRouteCommand$1, CreateStageCommand as CreateStageCommand$1, DeleteApiCommand, DeleteAuthorizerCommand as DeleteAuthorizerCommand$1, DeleteIntegrationCommand, DeleteRouteCommand as DeleteRouteCommand$1, DeleteStageCommand as DeleteStageCommand$1, GetApiCommand, GetApisCommand, GetAuthorizerCommand as GetAuthorizerCommand$1, GetIntegrationCommand, GetRouteCommand, GetStageCommand as GetStageCommand$1, NotFoundException as NotFoundException$3, UpdateApiCommand, UpdateAuthorizerCommand as UpdateAuthorizerCommand$1, UpdateIntegrationCommand, UpdateRouteCommand, UpdateStageCommand as UpdateStageCommand$1 } from "@aws-sdk/client-apigatewayv2";
|
|
@@ -608,6 +610,310 @@ const destroyOptions = [
|
|
|
608
610
|
allowUnsupportedTypesOption
|
|
609
611
|
];
|
|
610
612
|
|
|
613
|
+
//#endregion
|
|
614
|
+
//#region src/cli/commands/bootstrap-destroy.ts
|
|
615
|
+
/**
|
|
616
|
+
* `cdkd bootstrap --destroy` — teardown of cdkd-created account resources
|
|
617
|
+
* (issue #1010, the reverse of `cdkd bootstrap` / `ensureAssetStorage`).
|
|
618
|
+
*
|
|
619
|
+
* Scope per invocation is ONE region's asset storage (`--region`, same
|
|
620
|
+
* option the create side uses): empty + delete the asset bucket, force-
|
|
621
|
+
* delete the container-asset ECR repo, then delete the region's bootstrap
|
|
622
|
+
* marker LAST — the mirror image of the create side's marker-written-last
|
|
623
|
+
* ordering, so a crash mid-teardown leaves the region still consistently
|
|
624
|
+
* opted in (marker present → deploys keep hard-erroring at the missing
|
|
625
|
+
* resources with a re-bootstrap hint, never a silent legacy fallback).
|
|
626
|
+
*
|
|
627
|
+
* The asset bucket / repo NAMES are read from the region's bootstrap
|
|
628
|
+
* marker, NOT recomputed from the naming convention — the marker is the
|
|
629
|
+
* source of truth for names (design §4.1), which keeps this teardown
|
|
630
|
+
* compatible with custom asset-storage names (issue #1011).
|
|
631
|
+
*
|
|
632
|
+
* The state bucket is NOT deleted by default: it is the account's source
|
|
633
|
+
* of truth. `--include-state-bucket` opts in, and even then the deletion
|
|
634
|
+
* is refused while ANY stack state exists in the bucket or any OTHER
|
|
635
|
+
* region is still opted in to asset storage (deleting their markers would
|
|
636
|
+
* silently flip those regions back to legacy mode).
|
|
637
|
+
*/
|
|
638
|
+
/** S3 key prefix for state files — same fixed value the create side uses. */
|
|
639
|
+
const STATE_PREFIX = "cdkd";
|
|
640
|
+
/**
|
|
641
|
+
* Every cdkd state file ends with this suffix, regardless of the
|
|
642
|
+
* `--state-prefix` it was deployed under (`{prefix}/{stack}/{region}/state.json`,
|
|
643
|
+
* or legacy `{prefix}/{stack}/state.json`).
|
|
644
|
+
*/
|
|
645
|
+
const STATE_FILE_SUFFIX = "/state.json";
|
|
646
|
+
/** `us-east-1` / `ap-northeast-1` / `us-gov-west-1` — a region-shaped segment. */
|
|
647
|
+
const REGION_SEGMENT = /^[a-z]{2}(-[a-z]+)+-\d+$/;
|
|
648
|
+
/**
|
|
649
|
+
* List every state file in the bucket — the WHOLE bucket, not just the
|
|
650
|
+
* default `cdkd/` prefix. Other commands accept `--state-prefix`, so live
|
|
651
|
+
* stack state may exist under ANY prefix in this bucket; scoping this
|
|
652
|
+
* listing to the default prefix would let the reference scan and the
|
|
653
|
+
* `--include-state-bucket` guard silently miss those stacks and delete
|
|
654
|
+
* live state.
|
|
655
|
+
*/
|
|
656
|
+
async function listAllStateKeys(stateBackend) {
|
|
657
|
+
return (await stateBackend.listRawKeys("")).filter((k) => k.endsWith(STATE_FILE_SUFFIX));
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Interactive confirmation for the teardown. Follows the repo's
|
|
661
|
+
* destructive-prompt convention (see `recreate-confirm-prompt.ts`, the
|
|
662
|
+
* same pattern family as `confirm-prompt.ts`): print the deletion plan as
|
|
663
|
+
* a WARN block, `--yes` skips the prompt, a non-TTY stdin without `--yes`
|
|
664
|
+
* is a hard error (never hang / never silently decline in CI), and the
|
|
665
|
+
* prompt itself defaults to NO because the side effect is destructive.
|
|
666
|
+
*/
|
|
667
|
+
async function promptBootstrapDestroyConfirm(input) {
|
|
668
|
+
const logger = getLogger();
|
|
669
|
+
logger.warn("");
|
|
670
|
+
logger.warn("cdkd bootstrap --destroy will delete the following:");
|
|
671
|
+
for (const line of input.planLines) logger.warn(` - ${line}`);
|
|
672
|
+
if (input.yes) return true;
|
|
673
|
+
if (process.stdin.isTTY !== true) throw new CdkdError("The bootstrap --destroy confirmation prompt cannot run in a non-interactive environment. Pass --yes / -y to confirm the teardown, or run the command from a real terminal.", "NON_INTERACTIVE_CONFIRM");
|
|
674
|
+
const rl = readline.createInterface({
|
|
675
|
+
input: process.stdin,
|
|
676
|
+
output: process.stdout
|
|
677
|
+
});
|
|
678
|
+
try {
|
|
679
|
+
const trimmed = (await rl.question("\nContinue? (y/N): ")).trim().toLowerCase();
|
|
680
|
+
return trimmed === "y" || trimmed === "yes";
|
|
681
|
+
} finally {
|
|
682
|
+
rl.close();
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Scan every state file in the state bucket for references to the target
|
|
687
|
+
* region's asset bucket / container repo. A pragmatic string scan of the
|
|
688
|
+
* serialized state (design rationale in issue #1010): asset references
|
|
689
|
+
* appear in many property shapes (`Code.S3Bucket`, `ImageUri`, nested
|
|
690
|
+
* `TemplateURL`, `s3.Asset` env-var URLs, …) but all of them carry the
|
|
691
|
+
* bucket / repo NAME verbatim, so a substring match over the raw JSON body
|
|
692
|
+
* catches every shape — including ones future providers add — with zero
|
|
693
|
+
* per-shape maintenance. Covers both key layouts (region-prefixed and
|
|
694
|
+
* legacy), nested-stack children, and stacks deployed under a custom
|
|
695
|
+
* `--state-prefix` (the listing spans the whole bucket, not just the
|
|
696
|
+
* default `cdkd/` prefix).
|
|
697
|
+
*
|
|
698
|
+
* Returns a human-readable `stack (region)` descriptor per referencing
|
|
699
|
+
* state file.
|
|
700
|
+
*/
|
|
701
|
+
async function scanStateReferences(stateBackend, names) {
|
|
702
|
+
const stateKeys = await listAllStateKeys(stateBackend);
|
|
703
|
+
const referencing = [];
|
|
704
|
+
for (const key of stateKeys) {
|
|
705
|
+
const body = await stateBackend.getRawObject(key);
|
|
706
|
+
if (body === null) continue;
|
|
707
|
+
if (names.some((name) => body.includes(name))) referencing.push(describeStateKey(key));
|
|
708
|
+
}
|
|
709
|
+
return referencing;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* `{prefix}/{stack}/{region}/state.json` → `stack (region)`; legacy
|
|
713
|
+
* `{prefix}/{stack}/state.json` → `stack`. The prefix is arbitrary (custom
|
|
714
|
+
* `--state-prefix` values included), so the stack/region pair is derived
|
|
715
|
+
* from the key's TAIL: the last segment is treated as a region only when
|
|
716
|
+
* it is region-shaped.
|
|
717
|
+
*/
|
|
718
|
+
function describeStateKey(key) {
|
|
719
|
+
const segments = key.slice(0, -11).split("/");
|
|
720
|
+
const last = segments[segments.length - 1] ?? key;
|
|
721
|
+
const secondLast = segments[segments.length - 2];
|
|
722
|
+
if (secondLast !== void 0 && REGION_SEGMENT.test(last)) return `${secondLast} (${last})`;
|
|
723
|
+
return last;
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Empty (all versions + delete markers) and delete an S3 bucket, with
|
|
727
|
+
* `ExpectedBucketOwner` pinned on every call — deleting a foreign bucket
|
|
728
|
+
* that squatted the predictable name would be catastrophic, so the same
|
|
729
|
+
* ownership defense as the create side applies in reverse. Returns `false`
|
|
730
|
+
* (skip, info line) when the bucket does not exist — the idempotency
|
|
731
|
+
* mirror of `ensureAssetStorage`.
|
|
732
|
+
*/
|
|
733
|
+
async function emptyAndDeleteBucket(s3Client, bucket, accountId, label, logger) {
|
|
734
|
+
try {
|
|
735
|
+
await s3Client.send(new HeadBucketCommand({
|
|
736
|
+
Bucket: bucket,
|
|
737
|
+
ExpectedBucketOwner: accountId
|
|
738
|
+
}));
|
|
739
|
+
} catch (error) {
|
|
740
|
+
const err = error;
|
|
741
|
+
if (err.name === "NotFound" || err.name === "NoSuchBucket") {
|
|
742
|
+
logger.info(`${label} ${bucket} does not exist — skipping`);
|
|
743
|
+
return false;
|
|
744
|
+
}
|
|
745
|
+
if (err.$metadata?.httpStatusCode === 403) throw new CdkdError(`${label} '${bucket}' exists but is not owned by account ${accountId} (or access is denied). Refusing to delete it.`, "ASSET_STORAGE_FOREIGN_BUCKET", error);
|
|
746
|
+
throw normalizeAwsError(error, {
|
|
747
|
+
bucket,
|
|
748
|
+
operation: "HeadBucket"
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
logger.info(`Emptying ${label.toLowerCase()}: ${bucket}`);
|
|
752
|
+
let keyMarker;
|
|
753
|
+
let versionIdMarker;
|
|
754
|
+
do {
|
|
755
|
+
const page = await s3Client.send(new ListObjectVersionsCommand({
|
|
756
|
+
Bucket: bucket,
|
|
757
|
+
ExpectedBucketOwner: accountId,
|
|
758
|
+
...keyMarker && { KeyMarker: keyMarker },
|
|
759
|
+
...versionIdMarker && { VersionIdMarker: versionIdMarker }
|
|
760
|
+
}));
|
|
761
|
+
const entries = [...page.Versions ?? [], ...page.DeleteMarkers ?? []].filter((v) => v.Key).map((v) => ({
|
|
762
|
+
Key: v.Key,
|
|
763
|
+
...v.VersionId && { VersionId: v.VersionId }
|
|
764
|
+
}));
|
|
765
|
+
if (entries.length > 0) {
|
|
766
|
+
const failures = ((await s3Client.send(new DeleteObjectsCommand({
|
|
767
|
+
Bucket: bucket,
|
|
768
|
+
ExpectedBucketOwner: accountId,
|
|
769
|
+
Delete: {
|
|
770
|
+
Objects: entries,
|
|
771
|
+
Quiet: true
|
|
772
|
+
}
|
|
773
|
+
}))).Errors ?? []).map((e) => `${e.Key ?? "<unknown>"} (${e.Code ?? "Error"}: ${e.Message ?? ""})`);
|
|
774
|
+
if (failures.length > 0) throw new CdkdError(`Failed to delete ${failures.length} object(s) from ${label.toLowerCase()} '${bucket}': ${failures.join("; ")}`, "BUCKET_EMPTY_FAILED");
|
|
775
|
+
}
|
|
776
|
+
keyMarker = page.IsTruncated ? page.NextKeyMarker : void 0;
|
|
777
|
+
versionIdMarker = page.IsTruncated ? page.NextVersionIdMarker : void 0;
|
|
778
|
+
} while (keyMarker || versionIdMarker);
|
|
779
|
+
await s3Client.send(new DeleteBucketCommand({
|
|
780
|
+
Bucket: bucket,
|
|
781
|
+
ExpectedBucketOwner: accountId
|
|
782
|
+
}));
|
|
783
|
+
logger.info(`✓ Deleted ${label.toLowerCase()}: ${bucket}`);
|
|
784
|
+
return true;
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Force-delete the container-asset ECR repository. Missing repo is an
|
|
788
|
+
* idempotent skip with an info line.
|
|
789
|
+
*/
|
|
790
|
+
async function deleteContainerRepo(containerRepo, region, profile, logger) {
|
|
791
|
+
const ecrClient = new ECRClient({
|
|
792
|
+
region,
|
|
793
|
+
...profile && { profile }
|
|
794
|
+
});
|
|
795
|
+
try {
|
|
796
|
+
await ecrClient.send(new DeleteRepositoryCommand({
|
|
797
|
+
repositoryName: containerRepo,
|
|
798
|
+
force: true
|
|
799
|
+
}));
|
|
800
|
+
logger.info(`✓ Deleted container-asset ECR repository: ${containerRepo}`);
|
|
801
|
+
} catch (error) {
|
|
802
|
+
if (error.name === "RepositoryNotFoundException") logger.info(`Container-asset repository ${containerRepo} does not exist — skipping`);
|
|
803
|
+
else throw normalizeAwsError(error, { operation: "DeleteRepository" });
|
|
804
|
+
} finally {
|
|
805
|
+
ecrClient.destroy();
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* List OTHER regions that still have a bootstrap marker in the state
|
|
810
|
+
* bucket (i.e. are still opted in to cdkd asset storage).
|
|
811
|
+
*/
|
|
812
|
+
async function listOtherBootstrapRegions(stateBackend, region) {
|
|
813
|
+
return (await stateBackend.listRawKeys(BOOTSTRAP_MARKER_PREFIX)).filter((k) => k.endsWith(".json")).map((k) => k.slice(BOOTSTRAP_MARKER_PREFIX.length, -5)).filter((r) => r.length > 0 && r !== region);
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* `cdkd bootstrap --destroy` implementation. Dispatched from the bootstrap
|
|
817
|
+
* command action when `--destroy` is passed.
|
|
818
|
+
*/
|
|
819
|
+
async function bootstrapDestroyCommand(options) {
|
|
820
|
+
const logger = getLogger();
|
|
821
|
+
if (options.verbose) logger.setLevel("debug");
|
|
822
|
+
logger.info("Starting cdkd bootstrap --destroy...");
|
|
823
|
+
logger.debug("Options:", options);
|
|
824
|
+
await applyRoleArnIfSet({
|
|
825
|
+
roleArn: options.roleArn,
|
|
826
|
+
region: options.region
|
|
827
|
+
});
|
|
828
|
+
const awsClients = new AwsClients({
|
|
829
|
+
...options.region && { region: options.region },
|
|
830
|
+
...options.profile && { profile: options.profile }
|
|
831
|
+
});
|
|
832
|
+
setAwsClients(awsClients);
|
|
833
|
+
const region = options.region || process.env["AWS_REGION"] || "us-east-1";
|
|
834
|
+
const accountId = (await awsClients.sts.send(new GetCallerIdentityCommand({}))).Account;
|
|
835
|
+
const bucketName = options.stateBucket ?? getDefaultStateBucketName(accountId);
|
|
836
|
+
const markerS3Client = new S3Client({
|
|
837
|
+
region,
|
|
838
|
+
...options.profile && { profile: options.profile }
|
|
839
|
+
});
|
|
840
|
+
const stateBackend = new S3StateBackend(markerS3Client, {
|
|
841
|
+
bucket: bucketName,
|
|
842
|
+
prefix: STATE_PREFIX
|
|
843
|
+
}, {
|
|
844
|
+
region,
|
|
845
|
+
...options.profile && { profile: options.profile }
|
|
846
|
+
});
|
|
847
|
+
try {
|
|
848
|
+
const markerKey = getBootstrapMarkerKey(region);
|
|
849
|
+
let markerBody;
|
|
850
|
+
try {
|
|
851
|
+
markerBody = await stateBackend.getRawObject(markerKey);
|
|
852
|
+
} catch (error) {
|
|
853
|
+
if (error.name === "NoSuchBucket") {
|
|
854
|
+
logger.info(`State bucket '${bucketName}' does not exist — this account/region was never bootstrapped; nothing to delete.`);
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
throw error;
|
|
858
|
+
}
|
|
859
|
+
let marker;
|
|
860
|
+
if (markerBody === null) {
|
|
861
|
+
logger.info(`No bootstrap marker for region '${region}' (${markerKey}) — asset storage is not opted in for this region (or was already destroyed); nothing to delete.`);
|
|
862
|
+
if (!options.includeStateBucket) {
|
|
863
|
+
logger.info(`If the asset bucket / ECR repo still exist without a marker, re-run 'cdkd bootstrap --region ${region}' to recreate the marker, then destroy again.`);
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
} else marker = parseBootstrapMarker(markerBody, markerKey);
|
|
867
|
+
if (marker && !options.force) {
|
|
868
|
+
logger.info("Scanning stack state for references to the asset storage...");
|
|
869
|
+
const referencing = await scanStateReferences(stateBackend, [marker.assetBucket, marker.containerRepo]);
|
|
870
|
+
if (referencing.length > 0) throw new CdkdError(`Refusing to destroy asset storage for region '${region}': ${referencing.length} deployed stack(s) still reference '${marker.assetBucket}' / '${marker.containerRepo}':\n` + referencing.map((s) => ` - ${s}`).join("\n") + "\nDestroy (or re-deploy off the storage) these stacks first, or pass --force to delete anyway.", "ASSET_STORAGE_IN_USE");
|
|
871
|
+
}
|
|
872
|
+
if (options.includeStateBucket) {
|
|
873
|
+
const stateKeys = await listAllStateKeys(stateBackend);
|
|
874
|
+
if (stateKeys.length > 0) {
|
|
875
|
+
const listing = stateKeys.map((k) => ` - ${describeStateKey(k)} [${k}]`).join("\n");
|
|
876
|
+
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");
|
|
877
|
+
}
|
|
878
|
+
const otherRegions = await listOtherBootstrapRegions(stateBackend, region);
|
|
879
|
+
if (otherRegions.length > 0) throw new CdkdError(`Refusing to delete state bucket '${bucketName}': region(s) ${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");
|
|
880
|
+
}
|
|
881
|
+
const planLines = [];
|
|
882
|
+
if (marker) {
|
|
883
|
+
planLines.push(`Asset bucket: s3://${marker.assetBucket} (region ${region}, all contents)`);
|
|
884
|
+
planLines.push(`Container-asset ECR repository: ${marker.containerRepo} (region ${region}, all images)`);
|
|
885
|
+
planLines.push(`Bootstrap marker: s3://${bucketName}/${markerKey} (deleted last)`);
|
|
886
|
+
}
|
|
887
|
+
if (options.includeStateBucket) planLines.push(`State bucket: s3://${bucketName} (ALL contents, including all versions)`);
|
|
888
|
+
if (!await promptBootstrapDestroyConfirm({
|
|
889
|
+
planLines,
|
|
890
|
+
yes: options.yes
|
|
891
|
+
})) {
|
|
892
|
+
logger.info("Bootstrap teardown cancelled — nothing deleted.");
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
if (marker) {
|
|
896
|
+
await emptyAndDeleteBucket(awsClients.s3, marker.assetBucket, accountId, "Asset bucket", logger);
|
|
897
|
+
await deleteContainerRepo(marker.containerRepo, region, options.profile, logger);
|
|
898
|
+
await stateBackend.deleteRawObjects([markerKey]);
|
|
899
|
+
logger.info(`✓ Deleted bootstrap marker (${markerKey})`);
|
|
900
|
+
logger.info(`\ncdkd asset storage is now OFF for region ${region}: future deploys in this region fall back to legacy mode (CDK bootstrap destinations) unless the region is bootstrapped again.`);
|
|
901
|
+
}
|
|
902
|
+
if (options.includeStateBucket) {
|
|
903
|
+
const rebuiltStateBucketClient = await rebuildClientForBucketRegion(awsClients.s3, bucketName, { ...options.profile && { profile: options.profile } });
|
|
904
|
+
try {
|
|
905
|
+
await emptyAndDeleteBucket(rebuiltStateBucketClient ?? awsClients.s3, bucketName, accountId, "State bucket", logger);
|
|
906
|
+
} finally {
|
|
907
|
+
rebuiltStateBucketClient?.destroy();
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
logger.info("\n✓ Bootstrap teardown completed");
|
|
911
|
+
} finally {
|
|
912
|
+
markerS3Client.destroy();
|
|
913
|
+
awsClients.destroy();
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
611
917
|
//#endregion
|
|
612
918
|
//#region src/cli/commands/bootstrap.ts
|
|
613
919
|
/**
|
|
@@ -651,7 +957,10 @@ async function bootstrapCommand(options) {
|
|
|
651
957
|
try {
|
|
652
958
|
let bucketExists = false;
|
|
653
959
|
try {
|
|
654
|
-
await stateBucketS3.send(new HeadBucketCommand({
|
|
960
|
+
await stateBucketS3.send(new HeadBucketCommand({
|
|
961
|
+
Bucket: bucketName,
|
|
962
|
+
ExpectedBucketOwner: accountId
|
|
963
|
+
}));
|
|
655
964
|
bucketExists = true;
|
|
656
965
|
logger.info(`Bucket ${bucketName} already exists`);
|
|
657
966
|
} catch (error) {
|
|
@@ -682,12 +991,14 @@ async function bootstrapCommand(options) {
|
|
|
682
991
|
logger.debug("Enabling bucket versioning...");
|
|
683
992
|
await stateBucketS3.send(new PutBucketVersioningCommand({
|
|
684
993
|
Bucket: bucketName,
|
|
994
|
+
ExpectedBucketOwner: accountId,
|
|
685
995
|
VersioningConfiguration: { Status: "Enabled" }
|
|
686
996
|
}));
|
|
687
997
|
logger.info("✓ Enabled bucket versioning");
|
|
688
998
|
logger.debug("Enabling bucket encryption...");
|
|
689
999
|
await stateBucketS3.send(new PutBucketEncryptionCommand({
|
|
690
1000
|
Bucket: bucketName,
|
|
1001
|
+
ExpectedBucketOwner: accountId,
|
|
691
1002
|
ServerSideEncryptionConfiguration: { Rules: [{
|
|
692
1003
|
ApplyServerSideEncryptionByDefault: { SSEAlgorithm: "AES256" },
|
|
693
1004
|
BucketKeyEnabled: true
|
|
@@ -708,6 +1019,7 @@ async function bootstrapCommand(options) {
|
|
|
708
1019
|
};
|
|
709
1020
|
await stateBucketS3.send(new PutBucketPolicyCommand({
|
|
710
1021
|
Bucket: bucketName,
|
|
1022
|
+
ExpectedBucketOwner: accountId,
|
|
711
1023
|
Policy: JSON.stringify(bucketPolicy)
|
|
712
1024
|
}));
|
|
713
1025
|
logger.info("✓ Set bucket policy (deny external access)");
|
|
@@ -764,7 +1076,24 @@ async function bootstrapCommand(options) {
|
|
|
764
1076
|
* Create bootstrap command
|
|
765
1077
|
*/
|
|
766
1078
|
function createBootstrapCommand() {
|
|
767
|
-
const cmd = new Command("bootstrap").description("Bootstrap cdkd by creating the S3 state bucket plus cdkd-owned asset storage (asset bucket + container-asset ECR repo) for the region").option("--state-bucket <bucket>", "Name of S3 bucket to create for state storage (default: cdkd-state-{accountId})").option("--force", "
|
|
1079
|
+
const cmd = new Command("bootstrap").description("Bootstrap cdkd by creating the S3 state bucket plus cdkd-owned asset storage (asset bucket + container-asset ECR repo) for the region. With --destroy, tear down the region's asset storage instead (issue #1010).").option("--state-bucket <bucket>", "Name of S3 bucket to create for state storage (default: cdkd-state-{accountId})").option("--force", "Create: force reconfiguration of an existing bucket. Destroy: delete the asset storage even when deployed stacks still reference it", false).option("--no-assets", "Skip cdkd asset storage (asset bucket / ECR repo / marker) — deploys keep publishing assets to the CDK bootstrap bucket/repo").option("--destroy", "Tear down the region's cdkd asset storage (empty + delete the asset bucket, force-delete the container-asset ECR repo, then delete the bootstrap marker last). The state bucket is kept unless --include-state-bucket is also passed", false).option("--include-state-bucket", "With --destroy: also delete the S3 state bucket. Refused while any stack state exists or any other region is still opted in to asset storage", false).addOption(new Option("--region <region>", "AWS region in which to create the state bucket (defaults to AWS_REGION env or us-east-1)")).action(withErrorHandling(async (options) => {
|
|
1080
|
+
if (options.destroy) {
|
|
1081
|
+
if (!options.assets) throw new CdkdError("--no-assets cannot be combined with --destroy. The teardown scope is the asset storage itself; use --include-state-bucket to also delete the state bucket.", "INVALID_OPTIONS");
|
|
1082
|
+
await bootstrapDestroyCommand({
|
|
1083
|
+
...options.stateBucket && { stateBucket: options.stateBucket },
|
|
1084
|
+
...options.region && { region: options.region },
|
|
1085
|
+
...options.profile && { profile: options.profile },
|
|
1086
|
+
...options.roleArn && { roleArn: options.roleArn },
|
|
1087
|
+
force: options.force,
|
|
1088
|
+
includeStateBucket: options.includeStateBucket,
|
|
1089
|
+
yes: options.yes,
|
|
1090
|
+
verbose: options.verbose
|
|
1091
|
+
});
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
if (options.includeStateBucket) throw new CdkdError("--include-state-bucket requires --destroy (it opts the state bucket into the teardown).", "INVALID_OPTIONS");
|
|
1095
|
+
await bootstrapCommand(options);
|
|
1096
|
+
}));
|
|
768
1097
|
commonOptions.forEach((opt) => cmd.addOption(opt));
|
|
769
1098
|
return cmd;
|
|
770
1099
|
}
|
|
@@ -1526,7 +1855,7 @@ const FLUSH_INTERVAL_MS = 2e3;
|
|
|
1526
1855
|
const FLUSH_EVENT_THRESHOLD = 50;
|
|
1527
1856
|
/** Build-time cdkd version, with a dev fallback for non-built contexts. */
|
|
1528
1857
|
function getCdkdVersion() {
|
|
1529
|
-
return "0.
|
|
1858
|
+
return "0.236.0";
|
|
1530
1859
|
}
|
|
1531
1860
|
/**
|
|
1532
1861
|
* Generate a time-sortable unique run id, e.g.
|
|
@@ -2157,6 +2486,14 @@ var ExportIndexStore = class {
|
|
|
2157
2486
|
* `config.region()` shape (e.g. a hand-rolled test double) the resolution
|
|
2158
2487
|
* is skipped and the original client is used unchanged.
|
|
2159
2488
|
*/
|
|
2489
|
+
/**
|
|
2490
|
+
* `ExpectedBucketOwner` spread for every state-bucket S3 call (squatting
|
|
2491
|
+
* hardening — see src/utils/expected-bucket-owner.ts). Best-effort:
|
|
2492
|
+
* resolves to an empty object for non-standard clients (test doubles).
|
|
2493
|
+
*/
|
|
2494
|
+
async ownerParam() {
|
|
2495
|
+
return expectedOwnerParam(this.s3Client);
|
|
2496
|
+
}
|
|
2160
2497
|
async ensureClientForBucket() {
|
|
2161
2498
|
if (this.clientResolved) return;
|
|
2162
2499
|
if (this.resolveInFlight) return this.resolveInFlight;
|
|
@@ -2302,6 +2639,7 @@ var ExportIndexStore = class {
|
|
|
2302
2639
|
try {
|
|
2303
2640
|
const response = await this.s3Client.send(new GetObjectCommand({
|
|
2304
2641
|
Bucket: this.bucket,
|
|
2642
|
+
...await this.ownerParam(),
|
|
2305
2643
|
Key: this.indexKey()
|
|
2306
2644
|
}));
|
|
2307
2645
|
return {
|
|
@@ -2318,6 +2656,7 @@ var ExportIndexStore = class {
|
|
|
2318
2656
|
const body = JSON.stringify(next, null, 2);
|
|
2319
2657
|
return (await this.s3Client.send(new PutObjectCommand({
|
|
2320
2658
|
Bucket: this.bucket,
|
|
2659
|
+
...await this.ownerParam(),
|
|
2321
2660
|
Key: this.indexKey(),
|
|
2322
2661
|
Body: body,
|
|
2323
2662
|
ContentLength: Buffer.byteLength(body),
|
|
@@ -41938,6 +42277,8 @@ async function stateMigrateCommand(options) {
|
|
|
41938
42277
|
await newS3.send(new CopyObjectCommand({
|
|
41939
42278
|
Bucket: newBucket,
|
|
41940
42279
|
Key: obj.Key,
|
|
42280
|
+
...await expectedOwnerParam(newS3),
|
|
42281
|
+
ExpectedSourceBucketOwner: accountId,
|
|
41941
42282
|
CopySource: encodeURIComponent(`${legacyBucket}/${obj.Key}`)
|
|
41942
42283
|
}));
|
|
41943
42284
|
copied++;
|
|
@@ -41951,7 +42292,10 @@ async function stateMigrateCommand(options) {
|
|
|
41951
42292
|
logger.info(`Emptying source bucket ${legacyBucket} (all versions + delete markers)...`);
|
|
41952
42293
|
await emptyBucketAllVersions(legacyS3, legacyBucket);
|
|
41953
42294
|
logger.info(`Deleting source bucket ${legacyBucket}...`);
|
|
41954
|
-
await legacyS3.send(new DeleteBucketCommand({
|
|
42295
|
+
await legacyS3.send(new DeleteBucketCommand({
|
|
42296
|
+
Bucket: legacyBucket,
|
|
42297
|
+
...await expectedOwnerParam(legacyS3)
|
|
42298
|
+
}));
|
|
41955
42299
|
logger.info(`✓ Deleted source bucket: ${legacyBucket}`);
|
|
41956
42300
|
} else logger.info(`Source bucket ${legacyBucket} kept. Pass --remove-legacy on a future run to delete it.`);
|
|
41957
42301
|
logger.info(`✓ Migration complete: ${legacyBucket} -> ${newBucket}`);
|
|
@@ -41967,7 +42311,10 @@ async function stateMigrateCommand(options) {
|
|
|
41967
42311
|
}
|
|
41968
42312
|
async function bucketExists(s3, bucketName) {
|
|
41969
42313
|
try {
|
|
41970
|
-
await s3.send(new HeadBucketCommand({
|
|
42314
|
+
await s3.send(new HeadBucketCommand({
|
|
42315
|
+
Bucket: bucketName,
|
|
42316
|
+
...await expectedOwnerParam(s3)
|
|
42317
|
+
}));
|
|
41971
42318
|
return true;
|
|
41972
42319
|
} catch (error) {
|
|
41973
42320
|
const err = error;
|
|
@@ -41983,6 +42330,7 @@ async function listAllObjects(s3, bucket) {
|
|
|
41983
42330
|
do {
|
|
41984
42331
|
const resp = await s3.send(new ListObjectsV2Command({
|
|
41985
42332
|
Bucket: bucket,
|
|
42333
|
+
...await expectedOwnerParam(s3),
|
|
41986
42334
|
...continuationToken && { ContinuationToken: continuationToken }
|
|
41987
42335
|
}));
|
|
41988
42336
|
if (resp.Contents) all.push(...resp.Contents);
|
|
@@ -42025,10 +42373,12 @@ async function ensureDestinationBucket(bucketName, region, accountId, logger) {
|
|
|
42025
42373
|
logger.info(`✓ Created destination bucket: ${bucketName}`);
|
|
42026
42374
|
await s3.send(new PutBucketVersioningCommand({
|
|
42027
42375
|
Bucket: bucketName,
|
|
42376
|
+
...await expectedOwnerParam(s3),
|
|
42028
42377
|
VersioningConfiguration: { Status: "Enabled" }
|
|
42029
42378
|
}));
|
|
42030
42379
|
await s3.send(new PutBucketEncryptionCommand({
|
|
42031
42380
|
Bucket: bucketName,
|
|
42381
|
+
...await expectedOwnerParam(s3),
|
|
42032
42382
|
ServerSideEncryptionConfiguration: { Rules: [{
|
|
42033
42383
|
ApplyServerSideEncryptionByDefault: { SSEAlgorithm: "AES256" },
|
|
42034
42384
|
BucketKeyEnabled: true
|
|
@@ -42036,6 +42386,7 @@ async function ensureDestinationBucket(bucketName, region, accountId, logger) {
|
|
|
42036
42386
|
}));
|
|
42037
42387
|
await s3.send(new PutBucketPolicyCommand({
|
|
42038
42388
|
Bucket: bucketName,
|
|
42389
|
+
...await expectedOwnerParam(s3),
|
|
42039
42390
|
Policy: JSON.stringify({
|
|
42040
42391
|
Version: "2012-10-17",
|
|
42041
42392
|
Statement: [{
|
|
@@ -42062,6 +42413,7 @@ async function emptyBucketAllVersions(s3, bucket) {
|
|
|
42062
42413
|
do {
|
|
42063
42414
|
const resp = await s3.send(new ListObjectVersionsCommand({
|
|
42064
42415
|
Bucket: bucket,
|
|
42416
|
+
...await expectedOwnerParam(s3),
|
|
42065
42417
|
...keyMarker && { KeyMarker: keyMarker },
|
|
42066
42418
|
...versionIdMarker && { VersionIdMarker: versionIdMarker }
|
|
42067
42419
|
}));
|
|
@@ -42078,6 +42430,7 @@ async function emptyBucketAllVersions(s3, bucket) {
|
|
|
42078
42430
|
const batch = ids.slice(i, i + 1e3);
|
|
42079
42431
|
await s3.send(new DeleteObjectsCommand({
|
|
42080
42432
|
Bucket: bucket,
|
|
42433
|
+
...await expectedOwnerParam(s3),
|
|
42081
42434
|
Delete: {
|
|
42082
42435
|
Objects: batch,
|
|
42083
42436
|
Quiet: true
|
|
@@ -45314,7 +45667,10 @@ function formatBucketSource(source) {
|
|
|
45314
45667
|
*/
|
|
45315
45668
|
async function detectBucketRegion(awsClients, bucket) {
|
|
45316
45669
|
try {
|
|
45317
|
-
const constraint = (await awsClients.s3.send(new GetBucketLocationCommand({
|
|
45670
|
+
const constraint = (await awsClients.s3.send(new GetBucketLocationCommand({
|
|
45671
|
+
Bucket: bucket,
|
|
45672
|
+
...await expectedOwnerParam(awsClients.s3)
|
|
45673
|
+
}))).LocationConstraint;
|
|
45318
45674
|
if (!constraint) return "us-east-1";
|
|
45319
45675
|
if (constraint === "EU") return "eu-west-1";
|
|
45320
45676
|
return constraint;
|
|
@@ -45362,7 +45718,8 @@ async function readSchemaVersion(awsClients, bucket, keys) {
|
|
|
45362
45718
|
try {
|
|
45363
45719
|
const resp = await awsClients.s3.send(new GetObjectCommand({
|
|
45364
45720
|
Bucket: bucket,
|
|
45365
|
-
Key: keys[0]
|
|
45721
|
+
Key: keys[0],
|
|
45722
|
+
...await expectedOwnerParam(awsClients.s3)
|
|
45366
45723
|
}));
|
|
45367
45724
|
if (!resp.Body) return "unknown";
|
|
45368
45725
|
const body = await resp.Body.transformToString();
|
|
@@ -45397,7 +45754,8 @@ async function listAssetStorageMarkers(awsClients, bucket) {
|
|
|
45397
45754
|
try {
|
|
45398
45755
|
const marker = parseBootstrapMarker(await (await awsClients.s3.send(new GetObjectCommand({
|
|
45399
45756
|
Bucket: bucket,
|
|
45400
|
-
Key: key
|
|
45757
|
+
Key: key,
|
|
45758
|
+
...await expectedOwnerParam(awsClients.s3)
|
|
45401
45759
|
}))).Body?.transformToString() ?? "", key);
|
|
45402
45760
|
entries.push({
|
|
45403
45761
|
region,
|
|
@@ -56046,7 +56404,7 @@ function reorderArgs(argv) {
|
|
|
56046
56404
|
async function main() {
|
|
56047
56405
|
installPipeCloseHandler();
|
|
56048
56406
|
const program = new Command();
|
|
56049
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
56407
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.236.0");
|
|
56050
56408
|
program.addCommand(createBootstrapCommand());
|
|
56051
56409
|
program.addCommand(createSynthCommand());
|
|
56052
56410
|
program.addCommand(createListCommand());
|