cdk-agc 1.1.0 → 1.2.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
@@ -7,13 +7,12 @@
7
7
  `cdk-agc` is a fast CLI tool that scans your AWS CDK cloud assembly directory and helps you reclaim disk space:
8
8
 
9
9
  - **Clean `cdk.out` directories**: Remove unused assets while protecting referenced files
10
- - **Clean temporary directories**: Delete accumulated temporary CDK directories in `$TMPDIR` (can save GBs of disk space)
10
+ - Only deletes unreferenced `asset.*` directories - all other files are automatically protected
11
+ - Protects recently modified files (configurable with `-k/--keep-hours`)
11
12
 
12
- Safe cleanup with intelligent protection:
13
-
14
- - Assets referenced in `manifest.json` or `*.assets.json`
15
- - Recently modified files (configurable retention period)
16
- - Essential metadata files (`manifest.json`, `tree.json`, `*.template.json`, etc.)
13
+ - **Clean temporary directories** (`-t/--cleanup-tmp`): Delete accumulated temporary CDK directories in `$TMPDIR`
14
+ - Deletes entire directories
15
+ - Only time-based protection with `-k/--keep-hours`
17
16
 
18
17
  This helps optimize storage and streamline CI/CD caching.
19
18
 
@@ -25,13 +24,12 @@ This helps optimize storage and streamline CI/CD caching.
25
24
  - Temporary directories in `$TMPDIR` accumulate
26
25
  - Disk space exhaustion from accumulated build artifacts
27
26
  - Slow CI/CD pipelines due to large cache sizes
