@go-to-k/cdkd 0.12.0 → 0.13.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 CHANGED
@@ -428,6 +428,13 @@ cdkd destroy --all --force
428
428
  # Force-unlock a stale lock from interrupted deploy
429
429
  cdkd force-unlock MyStack
430
430
 
431
+ # Inspect state-bucket info on demand (bucket name, region, source, schema version, stack count).
432
+ # Routine commands (deploy / destroy / etc.) no longer print the bucket banner by default —
433
+ # pass --verbose to surface it in their debug logs, or use this subcommand for an explicit answer.
434
+ cdkd state info
435
+ cdkd state info --json # JSON output for tooling
436
+ cdkd state info --state-bucket my-bucket # explicit bucket; reports Source: --state-bucket flag
437
+
431
438
  # List stacks registered in the cdkd state bucket
432
439
  cdkd state list
433
440
  cdkd state ls --long # include resource count, last-modified, lock status
package/dist/cli.js CHANGED
@@ -970,16 +970,18 @@ function resolveApp(cliApp) {
970
970
  const cdkJson = loadCdkJson();
971
971
  return cdkJson?.app ?? void 0;
972
972
  }
973
- function resolveStateBucket(cliBucket) {
973
+ function resolveStateBucketWithSource(cliBucket) {
974
974
  if (cliBucket)
975
- return cliBucket;
975
+ return { bucket: cliBucket, source: "cli-flag" };
976
976
  const envBucket = process.env["CDKD_STATE_BUCKET"];
977
977
  if (envBucket)
978
- return envBucket;
978
+ return { bucket: envBucket, source: "env" };
979
979
  const cdkJson = loadCdkJson();
980
980
  const cdkdContext = cdkJson?.context?.["cdkd"];
981
981
  const bucket = cdkdContext?.["stateBucket"];
982
- return typeof bucket === "string" ? bucket : void 0;
982
+ if (typeof bucket === "string")
983
+ return { bucket, source: "cdk.json" };
984
+ return void 0;
983
985
  }
984
986
  function getDefaultStateBucketName(accountId) {
985
987
  return `cdkd-state-${accountId}`;
@@ -988,7 +990,10 @@ function getLegacyStateBucketName(accountId, region) {
988
990
  return `cdkd-state-${accountId}-${region}`;
989
991
  }
990
992
  async function resolveStateBucketWithDefault(cliBucket, region) {
991
- const syncResult = resolveStateBucket(cliBucket);
993
+ return (await resolveStateBucketWithDefaultAndSource(cliBucket, region)).bucket;
994
+ }
995
+ async function resolveStateBucketWithDefaultAndSource(cliBucket, region) {
996
+ const syncResult = resolveStateBucketWithSource(cliBucket);
992
997
  if (syncResult)
993
998
  return syncResult;
994
999
  const logger = getLogger();
@@ -1004,14 +1009,14 @@ async function resolveStateBucketWithDefault(cliBucket, region) {
1004
1009
  const probe = new S3Client10({ region: "us-east-1" });
1005
1010
  try {
1006
1011
  if (await bucketExists(probe, newName)) {
1007
- logger.info(`State bucket: ${newName}`);
1008
- return newName;
1012
+ logger.debug(`State bucket: ${newName}`);
1013
+ return { bucket: newName, source: "default" };
1009
1014
  }
1010
1015
  if (await bucketExists(probe, legacyName)) {
1011
1016
  logger.warn(
1012
1017
  `Using legacy state bucket name '${legacyName}'. The default has changed to '${newName}'. Future cdkd versions will drop legacy support; consider migrating with cdkd state migrate-bucket (coming in a future release).`
1013
1018
  );
1014
- return legacyName;
1019
+ return { bucket: legacyName, source: "default-legacy" };
1015
1020
  }
1016
1021
  throw new Error(
1017
1022
  `No cdkd state bucket found for account ${accountId}. Looked for '${newName}' (current default) and '${legacyName}' (legacy default). Run 'cdkd bootstrap' to create '${newName}'.`
@@ -29548,6 +29553,11 @@ function createForceUnlockCommand() {
29548
29553
  // src/cli/commands/state.ts
29549
29554
  import * as readline2 from "node:readline/promises";
29550
29555
  import { Command as Command9, Option as Option5 } from "commander";
29556
+ import {
29557
+ GetBucketLocationCommand as GetBucketLocationCommand2,
29558
+ GetObjectCommand as GetObjectCommand4,
29559
+ ListObjectsV2Command as ListObjectsV2Command3
29560
+ } from "@aws-sdk/client-s3";
29551
29561
  init_aws_clients();
29552
29562
  function formatStackRef(ref) {
29553
29563
  return ref.region ? `${ref.stackName} (${ref.region})` : ref.stackName;
@@ -30106,8 +30116,127 @@ function createStateDestroyCommand() {
30106
30116
  cmd.addOption(deprecatedRegionOption);
30107
30117
  return cmd;
30108
30118
  }
30119
+ function formatBucketSource(source) {
30120
+ switch (source) {
30121
+ case "cli-flag":
30122
+ return "--state-bucket flag";
30123
+ case "env":
30124
+ return "CDKD_STATE_BUCKET env";
30125
+ case "cdk.json":
30126
+ return "cdk.json (context.cdkd.stateBucket)";
30127
+ case "default":
30128
+ return "default (account ID from STS)";
30129
+ case "default-legacy":
30130
+ return "default (legacy region-suffixed name; cdkd state migrate-bucket recommended)";
30131
+ }
30132
+ }
30133
+ async function detectBucketRegion(awsClients, bucket) {
30134
+ try {
30135
+ const resp = await awsClients.s3.send(new GetBucketLocationCommand2({ Bucket: bucket }));
30136
+ const constraint = resp.LocationConstraint;
30137
+ if (!constraint)
30138
+ return "us-east-1";
30139
+ if (constraint === "EU")
30140
+ return "eu-west-1";
30141
+ return constraint;
30142
+ } catch {
30143
+ return void 0;
30144
+ }
30145
+ }
30146
+ async function listStateFileKeys(awsClients, bucket, prefix) {
30147
+ const keys = [];
30148
+ let continuationToken;
30149
+ const searchPrefix = `${prefix}/`;
30150
+ do {
30151
+ const resp = await awsClients.s3.send(
30152
+ new ListObjectsV2Command3({
30153
+ Bucket: bucket,
30154
+ Prefix: searchPrefix,
30155
+ ...continuationToken && { ContinuationToken: continuationToken }
30156
+ })
30157
+ );
30158
+ for (const obj of resp.Contents ?? []) {
30159
+ const key = obj.Key;
30160
+ if (typeof key === "string" && key.endsWith("/state.json")) {
30161
+ keys.push(key);
30162
+ }
30163
+ }
30164
+ continuationToken = resp.NextContinuationToken;
30165
+ } while (continuationToken);
30166
+ return keys;
30167
+ }
30168
+ async function readSchemaVersion(awsClients, bucket, keys) {
30169
+ if (keys.length === 0)
30170
+ return "unknown";
30171
+ try {
30172
+ const resp = await awsClients.s3.send(new GetObjectCommand4({ Bucket: bucket, Key: keys[0] }));
30173
+ if (!resp.Body)
30174
+ return "unknown";
30175
+ const body = await resp.Body.transformToString();
30176
+ const parsed = JSON.parse(body);
30177
+ return typeof parsed.version === "number" ? parsed.version : "unknown";
30178
+ } catch {
30179
+ return "unknown";
30180
+ }
30181
+ }
30182
+ async function stateInfoCommand(options) {
30183
+ const logger = getLogger();
30184
+ if (options.verbose)
30185
+ logger.setLevel("debug");
30186
+ const awsClients = new AwsClients({
30187
+ ...options.region && { region: options.region },
30188
+ ...options.profile && { profile: options.profile }
30189
+ });
30190
+ setAwsClients(awsClients);
30191
+ try {
30192
+ const region = options.region || process.env["AWS_REGION"] || "us-east-1";
30193
+ const resolved = await resolveStateBucketWithDefaultAndSource(options.stateBucket, region);
30194
+ const bucket = resolved.bucket;
30195
+ const prefix = options.statePrefix;
30196
+ const stateBackend = new S3StateBackend(awsClients.s3, { bucket, prefix });
30197
+ await stateBackend.verifyBucketExists();
30198
+ const detectedRegion = await detectBucketRegion(awsClients, bucket);
30199
+ const stateFileKeys = await listStateFileKeys(awsClients, bucket, prefix);
30200
+ const schemaVersion = await readSchemaVersion(awsClients, bucket, stateFileKeys);
30201
+ if (options.json) {
30202
+ const json = {
30203
+ bucket,
30204
+ region: detectedRegion ?? null,
30205
+ regionSource: detectedRegion ? "auto-detected" : "unknown",
30206
+ bucketSource: resolved.source,
30207
+ schemaVersion,
30208
+ stackCount: stateFileKeys.length
30209
+ };
30210
+ process.stdout.write(`${JSON.stringify(json, null, 2)}
30211
+ `);
30212
+ return;
30213
+ }
30214
+ const lines = [];
30215
+ lines.push(`State bucket: ${bucket}`);
30216
+ if (detectedRegion) {
30217
+ lines.push(`Region: ${detectedRegion} (auto-detected via GetBucketLocation)`);
30218
+ } else {
30219
+ lines.push("Region: unknown (GetBucketLocation failed or denied)");
30220
+ }
30221
+ lines.push(`Source: ${formatBucketSource(resolved.source)}`);
30222
+ lines.push(`Schema version: ${schemaVersion}`);
30223
+ lines.push(`Stacks: ${stateFileKeys.length}`);
30224
+ process.stdout.write(`${lines.join("\n")}
30225
+ `);
30226
+ } finally {
30227
+ awsClients.destroy();
30228
+ }
30229
+ }
30230
+ function createStateInfoCommand() {
30231
+ const cmd = new Command9("info").description(
30232
+ "Show cdkd state bucket info (bucket name, region, source, schema version, stack count)"
30233
+ ).option("--json", "Output as JSON", false).action(withErrorHandling(stateInfoCommand));
30234
+ [...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
30235
+ return cmd;
30236
+ }
30109
30237
  function createStateCommand() {
30110
30238
  const cmd = new Command9("state").description("Manage cdkd state stored in S3");
30239
+ cmd.addCommand(createStateInfoCommand());
30111
30240
  cmd.addCommand(createStateListCommand());
30112
30241
  cmd.addCommand(createStateResourcesCommand());
30113
30242
  cmd.addCommand(createStateShowCommand());
@@ -30141,7 +30270,7 @@ function reorderArgs(argv) {
30141
30270
  }
30142
30271
  async function main() {
30143
30272
  const program = new Command10();
30144
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.12.0");
30273
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.13.0");
30145
30274
  program.addCommand(createBootstrapCommand());
30146
30275
  program.addCommand(createSynthCommand());
30147
30276
  program.addCommand(createListCommand());