genbox 1.0.14 → 1.0.15
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/connect.js +7 -2
- package/dist/commands/create.js +51 -115
- package/dist/commands/init.js +360 -105
- package/dist/commands/profiles.js +49 -13
- package/dist/commands/scan.js +64 -2
- package/dist/commands/ssh-setup.js +34 -0
- package/dist/config-explainer.js +2 -1
- package/dist/config-loader.js +131 -41
- package/dist/index.js +3 -1
- package/dist/profile-resolver.js +35 -18
- package/package.json +1 -1
|
@@ -44,6 +44,7 @@ const yaml = __importStar(require("js-yaml"));
|
|
|
44
44
|
const fs = __importStar(require("fs"));
|
|
45
45
|
const path = __importStar(require("path"));
|
|
46
46
|
const config_loader_1 = require("../config-loader");
|
|
47
|
+
const schema_v4_1 = require("../schema-v4");
|
|
47
48
|
exports.profilesCommand = new commander_1.Command('profiles')
|
|
48
49
|
.description('List and manage profiles')
|
|
49
50
|
.option('--json', 'Output as JSON')
|
|
@@ -51,9 +52,15 @@ exports.profilesCommand = new commander_1.Command('profiles')
|
|
|
51
52
|
try {
|
|
52
53
|
const configLoader = new config_loader_1.ConfigLoader();
|
|
53
54
|
const loadResult = await configLoader.load();
|
|
54
|
-
if (!loadResult.config
|
|
55
|
-
console.log(chalk_1.default.yellow('
|
|
56
|
-
console.log(chalk_1.default.dim('Run "genbox init" to create a
|
|
55
|
+
if (!loadResult.config) {
|
|
56
|
+
console.log(chalk_1.default.yellow('No genbox.yaml found'));
|
|
57
|
+
console.log(chalk_1.default.dim('Run "genbox init" to create a configuration'));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const version = (0, schema_v4_1.getConfigVersion)(loadResult.config);
|
|
61
|
+
if (version === 'unknown') {
|
|
62
|
+
console.log(chalk_1.default.yellow('Unknown config version'));
|
|
63
|
+
console.log(chalk_1.default.dim('Run "genbox init" to create a v4 configuration'));
|
|
57
64
|
return;
|
|
58
65
|
}
|
|
59
66
|
const config = loadResult.config;
|
|
@@ -105,8 +112,13 @@ exports.profilesCommand
|
|
|
105
112
|
try {
|
|
106
113
|
const configLoader = new config_loader_1.ConfigLoader();
|
|
107
114
|
const loadResult = await configLoader.load();
|
|
108
|
-
if (!loadResult.config
|
|
109
|
-
console.log(chalk_1.default.yellow('
|
|
115
|
+
if (!loadResult.config) {
|
|
116
|
+
console.log(chalk_1.default.yellow('No genbox.yaml found'));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const configVersion = (0, schema_v4_1.getConfigVersion)(loadResult.config);
|
|
120
|
+
if (configVersion === 'unknown') {
|
|
121
|
+
console.log(chalk_1.default.yellow('Unknown config version'));
|
|
110
122
|
return;
|
|
111
123
|
}
|
|
112
124
|
const config = loadResult.config;
|
|
@@ -137,8 +149,10 @@ exports.profilesCommand
|
|
|
137
149
|
}
|
|
138
150
|
}
|
|
139
151
|
}
|
|
140
|
-
|
|
141
|
-
|
|
152
|
+
// Show connection (v4: default_connection, v3: connect_to)
|
|
153
|
+
const connection = (0, config_loader_1.getProfileConnection)(profile);
|
|
154
|
+
if (connection) {
|
|
155
|
+
console.log(` ${chalk_1.default.bold('Connect to:')} ${connection} environment`);
|
|
142
156
|
}
|
|
143
157
|
if (profile.database) {
|
|
144
158
|
console.log(` ${chalk_1.default.bold('Database:')}`);
|
|
@@ -147,9 +161,21 @@ exports.profilesCommand
|
|
|
147
161
|
console.log(` Source: ${profile.database.source}`);
|
|
148
162
|
}
|
|
149
163
|
}
|
|
150
|
-
|
|
164
|
+
// v4 uses connections, v3 uses infrastructure
|
|
165
|
+
const v4Profile = profile;
|
|
166
|
+
const v3Profile = profile;
|
|
167
|
+
if (v4Profile.connections) {
|
|
168
|
+
console.log(` ${chalk_1.default.bold('Connections:')}`);
|
|
169
|
+
for (const [appName, appConns] of Object.entries(v4Profile.connections)) {
|
|
170
|
+
console.log(` ${appName}:`);
|
|
171
|
+
for (const [targetName, mode] of Object.entries(appConns)) {
|
|
172
|
+
console.log(` ${targetName}: ${mode}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else if (v3Profile.infrastructure) {
|
|
151
177
|
console.log(` ${chalk_1.default.bold('Infrastructure:')}`);
|
|
152
|
-
for (const [name, mode] of Object.entries(
|
|
178
|
+
for (const [name, mode] of Object.entries(v3Profile.infrastructure)) {
|
|
153
179
|
console.log(` ${name}: ${mode}`);
|
|
154
180
|
}
|
|
155
181
|
}
|
|
@@ -169,8 +195,13 @@ exports.profilesCommand
|
|
|
169
195
|
try {
|
|
170
196
|
const configLoader = new config_loader_1.ConfigLoader();
|
|
171
197
|
const loadResult = await configLoader.load();
|
|
172
|
-
if (!loadResult.config
|
|
173
|
-
console.log(chalk_1.default.yellow('
|
|
198
|
+
if (!loadResult.config) {
|
|
199
|
+
console.log(chalk_1.default.yellow('No genbox.yaml found'));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const configVersion = (0, schema_v4_1.getConfigVersion)(loadResult.config);
|
|
203
|
+
if (configVersion === 'unknown') {
|
|
204
|
+
console.log(chalk_1.default.yellow('Unknown config version'));
|
|
174
205
|
return;
|
|
175
206
|
}
|
|
176
207
|
const config = loadResult.config;
|
|
@@ -286,8 +317,13 @@ exports.profilesCommand
|
|
|
286
317
|
try {
|
|
287
318
|
const configLoader = new config_loader_1.ConfigLoader();
|
|
288
319
|
const loadResult = await configLoader.load();
|
|
289
|
-
if (!loadResult.config
|
|
290
|
-
console.log(chalk_1.default.yellow('
|
|
320
|
+
if (!loadResult.config) {
|
|
321
|
+
console.log(chalk_1.default.yellow('No genbox.yaml found'));
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const configVersion = (0, schema_v4_1.getConfigVersion)(loadResult.config);
|
|
325
|
+
if (configVersion === 'unknown') {
|
|
326
|
+
console.log(chalk_1.default.yellow('Unknown config version'));
|
|
291
327
|
return;
|
|
292
328
|
}
|
|
293
329
|
const config = loadResult.config;
|
package/dist/commands/scan.js
CHANGED
|
@@ -56,9 +56,53 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
56
56
|
const fs = __importStar(require("fs"));
|
|
57
57
|
const path = __importStar(require("path"));
|
|
58
58
|
const yaml = __importStar(require("js-yaml"));
|
|
59
|
+
const child_process_1 = require("child_process");
|
|
59
60
|
const scanner_1 = require("../scanner");
|
|
60
61
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
61
62
|
const { version } = require('../../package.json');
|
|
63
|
+
/**
|
|
64
|
+
* Detect git repository info for a specific directory
|
|
65
|
+
*/
|
|
66
|
+
function detectGitForDirectory(dir) {
|
|
67
|
+
const gitDir = path.join(dir, '.git');
|
|
68
|
+
if (!fs.existsSync(gitDir))
|
|
69
|
+
return undefined;
|
|
70
|
+
try {
|
|
71
|
+
const remote = (0, child_process_1.execSync)('git remote get-url origin', {
|
|
72
|
+
cwd: dir,
|
|
73
|
+
stdio: 'pipe',
|
|
74
|
+
encoding: 'utf8',
|
|
75
|
+
}).trim();
|
|
76
|
+
if (!remote)
|
|
77
|
+
return undefined;
|
|
78
|
+
const isSSH = remote.startsWith('git@') || remote.startsWith('ssh://');
|
|
79
|
+
let provider = 'other';
|
|
80
|
+
if (remote.includes('github.com'))
|
|
81
|
+
provider = 'github';
|
|
82
|
+
else if (remote.includes('gitlab.com'))
|
|
83
|
+
provider = 'gitlab';
|
|
84
|
+
else if (remote.includes('bitbucket.org'))
|
|
85
|
+
provider = 'bitbucket';
|
|
86
|
+
let branch = 'main';
|
|
87
|
+
try {
|
|
88
|
+
branch = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', {
|
|
89
|
+
cwd: dir,
|
|
90
|
+
stdio: 'pipe',
|
|
91
|
+
encoding: 'utf8',
|
|
92
|
+
}).trim();
|
|
93
|
+
}
|
|
94
|
+
catch { }
|
|
95
|
+
return {
|
|
96
|
+
remote,
|
|
97
|
+
type: isSSH ? 'ssh' : 'https',
|
|
98
|
+
provider,
|
|
99
|
+
branch,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
62
106
|
exports.scanCommand = new commander_1.Command('scan')
|
|
63
107
|
.description('Analyze project structure and output detected configuration')
|
|
64
108
|
.option('--stdout', 'Output to stdout instead of .genbox/detected.yaml')
|
|
@@ -117,10 +161,17 @@ function convertScanToDetected(scan, root) {
|
|
|
117
161
|
})),
|
|
118
162
|
apps: {},
|
|
119
163
|
};
|
|
120
|
-
// Convert apps
|
|
164
|
+
// Convert apps (detect git info for each app in multi-repo workspaces)
|
|
165
|
+
const isMultiRepo = scan.structure.type === 'hybrid';
|
|
121
166
|
for (const app of scan.apps) {
|
|
122
167
|
// Map scanner AppType to DetectedApp type
|
|
123
168
|
const mappedType = mapAppType(app.type);
|
|
169
|
+
// Detect git info for this app (for multi-repo workspaces)
|
|
170
|
+
let appGit;
|
|
171
|
+
if (isMultiRepo) {
|
|
172
|
+
const appDir = path.join(root, app.path);
|
|
173
|
+
appGit = detectGitForDirectory(appDir);
|
|
174
|
+
}
|
|
124
175
|
detected.apps[app.name] = {
|
|
125
176
|
path: app.path,
|
|
126
177
|
type: mappedType,
|
|
@@ -135,6 +186,7 @@ function convertScanToDetected(scan, root) {
|
|
|
135
186
|
start: app.scripts.start,
|
|
136
187
|
} : undefined,
|
|
137
188
|
dependencies: app.dependencies,
|
|
189
|
+
git: appGit,
|
|
138
190
|
};
|
|
139
191
|
}
|
|
140
192
|
// Convert infrastructure
|
|
@@ -341,11 +393,21 @@ function showSummary(detected) {
|
|
|
341
393
|
console.log(` ${chalk_1.default.cyan(infra.name)}: ${infra.type} (${infra.image})`);
|
|
342
394
|
}
|
|
343
395
|
}
|
|
344
|
-
// Git
|
|
396
|
+
// Git (root level)
|
|
345
397
|
if (detected.git?.remote) {
|
|
346
398
|
console.log(`\n Git: ${detected.git.provider || 'git'} (${detected.git.type})`);
|
|
347
399
|
console.log(chalk_1.default.dim(` Branch: ${detected.git.branch || 'unknown'}`));
|
|
348
400
|
}
|
|
401
|
+
// Per-app git repos (for multi-repo workspaces)
|
|
402
|
+
const appsWithGit = Object.entries(detected.apps).filter(([, app]) => app.git);
|
|
403
|
+
if (appsWithGit.length > 0) {
|
|
404
|
+
console.log(`\n App Repositories (${appsWithGit.length}):`);
|
|
405
|
+
for (const [name, app] of appsWithGit) {
|
|
406
|
+
const git = app.git;
|
|
407
|
+
console.log(` ${chalk_1.default.cyan(name)}: ${git.provider} (${git.type})`);
|
|
408
|
+
console.log(chalk_1.default.dim(` ${git.remote}`));
|
|
409
|
+
}
|
|
410
|
+
}
|
|
349
411
|
console.log(chalk_1.default.bold('\n📝 Next steps:\n'));
|
|
350
412
|
console.log(' 1. Review the detected configuration in .genbox/detected.yaml');
|
|
351
413
|
console.log(' 2. Run ' + chalk_1.default.cyan('genbox init --from-scan') + ' to create genbox.yaml');
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sshSetupCommand = void 0;
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const api_1 = require("../api");
|
|
6
|
+
const ssh_config_1 = require("../ssh-config");
|
|
7
|
+
/**
|
|
8
|
+
* Internal command to poll for IP and add SSH config in background
|
|
9
|
+
* Called by: genbox ssh-setup <genboxId> <name>
|
|
10
|
+
*/
|
|
11
|
+
exports.sshSetupCommand = new commander_1.Command('ssh-setup')
|
|
12
|
+
.argument('<genboxId>', 'Genbox ID')
|
|
13
|
+
.argument('<name>', 'Genbox name')
|
|
14
|
+
.option('--max-attempts <n>', 'Max polling attempts', '90')
|
|
15
|
+
.option('--delay <ms>', 'Delay between attempts in ms', '2000')
|
|
16
|
+
.action(async (genboxId, name, options) => {
|
|
17
|
+
const maxAttempts = parseInt(options.maxAttempts, 10);
|
|
18
|
+
const delayMs = parseInt(options.delay, 10);
|
|
19
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
20
|
+
try {
|
|
21
|
+
const genbox = await (0, api_1.fetchApi)(`/genboxes/${genboxId}`);
|
|
22
|
+
if (genbox.ipAddress) {
|
|
23
|
+
(0, ssh_config_1.addSshConfigEntry)({ name, ipAddress: genbox.ipAddress });
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Ignore errors during polling
|
|
29
|
+
}
|
|
30
|
+
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
31
|
+
}
|
|
32
|
+
// Timed out - exit silently
|
|
33
|
+
process.exit(0);
|
|
34
|
+
});
|
package/dist/config-explainer.js
CHANGED
|
@@ -200,7 +200,8 @@ class ConfigExplainer {
|
|
|
200
200
|
if (!config)
|
|
201
201
|
return warnings;
|
|
202
202
|
// Check each app for implicit type/port/framework
|
|
203
|
-
for (const [appName,
|
|
203
|
+
for (const [appName, rawAppConfig] of Object.entries(config.apps || {})) {
|
|
204
|
+
const appConfig = rawAppConfig;
|
|
204
205
|
// Check type - was it inferred?
|
|
205
206
|
if (appConfig.type) {
|
|
206
207
|
const typeExplanation = this.explain(`apps.${appName}.type`);
|
package/dist/config-loader.js
CHANGED
|
@@ -44,10 +44,42 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
44
44
|
})();
|
|
45
45
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
46
|
exports.configLoader = exports.ConfigLoader = void 0;
|
|
47
|
+
exports.isV4 = isV4;
|
|
48
|
+
exports.getProfileConnection = getProfileConnection;
|
|
49
|
+
exports.getAppDependencies = getAppDependencies;
|
|
50
|
+
exports.getInfrastructure = getInfrastructure;
|
|
47
51
|
const fs = __importStar(require("fs"));
|
|
48
52
|
const path = __importStar(require("path"));
|
|
49
53
|
const yaml = __importStar(require("js-yaml"));
|
|
50
54
|
const os = __importStar(require("os"));
|
|
55
|
+
const schema_v4_1 = require("./schema-v4");
|
|
56
|
+
// Helper to check if config is v4
|
|
57
|
+
function isV4(config) {
|
|
58
|
+
return (0, schema_v4_1.getConfigVersion)(config) === 4;
|
|
59
|
+
}
|
|
60
|
+
// Helper to get profile connection (handles both v3 and v4)
|
|
61
|
+
function getProfileConnection(profile) {
|
|
62
|
+
// v4 uses default_connection, v3 uses connect_to
|
|
63
|
+
return profile.default_connection || profile.connect_to;
|
|
64
|
+
}
|
|
65
|
+
// Helper to get app dependencies (handles both v3 and v4)
|
|
66
|
+
function getAppDependencies(app) {
|
|
67
|
+
// v4 uses connects_to, v3 uses requires
|
|
68
|
+
if (app.connects_to) {
|
|
69
|
+
return Object.keys(app.connects_to);
|
|
70
|
+
}
|
|
71
|
+
if (app.requires) {
|
|
72
|
+
return Object.keys(app.requires);
|
|
73
|
+
}
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
// Helper to get infrastructure/provides (handles both v3 and v4)
|
|
77
|
+
function getInfrastructure(config) {
|
|
78
|
+
if (isV4(config)) {
|
|
79
|
+
return config.provides;
|
|
80
|
+
}
|
|
81
|
+
return config.infrastructure;
|
|
82
|
+
}
|
|
51
83
|
const CONFIG_FILENAME = 'genbox.yaml';
|
|
52
84
|
const ENV_FILENAME = '.env.genbox';
|
|
53
85
|
const USER_CONFIG_DIR = path.join(os.homedir(), '.genbox');
|
|
@@ -189,17 +221,41 @@ class ConfigLoader {
|
|
|
189
221
|
}
|
|
190
222
|
/**
|
|
191
223
|
* Merge configurations from all sources
|
|
224
|
+
* Supports both v3 and v4 config formats
|
|
192
225
|
*/
|
|
193
226
|
mergeConfigs(sources, userConfig) {
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
227
|
+
// Detect version from first source that has it
|
|
228
|
+
let isVersion4 = false;
|
|
229
|
+
for (const source of sources) {
|
|
230
|
+
// Version can be number (v4) or string (v3)
|
|
231
|
+
if (source.config.version === 4) {
|
|
232
|
+
isVersion4 = true;
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Start with appropriate default structure
|
|
237
|
+
const merged = isVersion4
|
|
238
|
+
? {
|
|
239
|
+
version: 4,
|
|
240
|
+
project: {
|
|
241
|
+
name: 'unnamed',
|
|
242
|
+
structure: 'single-app',
|
|
243
|
+
},
|
|
244
|
+
apps: {},
|
|
245
|
+
strict: {
|
|
246
|
+
enabled: true,
|
|
247
|
+
allow_detect: true,
|
|
248
|
+
warnings_as_errors: false,
|
|
249
|
+
},
|
|
250
|
+
}
|
|
251
|
+
: {
|
|
252
|
+
version: '3.0',
|
|
253
|
+
project: {
|
|
254
|
+
name: 'unnamed',
|
|
255
|
+
structure: 'single-app',
|
|
256
|
+
},
|
|
257
|
+
apps: {},
|
|
258
|
+
};
|
|
203
259
|
// Merge sources in order (workspace first, then project)
|
|
204
260
|
for (const source of sources) {
|
|
205
261
|
this.deepMerge(merged, source.config);
|
|
@@ -244,11 +300,12 @@ class ConfigLoader {
|
|
|
244
300
|
}
|
|
245
301
|
}
|
|
246
302
|
/**
|
|
247
|
-
* Validate configuration
|
|
303
|
+
* Validate configuration (supports both v3 and v4)
|
|
248
304
|
*/
|
|
249
305
|
validate(config) {
|
|
250
306
|
const errors = [];
|
|
251
307
|
const warnings = [];
|
|
308
|
+
const configIsV4 = isV4(config);
|
|
252
309
|
// Required fields
|
|
253
310
|
if (!config.project?.name) {
|
|
254
311
|
errors.push({
|
|
@@ -264,6 +321,8 @@ class ConfigLoader {
|
|
|
264
321
|
severity: 'warning',
|
|
265
322
|
});
|
|
266
323
|
}
|
|
324
|
+
// Get infrastructure (v4: provides, v3: infrastructure)
|
|
325
|
+
const infra = getInfrastructure(config);
|
|
267
326
|
// Validate apps
|
|
268
327
|
for (const [name, app] of Object.entries(config.apps || {})) {
|
|
269
328
|
if (!app.path) {
|
|
@@ -280,18 +339,18 @@ class ConfigLoader {
|
|
|
280
339
|
severity: 'warning',
|
|
281
340
|
});
|
|
282
341
|
}
|
|
283
|
-
// Validate dependencies
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
342
|
+
// Validate dependencies (v4: connects_to, v3: requires)
|
|
343
|
+
const deps = getAppDependencies(app);
|
|
344
|
+
const depField = configIsV4 ? 'connects_to' : 'requires';
|
|
345
|
+
for (const dep of deps) {
|
|
346
|
+
const isAppDep = config.apps?.[dep];
|
|
347
|
+
const isInfraDep = infra?.[dep];
|
|
348
|
+
if (!isAppDep && !isInfraDep) {
|
|
349
|
+
warnings.push({
|
|
350
|
+
path: `apps.${name}.${depField}.${dep}`,
|
|
351
|
+
message: `App '${name}' ${configIsV4 ? 'connects to' : 'requires'} '${dep}' which is not defined`,
|
|
352
|
+
severity: 'warning',
|
|
353
|
+
});
|
|
295
354
|
}
|
|
296
355
|
}
|
|
297
356
|
}
|
|
@@ -315,11 +374,13 @@ class ConfigLoader {
|
|
|
315
374
|
severity: 'error',
|
|
316
375
|
});
|
|
317
376
|
}
|
|
318
|
-
// Check connect_to
|
|
319
|
-
|
|
377
|
+
// Check connection (v4: default_connection, v3: connect_to)
|
|
378
|
+
const connection = getProfileConnection(profile);
|
|
379
|
+
if (connection && !config.environments?.[connection]) {
|
|
380
|
+
const connField = configIsV4 ? 'default_connection' : 'connect_to';
|
|
320
381
|
warnings.push({
|
|
321
|
-
path: `profiles.${name}
|
|
322
|
-
message: `Profile '${name}' connects to undefined environment '${
|
|
382
|
+
path: `profiles.${name}.${connField}`,
|
|
383
|
+
message: `Profile '${name}' connects to undefined environment '${connection}'`,
|
|
323
384
|
severity: 'warning',
|
|
324
385
|
});
|
|
325
386
|
}
|
|
@@ -339,12 +400,24 @@ class ConfigLoader {
|
|
|
339
400
|
}
|
|
340
401
|
}
|
|
341
402
|
};
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
403
|
+
// v4 uses urls object, v3 uses separate mongodb/redis/rabbitmq objects
|
|
404
|
+
if (configIsV4) {
|
|
405
|
+
const v4Env = env;
|
|
406
|
+
if (v4Env.urls) {
|
|
407
|
+
for (const [key, url] of Object.entries(v4Env.urls)) {
|
|
408
|
+
checkValue(url, `environments.${name}.urls.${key}`);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
const v3Env = env;
|
|
414
|
+
if (v3Env.mongodb?.url)
|
|
415
|
+
checkValue(v3Env.mongodb.url, `environments.${name}.mongodb.url`);
|
|
416
|
+
if (v3Env.redis?.url)
|
|
417
|
+
checkValue(v3Env.redis.url, `environments.${name}.redis.url`);
|
|
418
|
+
if (v3Env.rabbitmq?.url)
|
|
419
|
+
checkValue(v3Env.rabbitmq.url, `environments.${name}.rabbitmq.url`);
|
|
420
|
+
}
|
|
348
421
|
}
|
|
349
422
|
return {
|
|
350
423
|
valid: errors.length === 0,
|
|
@@ -353,7 +426,7 @@ class ConfigLoader {
|
|
|
353
426
|
};
|
|
354
427
|
}
|
|
355
428
|
/**
|
|
356
|
-
* Get a specific profile, resolving extends
|
|
429
|
+
* Get a specific profile, resolving extends (supports both v3 and v4)
|
|
357
430
|
*/
|
|
358
431
|
getProfile(config, profileName) {
|
|
359
432
|
const profile = config.profiles?.[profileName];
|
|
@@ -368,7 +441,7 @@ class ConfigLoader {
|
|
|
368
441
|
return this.resolveProfile(config, profile);
|
|
369
442
|
}
|
|
370
443
|
/**
|
|
371
|
-
* Resolve profile inheritance (extends)
|
|
444
|
+
* Resolve profile inheritance (extends) - supports both v3 and v4
|
|
372
445
|
*/
|
|
373
446
|
resolveProfile(config, profile, userProfiles) {
|
|
374
447
|
if (!profile.extends) {
|
|
@@ -385,25 +458,40 @@ class ConfigLoader {
|
|
|
385
458
|
}
|
|
386
459
|
// Resolve parent first (recursive)
|
|
387
460
|
const resolvedParent = this.resolveProfile(config, parent, userProfiles);
|
|
388
|
-
// Merge parent into child
|
|
389
|
-
|
|
461
|
+
// Merge parent into child - handle both v3 and v4
|
|
462
|
+
const merged = {
|
|
390
463
|
...resolvedParent,
|
|
391
464
|
...profile,
|
|
392
465
|
// Arrays are replaced, not merged
|
|
393
466
|
apps: profile.apps || resolvedParent.apps,
|
|
394
467
|
// Objects are merged
|
|
395
|
-
infrastructure: {
|
|
396
|
-
...resolvedParent.infrastructure,
|
|
397
|
-
...profile.infrastructure,
|
|
398
|
-
},
|
|
399
468
|
env: {
|
|
400
469
|
...resolvedParent.env,
|
|
401
470
|
...profile.env,
|
|
402
471
|
},
|
|
403
472
|
};
|
|
473
|
+
// Handle v4 connections override
|
|
474
|
+
if (isV4(config)) {
|
|
475
|
+
const v4Profile = profile;
|
|
476
|
+
const v4Parent = resolvedParent;
|
|
477
|
+
merged.connections = {
|
|
478
|
+
...v4Parent.connections,
|
|
479
|
+
...v4Profile.connections,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
// v3 infrastructure merge
|
|
484
|
+
const v3Profile = profile;
|
|
485
|
+
const v3Parent = resolvedParent;
|
|
486
|
+
merged.infrastructure = {
|
|
487
|
+
...v3Parent.infrastructure,
|
|
488
|
+
...v3Profile.infrastructure,
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
return merged;
|
|
404
492
|
}
|
|
405
493
|
/**
|
|
406
|
-
* List all available profiles
|
|
494
|
+
* List all available profiles (supports both v3 and v4)
|
|
407
495
|
*/
|
|
408
496
|
listProfiles(config) {
|
|
409
497
|
const profiles = [];
|
|
@@ -416,6 +504,7 @@ class ConfigLoader {
|
|
|
416
504
|
source: 'project',
|
|
417
505
|
apps: resolved?.apps || [],
|
|
418
506
|
size: resolved?.size,
|
|
507
|
+
connection: resolved ? getProfileConnection(resolved) : undefined,
|
|
419
508
|
});
|
|
420
509
|
}
|
|
421
510
|
// User profiles
|
|
@@ -432,6 +521,7 @@ class ConfigLoader {
|
|
|
432
521
|
source: 'user',
|
|
433
522
|
apps: resolved?.apps || [],
|
|
434
523
|
size: resolved?.size,
|
|
524
|
+
connection: resolved ? getProfileConnection(resolved) : undefined,
|
|
435
525
|
});
|
|
436
526
|
}
|
|
437
527
|
}
|
package/dist/index.js
CHANGED
|
@@ -29,6 +29,7 @@ const scan_1 = require("./commands/scan");
|
|
|
29
29
|
const resolve_1 = require("./commands/resolve");
|
|
30
30
|
const validate_1 = require("./commands/validate");
|
|
31
31
|
const migrate_1 = require("./commands/migrate");
|
|
32
|
+
const ssh_setup_1 = require("./commands/ssh-setup");
|
|
32
33
|
program
|
|
33
34
|
.addCommand(init_1.initCommand)
|
|
34
35
|
.addCommand(create_1.createCommand)
|
|
@@ -50,5 +51,6 @@ program
|
|
|
50
51
|
.addCommand(resolve_1.resolveCommand)
|
|
51
52
|
.addCommand(validate_1.validateCommand)
|
|
52
53
|
.addCommand(migrate_1.migrateCommand)
|
|
53
|
-
.addCommand(migrate_1.deprecationsCommand)
|
|
54
|
+
.addCommand(migrate_1.deprecationsCommand)
|
|
55
|
+
.addCommand(ssh_setup_1.sshSetupCommand);
|
|
54
56
|
program.parse(process.argv);
|