genbox 1.0.12 ā 1.0.14
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/commands/config.js +273 -0
- package/dist/commands/destroy.js +170 -6
- package/dist/commands/migrate.js +246 -0
- package/dist/commands/resolve.js +243 -0
- package/dist/commands/scan.js +354 -0
- package/dist/commands/validate.js +529 -0
- package/dist/config-explainer.js +379 -0
- package/dist/detected-config.js +145 -0
- package/dist/index.js +12 -1
- package/dist/migration.js +334 -0
- package/dist/profile-resolver.js +8 -3
- package/dist/schema-v3.js +36 -0
- package/dist/schema-v4.js +54 -0
- package/dist/strict-mode.js +288 -0
- package/package.json +1 -1
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Migrate Command
|
|
4
|
+
*
|
|
5
|
+
* Migrates genbox.yaml from v3 to v4 format:
|
|
6
|
+
* - Shows what changes will be made
|
|
7
|
+
* - Creates backup of original
|
|
8
|
+
* - Applies migration
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* genbox migrate # Interactive migration
|
|
12
|
+
* genbox migrate --dry-run # Show changes without applying
|
|
13
|
+
* genbox migrate --yes # Skip confirmation
|
|
14
|
+
*/
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
48
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
49
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
50
|
+
};
|
|
51
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
|
+
exports.deprecationsCommand = exports.migrateCommand = void 0;
|
|
53
|
+
const commander_1 = require("commander");
|
|
54
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
55
|
+
const fs = __importStar(require("fs"));
|
|
56
|
+
const yaml = __importStar(require("js-yaml"));
|
|
57
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
58
|
+
const config_loader_1 = require("../config-loader");
|
|
59
|
+
const migration_1 = require("../migration");
|
|
60
|
+
exports.migrateCommand = new commander_1.Command('migrate')
|
|
61
|
+
.description('Migrate genbox.yaml from v3 to v4 format')
|
|
62
|
+
.option('--dry-run', 'Show changes without applying')
|
|
63
|
+
.option('-y, --yes', 'Skip confirmation prompts')
|
|
64
|
+
.option('--no-backup', 'Skip creating backup file')
|
|
65
|
+
.option('--json', 'Output migration plan as JSON')
|
|
66
|
+
.action(async (options) => {
|
|
67
|
+
const cwd = process.cwd();
|
|
68
|
+
console.log(chalk_1.default.cyan('\nš Genbox Configuration Migration\n'));
|
|
69
|
+
try {
|
|
70
|
+
// Load current configuration
|
|
71
|
+
const configLoader = new config_loader_1.ConfigLoader();
|
|
72
|
+
const loadResult = await configLoader.load(cwd);
|
|
73
|
+
if (!loadResult.found || !loadResult.config) {
|
|
74
|
+
console.log(chalk_1.default.red('No genbox.yaml found. Nothing to migrate.'));
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
const config = loadResult.config;
|
|
78
|
+
// Check if migration is needed
|
|
79
|
+
if (!(0, migration_1.needsMigration)(config)) {
|
|
80
|
+
console.log(chalk_1.default.green('ā Configuration is already at v4. No migration needed.'));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Show deprecation warnings
|
|
84
|
+
const deprecations = (0, migration_1.checkDeprecations)(config);
|
|
85
|
+
if (deprecations.length > 0) {
|
|
86
|
+
console.log(chalk_1.default.yellow.bold(`Found ${deprecations.length} deprecated patterns:\n`));
|
|
87
|
+
for (const dep of deprecations) {
|
|
88
|
+
console.log(chalk_1.default.yellow(` ⢠${dep.path}: ${dep.message}`));
|
|
89
|
+
console.log(chalk_1.default.dim(` ā ${dep.suggestion}`));
|
|
90
|
+
}
|
|
91
|
+
console.log();
|
|
92
|
+
}
|
|
93
|
+
// Get migration summary
|
|
94
|
+
const summary = (0, migration_1.getMigrationSummary)(config);
|
|
95
|
+
console.log(chalk_1.default.bold('Migration Summary:\n'));
|
|
96
|
+
console.log(` Fields to migrate: ${summary.fieldsToMigrate}`);
|
|
97
|
+
console.log(` Deprecated patterns: ${summary.deprecatedPatterns}`);
|
|
98
|
+
if (summary.estimatedChanges.length > 0) {
|
|
99
|
+
console.log(chalk_1.default.bold('\n Changes to be made:'));
|
|
100
|
+
for (const change of summary.estimatedChanges) {
|
|
101
|
+
console.log(` ⢠${change}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Perform migration
|
|
105
|
+
const result = (0, migration_1.migrateV3ToV4)(config);
|
|
106
|
+
if (options.json) {
|
|
107
|
+
console.log(JSON.stringify({
|
|
108
|
+
summary,
|
|
109
|
+
changes: result.changes,
|
|
110
|
+
warnings: result.warnings,
|
|
111
|
+
config: result.config,
|
|
112
|
+
}, null, 2));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// Show detailed changes
|
|
116
|
+
console.log(chalk_1.default.bold('\nš Detailed Changes:\n'));
|
|
117
|
+
for (const change of result.changes) {
|
|
118
|
+
const icon = change.type === 'add' ? '+' :
|
|
119
|
+
change.type === 'remove' ? '-' :
|
|
120
|
+
change.type === 'rename' ? 'ā' : '~';
|
|
121
|
+
const color = change.type === 'add' ? chalk_1.default.green :
|
|
122
|
+
change.type === 'remove' ? chalk_1.default.red :
|
|
123
|
+
chalk_1.default.yellow;
|
|
124
|
+
console.log(color(` ${icon} ${change.path}: ${change.description}`));
|
|
125
|
+
}
|
|
126
|
+
// Show warnings
|
|
127
|
+
if (result.warnings.length > 0) {
|
|
128
|
+
console.log(chalk_1.default.yellow.bold('\nā ļø Warnings:\n'));
|
|
129
|
+
for (const warning of result.warnings) {
|
|
130
|
+
console.log(chalk_1.default.yellow(` ⢠${warning}`));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Dry run stops here
|
|
134
|
+
if (options.dryRun) {
|
|
135
|
+
console.log(chalk_1.default.bold('\nš Migrated Configuration (preview):\n'));
|
|
136
|
+
console.log(chalk_1.default.dim('---'));
|
|
137
|
+
console.log(yaml.dump(result.config, { lineWidth: 120, noRefs: true }));
|
|
138
|
+
console.log(chalk_1.default.dim('---'));
|
|
139
|
+
console.log(chalk_1.default.cyan('\nThis was a dry run. No changes were made.'));
|
|
140
|
+
console.log('Run ' + chalk_1.default.bold('genbox migrate') + ' (without --dry-run) to apply changes.');
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
// Confirm migration
|
|
144
|
+
if (!options.yes) {
|
|
145
|
+
console.log();
|
|
146
|
+
const confirmed = await (0, prompts_1.confirm)({
|
|
147
|
+
message: 'Apply these changes to genbox.yaml?',
|
|
148
|
+
default: true,
|
|
149
|
+
});
|
|
150
|
+
if (!confirmed) {
|
|
151
|
+
console.log(chalk_1.default.yellow('Migration cancelled.'));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Find the config file path
|
|
156
|
+
const projectSource = loadResult.sources.find(s => s.type === 'project');
|
|
157
|
+
if (!projectSource) {
|
|
158
|
+
console.log(chalk_1.default.red('Could not find project config file path.'));
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
const configPath = projectSource.path;
|
|
162
|
+
// Create backup
|
|
163
|
+
if (options.backup !== false) {
|
|
164
|
+
const backupPath = configPath.replace('.yaml', '.v3.yaml.bak');
|
|
165
|
+
fs.copyFileSync(configPath, backupPath);
|
|
166
|
+
console.log(chalk_1.default.dim(`\nBackup created: ${backupPath}`));
|
|
167
|
+
}
|
|
168
|
+
// Write migrated config
|
|
169
|
+
const migratedContent = yaml.dump(result.config, {
|
|
170
|
+
lineWidth: 120,
|
|
171
|
+
noRefs: true,
|
|
172
|
+
quotingType: '"',
|
|
173
|
+
forceQuotes: false,
|
|
174
|
+
});
|
|
175
|
+
// Add header comment
|
|
176
|
+
const header = `# Genbox Configuration v4
|
|
177
|
+
# Migrated from v3 on ${new Date().toISOString()}
|
|
178
|
+
# See: https://genbox.dev/docs/config-v4
|
|
179
|
+
|
|
180
|
+
`;
|
|
181
|
+
fs.writeFileSync(configPath, header + migratedContent);
|
|
182
|
+
console.log(chalk_1.default.green.bold('\nā Migration complete!'));
|
|
183
|
+
console.log(`\n Updated: ${configPath}`);
|
|
184
|
+
// Next steps
|
|
185
|
+
console.log(chalk_1.default.bold('\nš Next steps:\n'));
|
|
186
|
+
console.log(' 1. Review the migrated configuration');
|
|
187
|
+
console.log(' 2. Run ' + chalk_1.default.cyan('genbox validate') + ' to check for issues');
|
|
188
|
+
console.log(' 3. Run ' + chalk_1.default.cyan('genbox scan') + ' to update detected.yaml');
|
|
189
|
+
console.log(' 4. Run ' + chalk_1.default.cyan('genbox resolve') + ' to verify resolution');
|
|
190
|
+
console.log();
|
|
191
|
+
// Show v4 benefits
|
|
192
|
+
console.log(chalk_1.default.bold('š v4 Benefits:\n'));
|
|
193
|
+
console.log(' ⢠Explicit connections with ' + chalk_1.default.cyan('connects_to'));
|
|
194
|
+
console.log(' ⢠Clear infrastructure with ' + chalk_1.default.cyan('provides'));
|
|
195
|
+
console.log(' ⢠Opt-in detection with ' + chalk_1.default.cyan('$detect') + ' markers');
|
|
196
|
+
console.log(' ⢠Strict mode for predictable behavior');
|
|
197
|
+
console.log();
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
console.error(chalk_1.default.red('Migration failed:'), error);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
/**
|
|
205
|
+
* Show deprecation warnings subcommand
|
|
206
|
+
*/
|
|
207
|
+
exports.deprecationsCommand = new commander_1.Command('deprecations')
|
|
208
|
+
.description('Check for deprecated patterns in genbox.yaml')
|
|
209
|
+
.option('--json', 'Output as JSON')
|
|
210
|
+
.action(async (options) => {
|
|
211
|
+
const cwd = process.cwd();
|
|
212
|
+
try {
|
|
213
|
+
const configLoader = new config_loader_1.ConfigLoader();
|
|
214
|
+
const loadResult = await configLoader.load(cwd);
|
|
215
|
+
if (!loadResult.found || !loadResult.config) {
|
|
216
|
+
console.log(chalk_1.default.red('No genbox.yaml found.'));
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
const deprecations = (0, migration_1.checkDeprecations)(loadResult.config);
|
|
220
|
+
if (options.json) {
|
|
221
|
+
console.log(JSON.stringify(deprecations, null, 2));
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (deprecations.length === 0) {
|
|
225
|
+
console.log(chalk_1.default.green('ā No deprecated patterns found.'));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
console.log(chalk_1.default.yellow.bold(`\nFound ${deprecations.length} deprecated pattern(s):\n`));
|
|
229
|
+
for (const dep of deprecations) {
|
|
230
|
+
const icon = dep.severity === 'error' ? 'ā' : 'ā ';
|
|
231
|
+
const color = dep.severity === 'error' ? chalk_1.default.red : chalk_1.default.yellow;
|
|
232
|
+
console.log(color(`${icon} ${dep.path}`));
|
|
233
|
+
console.log(` ${dep.message}`);
|
|
234
|
+
console.log(chalk_1.default.dim(` ā ${dep.suggestion}`));
|
|
235
|
+
if (dep.autoMigrate) {
|
|
236
|
+
console.log(chalk_1.default.dim(' (can be auto-migrated)'));
|
|
237
|
+
}
|
|
238
|
+
console.log();
|
|
239
|
+
}
|
|
240
|
+
console.log(chalk_1.default.bold('Run ' + chalk_1.default.cyan('genbox migrate') + ' to fix these issues.\n'));
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
console.error(chalk_1.default.red('Check failed:'), error);
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Resolve Command
|
|
4
|
+
*
|
|
5
|
+
* Computes the final configuration by:
|
|
6
|
+
* 1. Loading genbox.yaml
|
|
7
|
+
* 2. Applying profile settings
|
|
8
|
+
* 3. Resolving $detect markers from detected.yaml
|
|
9
|
+
* 4. Computing environment variables
|
|
10
|
+
* 5. Outputting to .genbox/resolved.yaml
|
|
11
|
+
*
|
|
12
|
+
* This provides transparency into what will actually be created.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* genbox resolve # Resolve with defaults
|
|
16
|
+
* genbox resolve --profile quick # Resolve with specific profile
|
|
17
|
+
* genbox resolve --stdout # Output to stdout
|
|
18
|
+
*/
|
|
19
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
22
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
23
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
24
|
+
}
|
|
25
|
+
Object.defineProperty(o, k2, desc);
|
|
26
|
+
}) : (function(o, m, k, k2) {
|
|
27
|
+
if (k2 === undefined) k2 = k;
|
|
28
|
+
o[k2] = m[k];
|
|
29
|
+
}));
|
|
30
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
31
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
32
|
+
}) : function(o, v) {
|
|
33
|
+
o["default"] = v;
|
|
34
|
+
});
|
|
35
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
36
|
+
var ownKeys = function(o) {
|
|
37
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
38
|
+
var ar = [];
|
|
39
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
40
|
+
return ar;
|
|
41
|
+
};
|
|
42
|
+
return ownKeys(o);
|
|
43
|
+
};
|
|
44
|
+
return function (mod) {
|
|
45
|
+
if (mod && mod.__esModule) return mod;
|
|
46
|
+
var result = {};
|
|
47
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
48
|
+
__setModuleDefault(result, mod);
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
})();
|
|
52
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
53
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
54
|
+
};
|
|
55
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
56
|
+
exports.resolveCommand = void 0;
|
|
57
|
+
const commander_1 = require("commander");
|
|
58
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
59
|
+
const fs = __importStar(require("fs"));
|
|
60
|
+
const path = __importStar(require("path"));
|
|
61
|
+
const yaml = __importStar(require("js-yaml"));
|
|
62
|
+
const config_loader_1 = require("../config-loader");
|
|
63
|
+
const profile_resolver_1 = require("../profile-resolver");
|
|
64
|
+
const detected_config_1 = require("../detected-config");
|
|
65
|
+
exports.resolveCommand = new commander_1.Command('resolve')
|
|
66
|
+
.description('Compute final configuration from genbox.yaml and detected values')
|
|
67
|
+
.option('-p, --profile <profile>', 'Profile to use')
|
|
68
|
+
.option('-a, --apps <apps>', 'Comma-separated apps to include')
|
|
69
|
+
.option('--stdout', 'Output to stdout instead of file')
|
|
70
|
+
.option('--json', 'Output as JSON')
|
|
71
|
+
.option('-y, --yes', 'Skip interactive prompts')
|
|
72
|
+
.action(async (options) => {
|
|
73
|
+
const cwd = process.cwd();
|
|
74
|
+
console.log(chalk_1.default.cyan('\nš Resolving configuration...\n'));
|
|
75
|
+
try {
|
|
76
|
+
// Load configuration
|
|
77
|
+
const configLoader = new config_loader_1.ConfigLoader();
|
|
78
|
+
const loadResult = await configLoader.load(cwd);
|
|
79
|
+
if (!loadResult.found || !loadResult.config) {
|
|
80
|
+
console.log(chalk_1.default.red('No genbox.yaml found. Run "genbox init" first.'));
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
let config = loadResult.config;
|
|
84
|
+
// Load detected config if available
|
|
85
|
+
const detectedPath = path.join(loadResult.root, '.genbox', 'detected.yaml');
|
|
86
|
+
let detectedConfig = null;
|
|
87
|
+
let appliedDetections = [];
|
|
88
|
+
if (fs.existsSync(detectedPath)) {
|
|
89
|
+
try {
|
|
90
|
+
const content = fs.readFileSync(detectedPath, 'utf8');
|
|
91
|
+
detectedConfig = yaml.load(content);
|
|
92
|
+
console.log(chalk_1.default.dim(`Loaded detected.yaml from ${detectedPath}`));
|
|
93
|
+
// Apply $detect markers
|
|
94
|
+
const result = (0, detected_config_1.applyDetectedValues)(config, detectedConfig);
|
|
95
|
+
config = result.config;
|
|
96
|
+
appliedDetections = result.applied;
|
|
97
|
+
if (appliedDetections.length > 0) {
|
|
98
|
+
console.log(chalk_1.default.dim(`Applied ${appliedDetections.length} $detect markers`));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
console.log(chalk_1.default.yellow(`Warning: Could not parse detected.yaml: ${err}`));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Show resolution steps
|
|
106
|
+
console.log(chalk_1.default.bold('Resolution steps:'));
|
|
107
|
+
console.log(` 1. ${chalk_1.default.green('ā')} Loaded genbox.yaml`);
|
|
108
|
+
if (options.profile) {
|
|
109
|
+
const profile = configLoader.getProfile(config, options.profile);
|
|
110
|
+
if (profile) {
|
|
111
|
+
console.log(` 2. ${chalk_1.default.green('ā')} Applied profile '${options.profile}'`);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
console.log(` 2. ${chalk_1.default.yellow('ā ')} Profile '${options.profile}' not found`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (appliedDetections.length > 0) {
|
|
118
|
+
console.log(` 3. ${chalk_1.default.green('ā')} Resolved $detect markers:`);
|
|
119
|
+
for (const path of appliedDetections.slice(0, 5)) {
|
|
120
|
+
console.log(chalk_1.default.dim(` ā ${path}`));
|
|
121
|
+
}
|
|
122
|
+
if (appliedDetections.length > 5) {
|
|
123
|
+
console.log(chalk_1.default.dim(` ... and ${appliedDetections.length - 5} more`));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Resolve using ProfileResolver
|
|
127
|
+
const profileResolver = new profile_resolver_1.ProfileResolver(configLoader);
|
|
128
|
+
const resolved = await profileResolver.resolve(config, {
|
|
129
|
+
name: 'resolved',
|
|
130
|
+
profile: options.profile,
|
|
131
|
+
apps: options.apps?.split(',').map(s => s.trim()),
|
|
132
|
+
yes: options.yes ?? true, // Default to non-interactive for resolve
|
|
133
|
+
});
|
|
134
|
+
// Add metadata
|
|
135
|
+
const resolvedWithMeta = {
|
|
136
|
+
_meta: {
|
|
137
|
+
resolved_at: new Date().toISOString(),
|
|
138
|
+
profile: options.profile || null,
|
|
139
|
+
detect_markers_applied: appliedDetections,
|
|
140
|
+
},
|
|
141
|
+
...resolved,
|
|
142
|
+
};
|
|
143
|
+
// Output
|
|
144
|
+
if (options.stdout) {
|
|
145
|
+
const output = options.json
|
|
146
|
+
? JSON.stringify(resolvedWithMeta, null, 2)
|
|
147
|
+
: yaml.dump(resolvedWithMeta, { lineWidth: 120, noRefs: true });
|
|
148
|
+
console.log('\n' + output);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
await writeResolvedConfig(resolvedWithMeta, loadResult.root, options.json);
|
|
152
|
+
showResolvedSummary(resolved, appliedDetections);
|
|
153
|
+
}
|
|
154
|
+
// Show warnings
|
|
155
|
+
if (resolved.warnings.length > 0) {
|
|
156
|
+
console.log(chalk_1.default.yellow('\nā ļø Warnings:'));
|
|
157
|
+
for (const warning of resolved.warnings) {
|
|
158
|
+
console.log(chalk_1.default.yellow(` ⢠${warning}`));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
console.log(chalk_1.default.bold('\nš Next steps:\n'));
|
|
162
|
+
console.log(' 1. Review the resolved configuration in .genbox/resolved.yaml');
|
|
163
|
+
console.log(' 2. Run ' + chalk_1.default.cyan('genbox validate') + ' to check for errors');
|
|
164
|
+
console.log(' 3. Run ' + chalk_1.default.cyan('genbox create <name>') + ' to provision');
|
|
165
|
+
console.log();
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
console.error(chalk_1.default.red('Resolution failed:'), error);
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
async function writeResolvedConfig(resolved, root, asJson) {
|
|
173
|
+
const genboxDir = path.join(root, '.genbox');
|
|
174
|
+
// Create .genbox directory if it doesn't exist
|
|
175
|
+
if (!fs.existsSync(genboxDir)) {
|
|
176
|
+
fs.mkdirSync(genboxDir, { recursive: true });
|
|
177
|
+
}
|
|
178
|
+
// Write resolved config
|
|
179
|
+
const filename = asJson ? 'resolved.json' : 'resolved.yaml';
|
|
180
|
+
const filePath = path.join(genboxDir, filename);
|
|
181
|
+
const content = asJson
|
|
182
|
+
? JSON.stringify(resolved, null, 2)
|
|
183
|
+
: yaml.dump(resolved, { lineWidth: 120, noRefs: true });
|
|
184
|
+
fs.writeFileSync(filePath, content);
|
|
185
|
+
console.log(chalk_1.default.green(`\nā Resolved configuration written to: ${filePath}`));
|
|
186
|
+
}
|
|
187
|
+
function showResolvedSummary(resolved, appliedDetections) {
|
|
188
|
+
console.log(chalk_1.default.bold('\nš Resolved Configuration:\n'));
|
|
189
|
+
// Basic info
|
|
190
|
+
console.log(` Name: ${chalk_1.default.cyan(resolved.name)}`);
|
|
191
|
+
console.log(` Size: ${chalk_1.default.cyan(resolved.size)}`);
|
|
192
|
+
console.log(` Project: ${resolved.project.name} (${resolved.project.structure})`);
|
|
193
|
+
// Apps
|
|
194
|
+
if (resolved.apps.length > 0) {
|
|
195
|
+
console.log(`\n Apps (${resolved.apps.length}):`);
|
|
196
|
+
for (const app of resolved.apps) {
|
|
197
|
+
const deps = Object.entries(app.dependencies)
|
|
198
|
+
.map(([name, config]) => `${name}:${config.mode}`)
|
|
199
|
+
.join(', ');
|
|
200
|
+
console.log(` ${chalk_1.default.cyan(app.name)}: ${app.type}${app.framework ? ` [${app.framework}]` : ''}`);
|
|
201
|
+
if (deps) {
|
|
202
|
+
console.log(chalk_1.default.dim(` Dependencies: ${deps}`));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// Infrastructure
|
|
207
|
+
if (resolved.infrastructure.length > 0) {
|
|
208
|
+
console.log(`\n Infrastructure (${resolved.infrastructure.length}):`);
|
|
209
|
+
for (const infra of resolved.infrastructure) {
|
|
210
|
+
console.log(` ${chalk_1.default.cyan(infra.name)}: ${infra.type} (${infra.mode})`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Database
|
|
214
|
+
console.log(`\n Database: ${chalk_1.default.cyan(resolved.database.mode)}`);
|
|
215
|
+
if (resolved.database.source) {
|
|
216
|
+
console.log(chalk_1.default.dim(` Source: ${resolved.database.source}`));
|
|
217
|
+
}
|
|
218
|
+
// Repos
|
|
219
|
+
if (resolved.repos.length > 0) {
|
|
220
|
+
console.log(`\n Repositories (${resolved.repos.length}):`);
|
|
221
|
+
for (const repo of resolved.repos) {
|
|
222
|
+
console.log(` ${chalk_1.default.cyan(repo.name)}: ${repo.url}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// Environment variables
|
|
226
|
+
const envCount = Object.keys(resolved.env).length;
|
|
227
|
+
if (envCount > 0) {
|
|
228
|
+
console.log(`\n Environment variables: ${envCount}`);
|
|
229
|
+
for (const [key, value] of Object.entries(resolved.env).slice(0, 5)) {
|
|
230
|
+
const displayValue = key.includes('SECRET') || key.includes('PASSWORD')
|
|
231
|
+
? '***'
|
|
232
|
+
: value.length > 50 ? value.slice(0, 47) + '...' : value;
|
|
233
|
+
console.log(chalk_1.default.dim(` ${key}=${displayValue}`));
|
|
234
|
+
}
|
|
235
|
+
if (envCount > 5) {
|
|
236
|
+
console.log(chalk_1.default.dim(` ... and ${envCount - 5} more`));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
// Detection info
|
|
240
|
+
if (appliedDetections.length > 0) {
|
|
241
|
+
console.log(chalk_1.default.yellow(`\n ā” ${appliedDetections.length} values from $detect markers`));
|
|
242
|
+
}
|
|
243
|
+
}
|