genbox 1.0.11 → 1.0.13
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/create.js +139 -9
- package/dist/commands/init.js +154 -84
- 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,334 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Configuration Migration Utilities
|
|
4
|
+
*
|
|
5
|
+
* Handles migration from v3 to v4 schema:
|
|
6
|
+
* - Converts `requires` to `connects_to`
|
|
7
|
+
* - Converts `infrastructure` to `provides`
|
|
8
|
+
* - Adds explicit types where inferred
|
|
9
|
+
* - Shows deprecation warnings
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.checkDeprecations = checkDeprecations;
|
|
13
|
+
exports.migrateV3ToV4 = migrateV3ToV4;
|
|
14
|
+
exports.needsMigration = needsMigration;
|
|
15
|
+
exports.getMigrationSummary = getMigrationSummary;
|
|
16
|
+
const schema_v4_1 = require("./schema-v4");
|
|
17
|
+
/**
|
|
18
|
+
* Check a v3 config for deprecated patterns
|
|
19
|
+
*/
|
|
20
|
+
function checkDeprecations(config) {
|
|
21
|
+
const warnings = [];
|
|
22
|
+
// Check version (v3 version is '3.0' string, v4 is number 4)
|
|
23
|
+
if (config.version !== 4) {
|
|
24
|
+
warnings.push({
|
|
25
|
+
path: 'version',
|
|
26
|
+
message: `Config version ${config.version} is deprecated`,
|
|
27
|
+
suggestion: 'Run "genbox migrate" to upgrade to version 4',
|
|
28
|
+
severity: 'warning',
|
|
29
|
+
autoMigrate: true,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
// Check for requires in apps
|
|
33
|
+
for (const [appName, appConfig] of Object.entries(config.apps || {})) {
|
|
34
|
+
if (appConfig.requires) {
|
|
35
|
+
warnings.push({
|
|
36
|
+
path: `apps.${appName}.requires`,
|
|
37
|
+
message: '"requires" is deprecated in v4',
|
|
38
|
+
suggestion: 'Use "connects_to" with explicit connection modes',
|
|
39
|
+
severity: 'warning',
|
|
40
|
+
autoMigrate: true,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Check for infrastructure (should be provides)
|
|
45
|
+
if (config.infrastructure) {
|
|
46
|
+
warnings.push({
|
|
47
|
+
path: 'infrastructure',
|
|
48
|
+
message: '"infrastructure" is renamed to "provides" in v4',
|
|
49
|
+
suggestion: 'Rename to "provides" for clarity',
|
|
50
|
+
severity: 'warning',
|
|
51
|
+
autoMigrate: true,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
// Check for profile connect_to
|
|
55
|
+
for (const [profileName, profile] of Object.entries(config.profiles || {})) {
|
|
56
|
+
if (profile.connect_to && typeof profile.connect_to === 'string') {
|
|
57
|
+
warnings.push({
|
|
58
|
+
path: `profiles.${profileName}.connect_to`,
|
|
59
|
+
message: 'Single string "connect_to" is deprecated in v4',
|
|
60
|
+
suggestion: 'Use "default_connection" or per-app "connections" overrides',
|
|
61
|
+
severity: 'warning',
|
|
62
|
+
autoMigrate: true,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Check for implicit app types (inferred from names)
|
|
67
|
+
for (const [appName, appConfig] of Object.entries(config.apps || {})) {
|
|
68
|
+
if (!appConfig.type) {
|
|
69
|
+
warnings.push({
|
|
70
|
+
path: `apps.${appName}.type`,
|
|
71
|
+
message: `App "${appName}" has no explicit type`,
|
|
72
|
+
suggestion: 'Add explicit "type: frontend|backend|worker|gateway"',
|
|
73
|
+
severity: 'warning',
|
|
74
|
+
autoMigrate: false,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return warnings;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Migrate a v3 config to v4
|
|
82
|
+
*/
|
|
83
|
+
function migrateV3ToV4(v3Config) {
|
|
84
|
+
const changes = [];
|
|
85
|
+
const warnings = [];
|
|
86
|
+
// Start with base v4 structure
|
|
87
|
+
const v4Config = {
|
|
88
|
+
version: 4,
|
|
89
|
+
project: {
|
|
90
|
+
name: v3Config.project.name,
|
|
91
|
+
description: v3Config.project.description,
|
|
92
|
+
structure: v3Config.project.structure,
|
|
93
|
+
},
|
|
94
|
+
apps: {},
|
|
95
|
+
};
|
|
96
|
+
changes.push({
|
|
97
|
+
type: 'modify',
|
|
98
|
+
path: 'version',
|
|
99
|
+
description: 'Upgraded version from 3 to 4',
|
|
100
|
+
before: v3Config.version,
|
|
101
|
+
after: 4,
|
|
102
|
+
});
|
|
103
|
+
// Migrate infrastructure to provides
|
|
104
|
+
if (v3Config.infrastructure) {
|
|
105
|
+
v4Config.provides = {};
|
|
106
|
+
for (const [name, infra] of Object.entries(v3Config.infrastructure)) {
|
|
107
|
+
v4Config.provides[name] = migrateInfrastructure(name, infra);
|
|
108
|
+
}
|
|
109
|
+
changes.push({
|
|
110
|
+
type: 'rename',
|
|
111
|
+
path: 'infrastructure',
|
|
112
|
+
description: 'Renamed "infrastructure" to "provides"',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
// Migrate apps
|
|
116
|
+
for (const [appName, appConfig] of Object.entries(v3Config.apps || {})) {
|
|
117
|
+
const migratedApp = migrateApp(appName, appConfig, v3Config, changes, warnings);
|
|
118
|
+
v4Config.apps[appName] = migratedApp;
|
|
119
|
+
}
|
|
120
|
+
// Migrate profiles
|
|
121
|
+
if (v3Config.profiles) {
|
|
122
|
+
v4Config.profiles = {};
|
|
123
|
+
for (const [profileName, profile] of Object.entries(v3Config.profiles)) {
|
|
124
|
+
v4Config.profiles[profileName] = migrateProfile(profileName, profile, changes);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Copy over other sections
|
|
128
|
+
if (v3Config.environments) {
|
|
129
|
+
v4Config.environments = {};
|
|
130
|
+
for (const [envName, envConfig] of Object.entries(v3Config.environments)) {
|
|
131
|
+
v4Config.environments[envName] = {
|
|
132
|
+
description: envConfig.description,
|
|
133
|
+
urls: extractUrls(envConfig),
|
|
134
|
+
env: {},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (v3Config.repos) {
|
|
139
|
+
v4Config.repos = {};
|
|
140
|
+
for (const [repoName, repoConfig] of Object.entries(v3Config.repos)) {
|
|
141
|
+
v4Config.repos[repoName] = {
|
|
142
|
+
url: repoConfig.url,
|
|
143
|
+
path: repoConfig.path,
|
|
144
|
+
branch: repoConfig.branch,
|
|
145
|
+
auth: repoConfig.auth,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (v3Config.defaults) {
|
|
150
|
+
v4Config.defaults = {
|
|
151
|
+
size: v3Config.defaults.size,
|
|
152
|
+
database: v3Config.defaults.database,
|
|
153
|
+
branch: v3Config.defaults.branch,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if (v3Config.hooks) {
|
|
157
|
+
v4Config.hooks = v3Config.hooks;
|
|
158
|
+
}
|
|
159
|
+
if (v3Config.scripts) {
|
|
160
|
+
v4Config.scripts = v3Config.scripts.map(script => ({
|
|
161
|
+
name: script.name,
|
|
162
|
+
path: script.path,
|
|
163
|
+
stage: script.stage,
|
|
164
|
+
run_as: script.runAs,
|
|
165
|
+
depends_on: script.dependsOn,
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
// Add strict mode (default for v4)
|
|
169
|
+
v4Config.strict = {
|
|
170
|
+
enabled: true,
|
|
171
|
+
allow_detect: true,
|
|
172
|
+
warnings_as_errors: false,
|
|
173
|
+
};
|
|
174
|
+
changes.push({
|
|
175
|
+
type: 'add',
|
|
176
|
+
path: 'strict',
|
|
177
|
+
description: 'Added strict mode configuration (default for v4)',
|
|
178
|
+
after: v4Config.strict,
|
|
179
|
+
});
|
|
180
|
+
return { config: v4Config, changes, warnings };
|
|
181
|
+
}
|
|
182
|
+
function migrateInfrastructure(name, infra) {
|
|
183
|
+
return {
|
|
184
|
+
type: infra.type,
|
|
185
|
+
image: infra.image,
|
|
186
|
+
port: infra.port,
|
|
187
|
+
data_dir: infra.data_dir,
|
|
188
|
+
description: `Migrated from infrastructure.${name}`,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function migrateApp(appName, appConfig, v3Config, changes, warnings) {
|
|
192
|
+
const v4App = {
|
|
193
|
+
path: appConfig.path,
|
|
194
|
+
type: appConfig.type || '$detect',
|
|
195
|
+
};
|
|
196
|
+
// Migrate framework
|
|
197
|
+
if (appConfig.framework) {
|
|
198
|
+
v4App.framework = appConfig.framework;
|
|
199
|
+
}
|
|
200
|
+
// Migrate port
|
|
201
|
+
if (appConfig.port) {
|
|
202
|
+
v4App.port = appConfig.port;
|
|
203
|
+
}
|
|
204
|
+
// Migrate requires to connects_to
|
|
205
|
+
if (appConfig.requires) {
|
|
206
|
+
v4App.connects_to = {};
|
|
207
|
+
for (const [depName, requirement] of Object.entries(appConfig.requires)) {
|
|
208
|
+
v4App.connects_to[depName] = {
|
|
209
|
+
mode: 'local', // Default to local
|
|
210
|
+
required: requirement === 'required',
|
|
211
|
+
description: `Migrated from requires.${depName}`,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
changes.push({
|
|
215
|
+
type: 'rename',
|
|
216
|
+
path: `apps.${appName}.requires`,
|
|
217
|
+
description: `Converted "requires" to "connects_to" for app "${appName}"`,
|
|
218
|
+
before: appConfig.requires,
|
|
219
|
+
after: v4App.connects_to,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
// Also handle dependencies if present
|
|
223
|
+
if (appConfig.dependencies) {
|
|
224
|
+
if (!v4App.connects_to)
|
|
225
|
+
v4App.connects_to = {};
|
|
226
|
+
for (const [depName, depConfig] of Object.entries(appConfig.dependencies)) {
|
|
227
|
+
if (!v4App.connects_to[depName]) {
|
|
228
|
+
v4App.connects_to[depName] = {
|
|
229
|
+
mode: depConfig.mode,
|
|
230
|
+
required: true,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// Migrate commands
|
|
236
|
+
if (appConfig.commands) {
|
|
237
|
+
v4App.commands = appConfig.commands;
|
|
238
|
+
}
|
|
239
|
+
// Copy other fields
|
|
240
|
+
if (appConfig.repo)
|
|
241
|
+
v4App.repo = appConfig.repo;
|
|
242
|
+
if (appConfig.description)
|
|
243
|
+
v4App.description = appConfig.description;
|
|
244
|
+
if (appConfig.env)
|
|
245
|
+
v4App.env = appConfig.env;
|
|
246
|
+
if (appConfig.compose_file) {
|
|
247
|
+
v4App.compose = { file: appConfig.compose_file };
|
|
248
|
+
}
|
|
249
|
+
// Warn about missing explicit type
|
|
250
|
+
if (!appConfig.type) {
|
|
251
|
+
warnings.push(`App "${appName}" has no explicit type. Using $detect marker. ` +
|
|
252
|
+
`Run "genbox scan" then "genbox resolve" to determine the type.`);
|
|
253
|
+
}
|
|
254
|
+
return v4App;
|
|
255
|
+
}
|
|
256
|
+
function migrateProfile(profileName, profile, changes) {
|
|
257
|
+
const v4Profile = {
|
|
258
|
+
description: profile.description,
|
|
259
|
+
extends: profile.extends,
|
|
260
|
+
size: profile.size,
|
|
261
|
+
apps: profile.apps,
|
|
262
|
+
database: profile.database,
|
|
263
|
+
env: profile.env,
|
|
264
|
+
branch: profile.branch,
|
|
265
|
+
};
|
|
266
|
+
// Convert connect_to to default_connection
|
|
267
|
+
if (profile.connect_to && typeof profile.connect_to === 'string') {
|
|
268
|
+
v4Profile.default_connection = profile.connect_to;
|
|
269
|
+
changes.push({
|
|
270
|
+
type: 'rename',
|
|
271
|
+
path: `profiles.${profileName}.connect_to`,
|
|
272
|
+
description: `Converted string "connect_to" to "default_connection" in profile "${profileName}"`,
|
|
273
|
+
before: profile.connect_to,
|
|
274
|
+
after: v4Profile.default_connection,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
return v4Profile;
|
|
278
|
+
}
|
|
279
|
+
function extractUrls(envConfig) {
|
|
280
|
+
const urls = {};
|
|
281
|
+
if (envConfig.api?.url) {
|
|
282
|
+
urls['api'] = envConfig.api.url;
|
|
283
|
+
}
|
|
284
|
+
if (envConfig.api?.gateway) {
|
|
285
|
+
urls['gateway'] = envConfig.api.gateway;
|
|
286
|
+
}
|
|
287
|
+
if (envConfig.mongodb?.url) {
|
|
288
|
+
urls['mongodb'] = envConfig.mongodb.url;
|
|
289
|
+
}
|
|
290
|
+
if (envConfig.redis?.url) {
|
|
291
|
+
urls['redis'] = envConfig.redis.url;
|
|
292
|
+
}
|
|
293
|
+
if (envConfig.rabbitmq?.url) {
|
|
294
|
+
urls['rabbitmq'] = envConfig.rabbitmq.url;
|
|
295
|
+
}
|
|
296
|
+
return urls;
|
|
297
|
+
}
|
|
298
|
+
// ============================================
|
|
299
|
+
// Migration Detection
|
|
300
|
+
// ============================================
|
|
301
|
+
/**
|
|
302
|
+
* Check if a config needs migration
|
|
303
|
+
*/
|
|
304
|
+
function needsMigration(config) {
|
|
305
|
+
const version = (0, schema_v4_1.getConfigVersion)(config);
|
|
306
|
+
return version === 3 || version === 'unknown';
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Get migration summary
|
|
310
|
+
*/
|
|
311
|
+
function getMigrationSummary(v3Config) {
|
|
312
|
+
const deprecations = checkDeprecations(v3Config);
|
|
313
|
+
const estimatedChanges = [];
|
|
314
|
+
if (v3Config.infrastructure) {
|
|
315
|
+
estimatedChanges.push('Rename "infrastructure" to "provides"');
|
|
316
|
+
}
|
|
317
|
+
for (const appName of Object.keys(v3Config.apps || {})) {
|
|
318
|
+
const app = v3Config.apps[appName];
|
|
319
|
+
if (app.requires) {
|
|
320
|
+
estimatedChanges.push(`Convert "${appName}.requires" to "connects_to"`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
for (const profileName of Object.keys(v3Config.profiles || {})) {
|
|
324
|
+
const profile = v3Config.profiles[profileName];
|
|
325
|
+
if (profile.connect_to && typeof profile.connect_to === 'string') {
|
|
326
|
+
estimatedChanges.push(`Convert "${profileName}.connect_to" to "default_connection"`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
fieldsToMigrate: estimatedChanges.length,
|
|
331
|
+
deprecatedPatterns: deprecations.length,
|
|
332
|
+
estimatedChanges,
|
|
333
|
+
};
|
|
334
|
+
}
|
package/dist/profile-resolver.js
CHANGED
|
@@ -225,12 +225,17 @@ class ProfileResolver {
|
|
|
225
225
|
}
|
|
226
226
|
processedDeps.add(depName);
|
|
227
227
|
}
|
|
228
|
+
// Handle $detect markers - if still present, they should be resolved by now
|
|
229
|
+
// For now, filter them out with a warning
|
|
230
|
+
const type = appConfig.type === '$detect' ? 'backend' : appConfig.type;
|
|
231
|
+
const framework = appConfig.framework === '$detect' ? undefined : appConfig.framework;
|
|
232
|
+
const port = appConfig.port === '$detect' ? undefined : appConfig.port;
|
|
228
233
|
return {
|
|
229
234
|
name: appName,
|
|
230
235
|
path: appConfig.path,
|
|
231
|
-
type
|
|
232
|
-
framework
|
|
233
|
-
port
|
|
236
|
+
type,
|
|
237
|
+
framework,
|
|
238
|
+
port,
|
|
234
239
|
services: appConfig.services,
|
|
235
240
|
commands: appConfig.commands,
|
|
236
241
|
env: appConfig.env,
|
package/dist/schema-v3.js
CHANGED
|
@@ -10,3 +10,39 @@
|
|
|
10
10
|
* - Dependency resolution
|
|
11
11
|
*/
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.DETECTABLE_APP_FIELDS = exports.DETECT_MARKER = void 0;
|
|
14
|
+
exports.isDetectMarker = isDetectMarker;
|
|
15
|
+
// ============================================
|
|
16
|
+
// $detect Marker Support
|
|
17
|
+
// ============================================
|
|
18
|
+
/**
|
|
19
|
+
* Special marker indicating a value should be auto-detected.
|
|
20
|
+
* Used in genbox.yaml to opt-in to detection for specific fields.
|
|
21
|
+
*
|
|
22
|
+
* Example:
|
|
23
|
+
* apps:
|
|
24
|
+
* web:
|
|
25
|
+
* path: ./web
|
|
26
|
+
* type: $detect # Auto-detect from dependencies
|
|
27
|
+
* port: $detect # Auto-detect from scripts
|
|
28
|
+
* framework: $detect # Auto-detect from dependencies
|
|
29
|
+
*/
|
|
30
|
+
exports.DETECT_MARKER = '$detect';
|
|
31
|
+
/**
|
|
32
|
+
* List of fields that support $detect markers in AppConfig
|
|
33
|
+
*/
|
|
34
|
+
exports.DETECTABLE_APP_FIELDS = [
|
|
35
|
+
'type',
|
|
36
|
+
'framework',
|
|
37
|
+
'port',
|
|
38
|
+
'commands.install',
|
|
39
|
+
'commands.dev',
|
|
40
|
+
'commands.build',
|
|
41
|
+
'commands.start',
|
|
42
|
+
];
|
|
43
|
+
/**
|
|
44
|
+
* Check if a value is a $detect marker
|
|
45
|
+
*/
|
|
46
|
+
function isDetectMarker(value) {
|
|
47
|
+
return value === exports.DETECT_MARKER;
|
|
48
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Genbox Configuration Schema v4
|
|
4
|
+
*
|
|
5
|
+
* Key changes from v3:
|
|
6
|
+
* - Explicit `connects_to` replacing implicit dependency resolution
|
|
7
|
+
* - `provides` section for infrastructure definitions
|
|
8
|
+
* - `$detect` markers for opt-in auto-detection
|
|
9
|
+
* - Strict mode by default (no implicit inference)
|
|
10
|
+
* - Better separation of concerns
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.DEPRECATED_V3_FIELDS = void 0;
|
|
14
|
+
exports.isV4Config = isV4Config;
|
|
15
|
+
exports.getConfigVersion = getConfigVersion;
|
|
16
|
+
// ============================================
|
|
17
|
+
// Migration Helpers
|
|
18
|
+
// ============================================
|
|
19
|
+
/**
|
|
20
|
+
* Check if a config is v4
|
|
21
|
+
*/
|
|
22
|
+
function isV4Config(config) {
|
|
23
|
+
return config.version === 4;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get the version of a config
|
|
27
|
+
*/
|
|
28
|
+
function getConfigVersion(config) {
|
|
29
|
+
if (config.version === 4)
|
|
30
|
+
return 4;
|
|
31
|
+
if (config.version === 3 || config.version === '3.0')
|
|
32
|
+
return 3;
|
|
33
|
+
return 'unknown';
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* List of deprecated fields from v3
|
|
37
|
+
*/
|
|
38
|
+
exports.DEPRECATED_V3_FIELDS = [
|
|
39
|
+
{
|
|
40
|
+
path: 'apps.*.requires',
|
|
41
|
+
replacement: 'apps.*.connects_to',
|
|
42
|
+
message: 'Use connects_to with explicit connection modes',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
path: 'infrastructure',
|
|
46
|
+
replacement: 'provides',
|
|
47
|
+
message: 'Renamed to provides for clarity',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
path: 'profiles.*.connect_to',
|
|
51
|
+
replacement: 'profiles.*.default_connection',
|
|
52
|
+
message: 'Use default_connection or per-app connections override',
|
|
53
|
+
},
|
|
54
|
+
];
|