|
17 | 17 |
|
18 | 18 | import { runSetup } from '../commands/setup'; |
19 | 19 | import { runGenerate } from '../commands/generate'; |
| 20 | +import { runValidate } from '../commands/validate'; |
20 | 21 | import { runInit, isInteractive } from '../commands/init'; |
21 | | -import { runSnapshotCreate, runSnapshotList, runSnapshotRestore } from '../commands/snapshot'; |
| 22 | +import { runSnapshotCreate, runSnapshotList, runSnapshotRestore, runSnapshotDelete } from '../commands/snapshot'; |
22 | 23 | import { runShareExport, runShareImport } from '../commands/share'; |
23 | 24 | import { runPathSetup } from '../commands/path-setup'; |
24 | 25 | import { findProjectRoot } from '../core/config/loader'; |
@@ -80,6 +81,19 @@ program |
80 | 81 | } |
81 | 82 | }); |
82 | 83 |
|
| 84 | +program |
| 85 | + .command('validate') |
| 86 | + .description('Validate .dev-env.yml (and .env) without running setup or generate') |
| 87 | + .option('--strict', 'Fail on warnings or unresolved ${VAR} references') |
| 88 | + .action(async (options) => { |
| 89 | + try { |
| 90 | + await runValidate({ strict: options.strict }); |
| 91 | + } catch (err: unknown) { |
| 92 | + logger.error((err as Error).message ?? 'Validation failed'); |
| 93 | + process.exit(1); |
| 94 | + } |
| 95 | + }); |
| 96 | + |
83 | 97 | const snapshotCommand = program |
84 | 98 | .command('snapshot') |
85 | 99 | .description('Manage environment snapshots'); |
@@ -111,24 +125,39 @@ snapshotCommand |
111 | 125 |
|
112 | 126 | snapshotCommand |
113 | 127 | .command('restore') |
114 | | - .description('Restore .dev-env.yml from a snapshot') |
| 128 | + .description('Restore .dev-env.yml from a snapshot (backs up current config first)') |
115 | 129 | .argument('<name>', 'Snapshot name') |
116 | | - .action(async (name) => { |
| 130 | + .option('-y, --yes', 'Skip the overwrite confirmation prompt') |
| 131 | + .action(async (name, options) => { |
117 | 132 | try { |
118 | | - await runSnapshotRestore(name); |
| 133 | + await runSnapshotRestore(name, { yes: options.yes }); |
119 | 134 | } catch (err: unknown) { |
120 | 135 | logger.error((err as Error).message ?? 'Snapshot restore failed'); |
121 | 136 | process.exit(1); |
122 | 137 | } |
123 | 138 | }); |
124 | 139 |
|
| 140 | +snapshotCommand |
| 141 | + .command('delete') |
| 142 | + .description('Delete a snapshot') |
| 143 | + .argument('<name>', 'Snapshot name') |
| 144 | + .action(async (name) => { |
| 145 | + try { |
| 146 | + await runSnapshotDelete(name); |
| 147 | + } catch (err: unknown) { |
| 148 | + logger.error((err as Error).message ?? 'Snapshot delete failed'); |
| 149 | + process.exit(1); |
| 150 | + } |
| 151 | + }); |
| 152 | + |
125 | 153 | program |
126 | 154 | .command('generate') |
127 | 155 | .description('Generate docker-compose.yml from configuration') |
128 | 156 | .option('-o, --output <file>', 'Output file path', 'docker-compose.yml') |
| 157 | + .option('--dry-run', 'Print the generated compose to stdout without writing a file') |
129 | 158 | .action(async (options) => { |
130 | 159 | try { |
131 | | - await runGenerate({ output: options.output }); |
| 160 | + await runGenerate({ output: options.output, dryRun: options.dryRun }); |
132 | 161 | } catch (err: any) { |
133 | 162 | logger.error(err.message ?? 'Generate failed'); |
134 | 163 | process.exit(1); |
@@ -157,9 +186,10 @@ shareCommand |
157 | 186 | .description('Import shared configuration into project') |
158 | 187 | .argument('<file>', 'Configuration file to import') |
159 | 188 | .option('-o, --output <file>', 'Output file path', '.dev-env.yml') |
| 189 | + .option('--validate-only', 'Validate the file without writing it') |
160 | 190 | .action(async (file, options) => { |
161 | 191 | try { |
162 | | - await runShareImport(file, { output: options.output }); |
| 192 | + await runShareImport(file, { output: options.output, validateOnly: options.validateOnly }); |
163 | 193 | } catch (err: unknown) { |
164 | 194 | logger.error((err as Error).message ?? 'Share import failed'); |
165 | 195 | process.exit(1); |
|
0 commit comments