28
- - No official local cleanup tool (AWS CDK's `cdk gc` only handles cloud-side resources)
29
27
 
30
28
  ### Solution
31
29
 
32
30
  `cdk-agc` provides safe, intelligent cleanup with:
33
31
 
34
- - **Manifest-based protection**: Only removes assets not referenced by the current configuration
32
+ - **Reference-based protection**: Only removes assets not referenced in `*.assets.json` files
35
33
  - **Time-based protection**: Keeps recent assets for quick rollbacks
36
34
 
37
35
  ## Installation
@@ -53,12 +51,9 @@ npm install -g cdk-agc
53
51
  ### Basic Examples
54
52
 
55
53
  ```bash
56
- # Default: Clean cdk.out, keeping only active manifest assets
54
+ # Default: Clean cdk.out, keeping only referenced assets
57
55
  npx cdk-agc
58
56
 
59
- # Clean temporary directories in $TMPDIR (can save GBs)
60
- npx cdk-agc -t
61
-
62
57
  # Dry-run: Preview what would be deleted
63
58
  npx cdk-agc -d
64
59
 
@@ -67,6 +62,9 @@ npx cdk-agc -o ./packages/infra/cdk.out
67
62
 
68
63
  # Keep assets modified within the last 24 hours
69
64
  npx cdk-agc -k 24
65
+
66
+ # Clean temporary directories in $TMPDIR
67
+ npx cdk-agc -t
70
68
  ```
71
69
 
72
70
  ### Options
@@ -107,7 +105,7 @@ npx cdk-agc -k 24
107
105
 
108
106
  `asset.*` files/directories are **protected** from deletion if they meet **any** of these criteria:
109
107
 
110
- 1. **Active References**: Referenced in `manifest.json` or `*.assets.json` files
108
+ 1. **Active References**: Referenced in `*.assets.json` files
111
109
  2. **Recent Modifications**: Modified within the last N hours (when using `-k/--keep-hours`)
112
110
 
113
111
  ## Use Cases
@@ -147,9 +145,9 @@ npx cdk-agc -o ./apps/web/cdk.out
147
145
 
148
146
  ### Clean Temporary Directories
149
147
 
150
- CDK creates temporary directories in `$TMPDIR` during synthesis (directories starting with `cdk.out`, `cdk-`, or `.cdk`), which can accumulate over time (especially after failed synths or interruptions). Use `-t/--cleanup-tmp` to reclaim disk space.
148
+ CDK creates temporary directories in `$TMPDIR` during synthesis (directories starting with `cdk.out`, `cdk-`, or `.cdk`), which can accumulate over time. Use `-t/--cleanup-tmp` to reclaim disk space.
151
149
 
152
- **Note**: This option deletes entire directories, not individual assets. Time-based protection with `-k/--keep-hours` is the only protection applied.
150
+ **Note**: This option deletes entire directories. Time-based protection with `-k/--keep-hours` is the only protection applied.
153
151
 
154
152
  ```bash
155
153
  # Clean all temporary CDK directories (dry-run first)
@@ -1 +1 @@
1
- {"version":3,"file":"cleanup.d.mts","names":[],"sources":["../src/cleanup.ts"],"mappings":";UAIiB,cAAA;EACf,MAAA;EACA,MAAA;EACA,SAAA;AAAA;AAAA,UAGe,kBAAA;EACf,MAAA;EACA,SAAA;AAAA;;AAFF;;iBA6LsB,aAAA,CAAc,OAAA,EAAS,cAAA,GAAiB,OAAA;;;AAA9D;iBAgHsB,sBAAA,CAAuB,OAAA,EAAS,kBAAA,GAAqB,OAAA"}
1
+ {"version":3,"file":"cleanup.d.mts","names":[],"sources":["../src/cleanup.ts"],"mappings":";UAIiB,cAAA;EACf,MAAA;EACA,MAAA;EACA,SAAA;AAAA;AAAA,UAGe,kBAAA;EACf,MAAA;EACA,SAAA;AAAA;;AAFF;;iBA4IsB,aAAA,CAAc,OAAA,EAAS,cAAA,GAAiB,OAAA;;;AAA9D;iBAgHsB,sBAAA,CAAuB,OAAA,EAAS,kBAAA,GAAqB,OAAA"}
package/dist/cleanup.mjs CHANGED
@@ -4,42 +4,32 @@ import os from "os";
4
4
 
5
5
  //#region src/cleanup.ts
6
6
  /**
7
- * Recursively collect all paths referenced in the manifest
7
+ * Recursively collect asset paths from *.assets.json files
8
8
  */
9
- function collectActivePaths(obj, basePath, collected) {
10
- if (typeof obj === "string") {
11
- const fullPath = path.join(basePath, obj);
12
- collected.add(fullPath);
13
- let parent = path.dirname(fullPath);
14
- while (parent !== basePath && parent !== ".") {
15
- collected.add(parent);
16
- parent = path.dirname(parent);
17
- }
18
- } else if (Array.isArray(obj)) obj.forEach((item) => collectActivePaths(item, basePath, collected));
19
- else if (obj && typeof obj === "object") Object.values(obj).forEach((value) => collectActivePaths(value, basePath, collected));
20
- }
21
- /**
22
- * Parse *.assets.json files and collect asset paths (recursively)
23
- */
24
- async function collectAssetPaths(outdir, activePaths) {
25
- const items = await promises.readdir(outdir, { withFileTypes: true });
9
+ async function collectAssetPaths(dirPath) {
10
+ const activePaths = /* @__PURE__ */ new Set();
11
+ const items = await promises.readdir(dirPath, { withFileTypes: true });
26
12
  for (const item of items) {
27
- const itemPath = path.join(outdir, item.name);
28
- if (item.isDirectory()) await collectAssetPaths(itemPath, activePaths);
29
- else if (item.name.endsWith(".assets.json")) try {
13
+ const itemPath = path.join(dirPath, item.name);
14
+ if (item.isDirectory()) {
15
+ (await collectAssetPaths(itemPath)).forEach((p) => activePaths.add(p));
16
+ continue;
17
+ }
18
+ if (!item.name.endsWith(".assets.json")) continue;
19
+ try {
30
20
  const content = await promises.readFile(itemPath, "utf-8");
31
21
  const assets = JSON.parse(content);
32
22
  if (assets.files) for (const fileEntry of Object.values(assets.files)) {
33
23
  const entry = fileEntry;
34
24
  if (entry.source?.path) {
35
- const assetPath = path.join(outdir, entry.source.path);
25
+ const assetPath = path.join(path.dirname(itemPath), entry.source.path);
36
26
  activePaths.add(assetPath);
37
27
  }
38
28
  }
39
29
  if (assets.dockerImages) for (const imageEntry of Object.values(assets.dockerImages)) {
40
30
  const entry = imageEntry;
41
31
  if (entry.source?.directory) {
42
- const assetPath = path.join(outdir, entry.source.directory);
32
+ const assetPath = path.join(path.dirname(itemPath), entry.source.directory);
43
33
  activePaths.add(assetPath);
44
34
  }
45
35
  }
@@ -47,20 +37,6 @@ async function collectAssetPaths(outdir, activePaths) {
47
37
  console.warn(`Warning: Failed to parse ${item.name}:`, error);
48
38
  }
49
39
  }
50
- }
51
- /**
52
- * Parse manifest file and get active paths
53
- */
54
- async function getActivePathsFromManifest(outdir) {
55
- const manifestPath = path.join(outdir, "manifest.json");
56
- const activePaths = /* @__PURE__ */ new Set();
57
- try {
58
- const content = await promises.readFile(manifestPath, "utf-8");
59
- collectActivePaths(JSON.parse(content), outdir, activePaths);
60
- await collectAssetPaths(outdir, activePaths);
61
- } catch (error) {
62
- throw new Error(`Failed to read manifest.json: ${error instanceof Error ? error.message : String(error)}`);
63
- }
64
40
  return activePaths;
65
41
  }
66
42
  /**
@@ -120,13 +96,13 @@ function formatSize(bytes) {
120
96
  async function cleanupAssets(options) {
121
97
  const { outdir, dryRun, keepHours } = options;
122
98
  console.log(`Scanning ${outdir}...`);
123
- console.log(`Protection policy: Active manifest + files modified within ${keepHours} hours\n`);
99
+ console.log(`Protection policy: Referenced assets + files modified within ${keepHours} hours\n`);
124
100
  try {
125
101
  await promises.access(outdir);
126
102
  } catch {
127
103
  throw new Error(`Directory not found: ${outdir}`);
128
104
  }
129
- const activePaths = await getActivePathsFromManifest(outdir);
105
+ const activePaths = await collectAssetPaths(outdir);
130
106
  const entries = await promises.readdir(outdir);
131
107
  const itemsToDelete = [];
132
108
  await Promise.all(entries.map(async (entry) => {
@@ -199,18 +175,14 @@ async function cleanupTempDirectories(options) {
199
175
  return;
200
176
  }
201
177
  let totalCleaned = 0;
202
- let totalProtected = 0;
203
178
  let totalSize = 0;
204
179
  for (const dir of directories) try {
205
- if (await shouldProtectDirectory(dir, keepHours)) {
206
- totalProtected++;
207
- continue;
208
- }
180
+ if (await shouldProtectDirectory(dir, keepHours)) continue;
209
181
  const size = await calculateSize(dir);
210
182
  totalSize += size;
211
183
  if (!dryRun) await remove(dir);
212
184
  totalCleaned++;
213
- } catch (error) {
185
+ } catch {
214
186
  continue;
215
187
  }
216
188
  if (totalCleaned === 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"cleanup.mjs","names":["fs"],"sources":["../src/cleanup.ts"],"sourcesContent":["import { promises as fs } from \"fs\";\nimport path from \"path\";\nimport os from \"os\";\n\nexport interface CleanupOptions {\n outdir: string;\n dryRun: boolean;\n keepHours: number;\n}\n\nexport interface TempCleanupOptions {\n dryRun: boolean;\n keepHours: number;\n}\n\ninterface ManifestArtifact {\n type: string;\n properties?: {\n file?: string;\n templateFile?: string;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n}\n\ninterface Manifest {\n version: string;\n artifacts?: Record<string, ManifestArtifact>;\n}\n\n/**\n * Recursively collect all paths referenced in the manifest\n */\nfunction collectActivePaths(obj: unknown, basePath: string, collected: Set<string>): void {\n if (typeof obj === \"string\") {\n const fullPath = path.join(basePath, obj);\n collected.add(fullPath);\n // Also protect parent directories\n let parent = path.dirname(fullPath);\n while (parent !== basePath && parent !== \".\") {\n collected.add(parent);\n parent = path.dirname(parent);\n }\n } else if (Array.isArray(obj)) {\n obj.forEach((item) => collectActivePaths(item, basePath, collected));\n } else if (obj && typeof obj === \"object\") {\n Object.values(obj).forEach((value) => collectActivePaths(value, basePath, collected));\n }\n}\n\n/**\n * Parse *.assets.json files and collect asset paths (recursively)\n */\nasync function collectAssetPaths(outdir: string, activePaths: Set<string>): Promise<void> {\n const items = await fs.readdir(outdir, { withFileTypes: true });\n\n for (const item of items) {\n const itemPath = path.join(outdir, item.name);\n\n if (item.isDirectory()) {\n // Recursively scan subdirectories (e.g., assembly-MyStage/)\n await collectAssetPaths(itemPath, activePaths);\n } else if (item.name.endsWith(\".assets.json\")) {\n // Parse assets.json file\n try {\n const content = await fs.readFile(itemPath, \"utf-8\");\n const assets = JSON.parse(content);\n\n // Collect asset paths from files object\n if (assets.files) {\n for (const fileEntry of Object.values(assets.files)) {\n const entry = fileEntry as { source?: { path?: string } };\n if (entry.source?.path) {\n const assetPath = path.join(outdir, entry.source.path);\n activePaths.add(assetPath);\n }\n }\n }\n\n // Collect asset paths from dockerImages object\n if (assets.dockerImages) {\n for (const imageEntry of Object.values(assets.dockerImages)) {\n const entry = imageEntry as { source?: { directory?: string } };\n if (entry.source?.directory) {\n const assetPath = path.join(outdir, entry.source.directory);\n activePaths.add(assetPath);\n }\n }\n }\n } catch (error) {\n // Skip malformed asset files\n console.warn(`Warning: Failed to parse ${item.name}:`, error);\n }\n }\n }\n}\n\n/**\n * Parse manifest file and get active paths\n */\nasync function getActivePathsFromManifest(outdir: string): Promise<Set<string>> {\n const manifestPath = path.join(outdir, \"manifest.json\");\n const activePaths = new Set<string>();\n\n try {\n const content = await fs.readFile(manifestPath, \"utf-8\");\n const manifest: Manifest = JSON.parse(content);\n\n collectActivePaths(manifest, outdir, activePaths);\n\n // Also collect paths from *.assets.json files\n await collectAssetPaths(outdir, activePaths);\n } catch (error) {\n throw new Error(\n `Failed to read manifest.json: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n return activePaths;\n}\n\n/**\n * Check if file/directory should be protected from deletion\n */\nasync function isProtected(\n itemPath: string,\n activePaths: Set<string>,\n keepHours: number,\n): Promise<boolean> {\n // Protect paths referenced in manifest\n if (activePaths.has(itemPath)) {\n return true;\n }\n\n // Protect files/directories within retention period\n if (keepHours > 0) {\n const stats = await fs.stat(itemPath);\n const ageHours = (Date.now() - stats.mtimeMs) / (1000 * 60 * 60);\n if (ageHours <= keepHours) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Calculate size of file or directory\n */\nasync function calculateSize(itemPath: string): Promise<number> {\n const stats = await fs.stat(itemPath);\n\n if (stats.isFile()) {\n return stats.size;\n }\n\n if (stats.isDirectory()) {\n const entries = await fs.readdir(itemPath);\n const sizes = await Promise.all(\n entries.map((entry) => calculateSize(path.join(itemPath, entry))),\n );\n return sizes.reduce((sum, size) => sum + size, 0);\n }\n\n return 0;\n}\n\n/**\n * Recursively remove file or directory\n */\nasync function remove(itemPath: string): Promise<void> {\n const stats = await fs.stat(itemPath);\n\n if (stats.isDirectory()) {\n await fs.rm(itemPath, { recursive: true, force: true });\n } else {\n await fs.unlink(itemPath);\n }\n}\n\n/**\n * Format bytes to human-readable string\n */\nfunction formatSize(bytes: number): string {\n const units = [\"B\", \"KB\", \"MB\", \"GB\"];\n let size = bytes;\n let unitIndex = 0;\n\n while (size >= 1024 && unitIndex < units.length - 1) {\n size /= 1024;\n unitIndex++;\n }\n\n return `${size.toFixed(2)} ${units[unitIndex]}`;\n}\n\n/**\n * Clean up cdk.out directory\n */\nexport async function cleanupAssets(options: CleanupOptions): Promise<void> {\n const { outdir, dryRun, keepHours } = options;\n\n console.log(`Scanning ${outdir}...`);\n console.log(`Protection policy: Active manifest + files modified within ${keepHours} hours\\n`);\n\n // Check directory exists\n try {\n await fs.access(outdir);\n } catch {\n throw new Error(`Directory not found: ${outdir}`);\n }\n\n // Collect paths referenced in manifest\n const activePaths = await getActivePathsFromManifest(outdir);\n\n // Scan directory items\n const entries = await fs.readdir(outdir);\n const itemsToDelete: Array<{ path: string; size: number }> = [];\n\n await Promise.all(\n entries.map(async (entry) => {\n const itemPath = path.join(outdir, entry);\n\n // Only consider asset.* files/directories for deletion\n if (!entry.startsWith(\"asset.\")) {\n return;\n }\n\n if (await isProtected(itemPath, activePaths, keepHours)) {\n return;\n }\n\n const size = await calculateSize(itemPath);\n itemsToDelete.push({ path: itemPath, size });\n }),\n );\n\n // Display results\n if (itemsToDelete.length === 0) {\n console.log(`✓ No unused assets found.`);\n return;\n }\n\n const totalSize = itemsToDelete.reduce((sum, item) => sum + item.size, 0);\n\n console.log(`Found ${itemsToDelete.length} unused item(s):\\n`);\n itemsToDelete.forEach((item) => {\n const relativePath = path.relative(outdir, item.path);\n console.log(` - ${relativePath} (${formatSize(item.size)})`);\n });\n\n console.log(`\\nTotal size to reclaim: ${formatSize(totalSize)}\\n`);\n\n if (dryRun) {\n console.log(\"Dry-run mode: No files were deleted.\");\n } else {\n // Delete in parallel\n await Promise.all(itemsToDelete.map((item) => remove(item.path)));\n console.log(\"✓ Cleanup completed successfully.\");\n }\n}\n\n/**\n * Find all cdk.out temporary directories in $TMPDIR\n */\nasync function findTempDirectories(): Promise<string[]> {\n const tmpdir = os.tmpdir();\n const directories: string[] = [];\n\n try {\n const items = await fs.readdir(tmpdir, { withFileTypes: true });\n\n for (const item of items) {\n // Match directories starting with \"cdk.out\", \"cdk-\", or \".cdk\"\n if (\n item.isDirectory() &&\n (item.name.startsWith(\"cdk.out\") ||\n item.name.startsWith(\"cdk-\") ||\n item.name.startsWith(\".cdk\"))\n ) {\n const dirPath = path.join(tmpdir, item.name);\n directories.push(dirPath);\n }\n }\n } catch (error) {\n console.warn(`Warning: Failed to scan $TMPDIR (${tmpdir}):`, error);\n }\n\n return directories;\n}\n\n/**\n * Check if directory should be protected based on age\n */\nasync function shouldProtectDirectory(dirPath: string, keepHours: number): Promise<boolean> {\n if (keepHours <= 0) {\n return false;\n }\n\n try {\n const stats = await fs.stat(dirPath);\n const ageHours = (Date.now() - stats.mtimeMs) / (1000 * 60 * 60);\n return ageHours <= keepHours;\n } catch {\n return false;\n }\n}\n\n/**\n * Clean up all temporary CDK output directories\n */\nexport async function cleanupTempDirectories(options: TempCleanupOptions): Promise<void> {\n const { dryRun, keepHours } = options;\n const tmpdir = os.tmpdir();\n\n console.log(`Scanning ${tmpdir}...`);\n console.log(`Protection policy: Time-based (directories modified within ${keepHours} hours)\\n`);\n\n const directories = await findTempDirectories();\n\n if (directories.length === 0) {\n console.log(\"✓ No temporary CDK directories found.\");\n return;\n }\n\n let totalCleaned = 0;\n let totalProtected = 0;\n let totalSize = 0;\n\n for (const dir of directories) {\n try {\n // Check if directory should be protected by age\n if (await shouldProtectDirectory(dir, keepHours)) {\n totalProtected++;\n continue;\n }\n\n // Calculate size before deletion\n const size = await calculateSize(dir);\n totalSize += size;\n\n if (!dryRun) {\n await remove(dir);\n }\n\n totalCleaned++;\n } catch (error) {\n // Silently continue on error\n continue;\n }\n }\n\n if (totalCleaned === 0) {\n console.log(\"✓ No temporary CDK directories to clean.\");\n return;\n }\n\n console.log(`Found ${totalCleaned} temporary CDK directory(ies)\\n`);\n console.log(`Total size to reclaim: ${formatSize(totalSize)}\\n`);\n\n if (dryRun) {\n console.log(\"Dry-run mode: No files were deleted.\");\n } else {\n console.log(\"✓ Cleanup completed successfully.\");\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cleanup.mjs","names":["fs"],"sources":["../src/cleanup.ts"],"sourcesContent":["import { promises as fs } from \"fs\";\nimport path from \"path\";\nimport os from \"os\";\n\nexport interface CleanupOptions {\n outdir: string;\n dryRun: boolean;\n keepHours: number;\n}\n\nexport interface TempCleanupOptions {\n dryRun: boolean;\n keepHours: number;\n}\n\n/**\n * Recursively collect asset paths from *.assets.json files\n */\nasync function collectAssetPaths(dirPath: string): Promise<Set<string>> {\n const activePaths = new Set<string>();\n const items = await fs.readdir(dirPath, { withFileTypes: true });\n\n for (const item of items) {\n const itemPath = path.join(dirPath, item.name);\n\n // Recursively scan subdirectories (e.g., assembly-MyStage/)\n if (item.isDirectory()) {\n const subPaths = await collectAssetPaths(itemPath);\n subPaths.forEach((p) => activePaths.add(p));\n continue;\n }\n\n // Only process *.assets.json files\n if (!item.name.endsWith(\".assets.json\")) {\n continue;\n }\n\n // Parse assets.json file\n try {\n const content = await fs.readFile(itemPath, \"utf-8\");\n const assets = JSON.parse(content);\n\n // Collect asset paths from files object\n if (assets.files) {\n for (const fileEntry of Object.values(assets.files)) {\n const entry = fileEntry as { source?: { path?: string } };\n if (entry.source?.path) {\n const assetPath = path.join(path.dirname(itemPath), entry.source.path);\n activePaths.add(assetPath);\n }\n }\n }\n\n // Collect asset paths from dockerImages object\n if (assets.dockerImages) {\n for (const imageEntry of Object.values(assets.dockerImages)) {\n const entry = imageEntry as { source?: { directory?: string } };\n if (entry.source?.directory) {\n const assetPath = path.join(path.dirname(itemPath), entry.source.directory);\n activePaths.add(assetPath);\n }\n }\n }\n } catch (error) {\n // Skip malformed asset files\n console.warn(`Warning: Failed to parse ${item.name}:`, error);\n }\n }\n\n return activePaths;\n}\n\n/**\n * Check if file/directory should be protected from deletion\n */\nasync function isProtected(\n itemPath: string,\n activePaths: Set<string>,\n keepHours: number,\n): Promise<boolean> {\n // Protect assets referenced in *.assets.json files\n if (activePaths.has(itemPath)) {\n return true;\n }\n\n // Protect files/directories within retention period\n if (keepHours > 0) {\n const stats = await fs.stat(itemPath);\n const ageHours = (Date.now() - stats.mtimeMs) / (1000 * 60 * 60);\n if (ageHours <= keepHours) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Calculate size of file or directory\n */\nasync function calculateSize(itemPath: string): Promise<number> {\n const stats = await fs.stat(itemPath);\n\n if (stats.isFile()) {\n return stats.size;\n }\n\n if (stats.isDirectory()) {\n const entries = await fs.readdir(itemPath);\n const sizes = await Promise.all(\n entries.map((entry) => calculateSize(path.join(itemPath, entry))),\n );\n return sizes.reduce((sum, size) => sum + size, 0);\n }\n\n return 0;\n}\n\n/**\n * Recursively remove file or directory\n */\nasync function remove(itemPath: string): Promise<void> {\n const stats = await fs.stat(itemPath);\n\n if (stats.isDirectory()) {\n await fs.rm(itemPath, { recursive: true, force: true });\n } else {\n await fs.unlink(itemPath);\n }\n}\n\n/**\n * Format bytes to human-readable string\n */\nfunction formatSize(bytes: number): string {\n const units = [\"B\", \"KB\", \"MB\", \"GB\"];\n let size = bytes;\n let unitIndex = 0;\n\n while (size >= 1024 && unitIndex < units.length - 1) {\n size /= 1024;\n unitIndex++;\n }\n\n return `${size.toFixed(2)} ${units[unitIndex]}`;\n}\n\n/**\n * Clean up cdk.out directory\n */\nexport async function cleanupAssets(options: CleanupOptions): Promise<void> {\n const { outdir, dryRun, keepHours } = options;\n\n console.log(`Scanning ${outdir}...`);\n console.log(`Protection policy: Referenced assets + files modified within ${keepHours} hours\\n`);\n\n // Check directory exists\n try {\n await fs.access(outdir);\n } catch {\n throw new Error(`Directory not found: ${outdir}`);\n }\n\n // Collect asset paths referenced in *.assets.json files\n const activePaths = await collectAssetPaths(outdir);\n\n // Scan directory items\n const entries = await fs.readdir(outdir);\n const itemsToDelete: Array<{ path: string; size: number }> = [];\n\n await Promise.all(\n entries.map(async (entry) => {\n const itemPath = path.join(outdir, entry);\n\n // Only consider asset.* files/directories for deletion\n if (!entry.startsWith(\"asset.\")) {\n return;\n }\n\n if (await isProtected(itemPath, activePaths, keepHours)) {\n return;\n }\n\n const size = await calculateSize(itemPath);\n itemsToDelete.push({ path: itemPath, size });\n }),\n );\n\n // Display results\n if (itemsToDelete.length === 0) {\n console.log(`✓ No unused assets found.`);\n return;\n }\n\n const totalSize = itemsToDelete.reduce((sum, item) => sum + item.size, 0);\n\n console.log(`Found ${itemsToDelete.length} unused item(s):\\n`);\n itemsToDelete.forEach((item) => {\n const relativePath = path.relative(outdir, item.path);\n console.log(` - ${relativePath} (${formatSize(item.size)})`);\n });\n\n console.log(`\\nTotal size to reclaim: ${formatSize(totalSize)}\\n`);\n\n if (dryRun) {\n console.log(\"Dry-run mode: No files were deleted.\");\n } else {\n // Delete in parallel\n await Promise.all(itemsToDelete.map((item) => remove(item.path)));\n console.log(\"✓ Cleanup completed successfully.\");\n }\n}\n\n/**\n * Find all cdk.out temporary directories in $TMPDIR\n */\nasync function findTempDirectories(): Promise<string[]> {\n const tmpdir = os.tmpdir();\n const directories: string[] = [];\n\n try {\n const items = await fs.readdir(tmpdir, { withFileTypes: true });\n\n for (const item of items) {\n // Match directories starting with \"cdk.out\", \"cdk-\", or \".cdk\"\n if (\n item.isDirectory() &&\n (item.name.startsWith(\"cdk.out\") ||\n item.name.startsWith(\"cdk-\") ||\n item.name.startsWith(\".cdk\"))\n ) {\n const dirPath = path.join(tmpdir, item.name);\n directories.push(dirPath);\n }\n }\n } catch (error) {\n console.warn(`Warning: Failed to scan $TMPDIR (${tmpdir}):`, error);\n }\n\n return directories;\n}\n\n/**\n * Check if directory should be protected based on age\n */\nasync function shouldProtectDirectory(dirPath: string, keepHours: number): Promise<boolean> {\n if (keepHours <= 0) {\n return false;\n }\n\n try {\n const stats = await fs.stat(dirPath);\n const ageHours = (Date.now() - stats.mtimeMs) / (1000 * 60 * 60);\n return ageHours <= keepHours;\n } catch {\n return false;\n }\n}\n\n/**\n * Clean up all temporary CDK output directories\n */\nexport async function cleanupTempDirectories(options: TempCleanupOptions): Promise<void> {\n const { dryRun, keepHours } = options;\n const tmpdir = os.tmpdir();\n\n console.log(`Scanning ${tmpdir}...`);\n console.log(`Protection policy: Time-based (directories modified within ${keepHours} hours)\\n`);\n\n const directories = await findTempDirectories();\n\n if (directories.length === 0) {\n console.log(\"✓ No temporary CDK directories found.\");\n return;\n }\n\n let totalCleaned = 0;\n let totalSize = 0;\n\n for (const dir of directories) {\n try {\n // Check if directory should be protected by age\n if (await shouldProtectDirectory(dir, keepHours)) {\n continue;\n }\n\n // Calculate size before deletion\n const size = await calculateSize(dir);\n totalSize += size;\n\n if (!dryRun) {\n await remove(dir);\n }\n\n totalCleaned++;\n } catch {\n // Silently continue on error\n continue;\n }\n }\n\n if (totalCleaned === 0) {\n console.log(\"✓ No temporary CDK directories to clean.\");\n return;\n }\n\n console.log(`Found ${totalCleaned} temporary CDK directory(ies)\\n`);\n console.log(`Total size to reclaim: ${formatSize(totalSize)}\\n`);\n\n if (dryRun) {\n console.log(\"Dry-run mode: No files were deleted.\");\n } else {\n console.log(\"✓ Cleanup completed successfully.\");\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdk-agc",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "CDK Assembly Garbage Collector - Clean up unused assets in cdk.out",
5
5
  "type": "module",
6
6
  "bin": {