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
package/dist/commands/init.js
CHANGED
|
@@ -103,13 +103,15 @@ function detectAppGitRepos(apps, rootDir) {
|
|
|
103
103
|
return repos;
|
|
104
104
|
}
|
|
105
105
|
/**
|
|
106
|
-
* Find .env files in app directories
|
|
106
|
+
* Find .env files in app directories (including nested microservices)
|
|
107
107
|
*/
|
|
108
108
|
function findAppEnvFiles(apps, rootDir) {
|
|
109
109
|
const envFiles = [];
|
|
110
110
|
const envPatterns = ['.env', '.env.local', '.env.development'];
|
|
111
111
|
for (const app of apps) {
|
|
112
112
|
const appDir = path_1.default.join(rootDir, app.path);
|
|
113
|
+
// Check for direct .env file in app directory
|
|
114
|
+
let foundDirectEnv = false;
|
|
113
115
|
for (const pattern of envPatterns) {
|
|
114
116
|
const envPath = path_1.default.join(appDir, pattern);
|
|
115
117
|
if (fs_1.default.existsSync(envPath)) {
|
|
@@ -118,7 +120,35 @@ function findAppEnvFiles(apps, rootDir) {
|
|
|
118
120
|
envFile: pattern,
|
|
119
121
|
fullPath: envPath,
|
|
120
122
|
});
|
|
121
|
-
|
|
123
|
+
foundDirectEnv = true;
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Check for nested microservices (e.g., api/apps/*)
|
|
128
|
+
const appsSubdir = path_1.default.join(appDir, 'apps');
|
|
129
|
+
if (fs_1.default.existsSync(appsSubdir) && fs_1.default.statSync(appsSubdir).isDirectory()) {
|
|
130
|
+
try {
|
|
131
|
+
const services = fs_1.default.readdirSync(appsSubdir);
|
|
132
|
+
for (const service of services) {
|
|
133
|
+
const serviceDir = path_1.default.join(appsSubdir, service);
|
|
134
|
+
if (!fs_1.default.statSync(serviceDir).isDirectory())
|
|
135
|
+
continue;
|
|
136
|
+
for (const pattern of envPatterns) {
|
|
137
|
+
const envPath = path_1.default.join(serviceDir, pattern);
|
|
138
|
+
if (fs_1.default.existsSync(envPath)) {
|
|
139
|
+
envFiles.push({
|
|
140
|
+
appName: `${app.name}/${service}`,
|
|
141
|
+
envFile: pattern,
|
|
142
|
+
fullPath: envPath,
|
|
143
|
+
isService: true,
|
|
144
|
+
});
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Ignore errors reading subdirectories
|
|
122
152
|
}
|
|
123
153
|
}
|
|
124
154
|
}
|
|
@@ -454,6 +484,20 @@ exports.initCommand = new commander_1.Command('init')
|
|
|
454
484
|
});
|
|
455
485
|
fs_1.default.writeFileSync(configPath, yamlContent);
|
|
456
486
|
console.log(chalk_1.default.green(`\n✔ Configuration saved to ${CONFIG_FILENAME}`));
|
|
487
|
+
// Add API URLs from environments to envVarsToAdd
|
|
488
|
+
// Always add LOCAL_API_URL for local development
|
|
489
|
+
envVarsToAdd['LOCAL_API_URL'] = 'http://localhost:3050';
|
|
490
|
+
if (v3Config.environments) {
|
|
491
|
+
for (const [envName, envConfig] of Object.entries(v3Config.environments)) {
|
|
492
|
+
const apiUrl = envConfig.api?.api ||
|
|
493
|
+
envConfig.api?.url ||
|
|
494
|
+
envConfig.api?.gateway;
|
|
495
|
+
if (apiUrl) {
|
|
496
|
+
const varName = `${envName.toUpperCase()}_API_URL`;
|
|
497
|
+
envVarsToAdd[varName] = apiUrl;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
457
501
|
// Generate .env.genbox
|
|
458
502
|
await setupEnvFile(projectName, v3Config, nonInteractive, scan, isMultiRepo, envVarsToAdd, overwriteExisting);
|
|
459
503
|
// Show warnings
|
|
@@ -467,26 +511,35 @@ exports.initCommand = new commander_1.Command('init')
|
|
|
467
511
|
// Show API URL guidance if environments are configured
|
|
468
512
|
if (v3Config.environments && Object.keys(v3Config.environments).length > 0) {
|
|
469
513
|
console.log('');
|
|
470
|
-
console.log(chalk_1.default.
|
|
514
|
+
console.log(chalk_1.default.blue('=== API URL Configuration ==='));
|
|
515
|
+
console.log(chalk_1.default.dim('The following API URLs were added to .env.genbox:'));
|
|
471
516
|
console.log('');
|
|
517
|
+
console.log(chalk_1.default.dim(' LOCAL_API_URL=http://localhost:3050'));
|
|
472
518
|
for (const [envName, envConfig] of Object.entries(v3Config.environments)) {
|
|
473
519
|
const apiUrl = envConfig.api?.api ||
|
|
474
520
|
envConfig.api?.url ||
|
|
475
521
|
envConfig.api?.gateway;
|
|
476
522
|
if (apiUrl) {
|
|
477
|
-
|
|
478
|
-
console.log(chalk_1.default.
|
|
479
|
-
console.log(chalk_1.default.cyan(` VITE_AUTH_SERVICE_URL="${apiUrl}"`));
|
|
480
|
-
console.log(chalk_1.default.cyan(` NEXT_PUBLIC_API_BASE_URL="${apiUrl}"`));
|
|
481
|
-
console.log('');
|
|
523
|
+
const varName = `${envName.toUpperCase()}_API_URL`;
|
|
524
|
+
console.log(chalk_1.default.dim(` ${varName}=${apiUrl}`));
|
|
482
525
|
}
|
|
483
526
|
}
|
|
527
|
+
console.log('');
|
|
528
|
+
console.log(chalk_1.default.yellow('To use dynamic API URLs:'));
|
|
529
|
+
console.log(chalk_1.default.dim(' Use ${API_URL} in your app env vars, e.g.:'));
|
|
530
|
+
console.log(chalk_1.default.cyan(' VITE_API_BASE_URL=${API_URL}'));
|
|
531
|
+
console.log(chalk_1.default.cyan(' NEXT_PUBLIC_API_URL=${API_URL}'));
|
|
532
|
+
console.log('');
|
|
533
|
+
console.log(chalk_1.default.dim(' At create time, ${API_URL} expands based on profile:'));
|
|
534
|
+
console.log(chalk_1.default.dim(' • connect_to: staging → uses STAGING_API_URL'));
|
|
535
|
+
console.log(chalk_1.default.dim(' • connect_to: production → uses PRODUCTION_API_URL'));
|
|
536
|
+
console.log(chalk_1.default.dim(' • local/no connect_to → uses LOCAL_API_URL'));
|
|
484
537
|
}
|
|
485
538
|
// Next steps
|
|
486
539
|
console.log('');
|
|
487
540
|
console.log(chalk_1.default.bold('Next steps:'));
|
|
488
541
|
console.log(chalk_1.default.dim(` 1. Review and edit ${CONFIG_FILENAME}`));
|
|
489
|
-
console.log(chalk_1.default.dim(` 2. Update
|
|
542
|
+
console.log(chalk_1.default.dim(` 2. Update ${ENV_FILENAME} to use API URL variables where needed`));
|
|
490
543
|
console.log(chalk_1.default.dim(` 3. Run 'genbox profiles' to see available profiles`));
|
|
491
544
|
console.log(chalk_1.default.dim(` 4. Run 'genbox create <name> --profile <profile>' to create an environment`));
|
|
492
545
|
}
|
|
@@ -764,7 +817,7 @@ async function setupEnvironments(scan, config, isMultiRepo = false) {
|
|
|
764
817
|
return Object.keys(environments).length > 0 ? environments : undefined;
|
|
765
818
|
}
|
|
766
819
|
/**
|
|
767
|
-
* Setup .env.genbox file
|
|
820
|
+
* Setup .env.genbox file with segregated app sections
|
|
768
821
|
*/
|
|
769
822
|
async function setupEnvFile(projectName, config, nonInteractive = false, scan, isMultiRepo = false, extraEnvVars = {}, overwriteExisting = false) {
|
|
770
823
|
const envPath = path_1.default.join(process.cwd(), ENV_FILENAME);
|
|
@@ -778,66 +831,84 @@ async function setupEnvFile(projectName, config, nonInteractive = false, scan, i
|
|
|
778
831
|
return;
|
|
779
832
|
}
|
|
780
833
|
}
|
|
834
|
+
// Build segregated content with GLOBAL section first
|
|
835
|
+
let segregatedContent = `# Genbox Environment Variables
|
|
836
|
+
# Project: ${projectName}
|
|
837
|
+
# DO NOT COMMIT THIS FILE
|
|
838
|
+
#
|
|
839
|
+
# This file uses segregated sections for each app/service.
|
|
840
|
+
# At 'genbox create' time, only GLOBAL + selected app sections are used.
|
|
841
|
+
# Use \${API_URL} for dynamic API URLs based on profile's connect_to setting.
|
|
842
|
+
|
|
843
|
+
# === GLOBAL ===
|
|
844
|
+
# These variables are always included regardless of which apps are selected
|
|
845
|
+
|
|
846
|
+
`;
|
|
847
|
+
// Add global env vars
|
|
848
|
+
for (const [key, value] of Object.entries(extraEnvVars)) {
|
|
849
|
+
segregatedContent += `${key}=${value}\n`;
|
|
850
|
+
}
|
|
851
|
+
// Add GIT authentication placeholder if not already added
|
|
852
|
+
if (!extraEnvVars['GIT_TOKEN']) {
|
|
853
|
+
segregatedContent += `# GIT_TOKEN=ghp_xxxxxxxxxxxx\n`;
|
|
854
|
+
}
|
|
855
|
+
segregatedContent += `
|
|
856
|
+
# Database URLs (used by profiles with database mode)
|
|
857
|
+
# STAGING_MONGODB_URL=mongodb+srv://user:password@staging.mongodb.net
|
|
858
|
+
# PROD_MONGODB_URL=mongodb+srv://readonly:password@prod.mongodb.net
|
|
859
|
+
|
|
860
|
+
`;
|
|
781
861
|
// For multi-repo: find env files in app directories
|
|
782
862
|
if (isMultiRepo && scan) {
|
|
783
863
|
const appEnvFiles = findAppEnvFiles(scan.apps, process.cwd());
|
|
784
864
|
if (appEnvFiles.length > 0 && !nonInteractive) {
|
|
785
865
|
console.log('');
|
|
786
866
|
console.log(chalk_1.default.blue('=== Environment Files ==='));
|
|
787
|
-
|
|
867
|
+
// Group by app type
|
|
868
|
+
const directApps = appEnvFiles.filter(e => !e.isService);
|
|
869
|
+
const serviceApps = appEnvFiles.filter(e => e.isService);
|
|
870
|
+
if (directApps.length > 0) {
|
|
871
|
+
console.log(chalk_1.default.dim(`Found ${directApps.length} app env files`));
|
|
872
|
+
}
|
|
873
|
+
if (serviceApps.length > 0) {
|
|
874
|
+
console.log(chalk_1.default.dim(`Found ${serviceApps.length} microservice env files`));
|
|
875
|
+
}
|
|
788
876
|
const envChoices = appEnvFiles.map(env => ({
|
|
789
|
-
name: `${env.appName}
|
|
877
|
+
name: env.isService ? `${env.appName} (service)` : env.appName,
|
|
790
878
|
value: env.fullPath,
|
|
791
879
|
checked: true,
|
|
792
880
|
}));
|
|
793
881
|
const selectedEnvFiles = await prompts.checkbox({
|
|
794
|
-
message: 'Select .env files to
|
|
882
|
+
message: 'Select .env files to include in .env.genbox:',
|
|
795
883
|
choices: envChoices,
|
|
796
884
|
});
|
|
797
885
|
if (selectedEnvFiles.length > 0) {
|
|
798
|
-
let mergedContent = `# Genbox Environment Variables
|
|
799
|
-
# Merged from: ${selectedEnvFiles.map(f => path_1.default.relative(process.cwd(), f)).join(', ')}
|
|
800
|
-
# DO NOT COMMIT THIS FILE
|
|
801
|
-
#
|
|
802
|
-
# Add staging/production URLs:
|
|
803
|
-
# STAGING_MONGODB_URL=mongodb+srv://...
|
|
804
|
-
# STAGING_REDIS_URL=redis://...
|
|
805
|
-
# PROD_MONGODB_URL=mongodb+srv://...
|
|
806
|
-
#
|
|
807
|
-
# Git authentication:
|
|
808
|
-
# GIT_TOKEN=ghp_xxxxxxxxxxxx
|
|
809
|
-
|
|
810
|
-
`;
|
|
811
886
|
for (const envFilePath of selectedEnvFiles) {
|
|
812
887
|
const appInfo = appEnvFiles.find(e => e.fullPath === envFilePath);
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
888
|
+
if (!appInfo)
|
|
889
|
+
continue;
|
|
890
|
+
const content = fs_1.default.readFileSync(envFilePath, 'utf8').trim();
|
|
891
|
+
// Add section header and content
|
|
892
|
+
segregatedContent += `# === ${appInfo.appName} ===\n`;
|
|
893
|
+
segregatedContent += content;
|
|
894
|
+
segregatedContent += '\n\n';
|
|
817
895
|
}
|
|
818
|
-
fs_1.default.writeFileSync(envPath, mergedContent);
|
|
819
|
-
console.log(chalk_1.default.green(`✔ Created ${ENV_FILENAME} from ${selectedEnvFiles.length} app env files`));
|
|
820
896
|
}
|
|
821
897
|
}
|
|
822
898
|
else if (appEnvFiles.length > 0 && nonInteractive) {
|
|
823
899
|
// Non-interactive: merge all env files
|
|
824
|
-
let mergedContent = `# Genbox Environment Variables
|
|
825
|
-
# Merged from app directories
|
|
826
|
-
# DO NOT COMMIT THIS FILE
|
|
827
|
-
|
|
828
|
-
`;
|
|
829
900
|
for (const envFile of appEnvFiles) {
|
|
830
|
-
const content = fs_1.default.readFileSync(envFile.fullPath, 'utf8');
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
901
|
+
const content = fs_1.default.readFileSync(envFile.fullPath, 'utf8').trim();
|
|
902
|
+
segregatedContent += `# === ${envFile.appName} ===\n`;
|
|
903
|
+
segregatedContent += content;
|
|
904
|
+
segregatedContent += '\n\n';
|
|
834
905
|
}
|
|
835
|
-
fs_1.default.writeFileSync(envPath, mergedContent);
|
|
836
|
-
console.log(chalk_1.default.green(`✔ Created ${ENV_FILENAME} from ${appEnvFiles.length} app env files`));
|
|
837
906
|
}
|
|
838
907
|
}
|
|
839
|
-
// If no env
|
|
840
|
-
|
|
908
|
+
// If no app env files found, check for root .env
|
|
909
|
+
const hasAppSections = segregatedContent.includes('# === ') &&
|
|
910
|
+
!segregatedContent.endsWith('# === GLOBAL ===\n');
|
|
911
|
+
if (!hasAppSections) {
|
|
841
912
|
const existingEnvFiles = ['.env.local', '.env', '.env.development'];
|
|
842
913
|
let existingEnvPath;
|
|
843
914
|
for (const envFile of existingEnvFiles) {
|
|
@@ -849,51 +920,28 @@ async function setupEnvFile(projectName, config, nonInteractive = false, scan, i
|
|
|
849
920
|
}
|
|
850
921
|
if (existingEnvPath) {
|
|
851
922
|
const copyExisting = nonInteractive ? true : await prompts.confirm({
|
|
852
|
-
message: `Found ${path_1.default.basename(existingEnvPath)}.
|
|
923
|
+
message: `Found ${path_1.default.basename(existingEnvPath)}. Include in ${ENV_FILENAME}?`,
|
|
853
924
|
default: true,
|
|
854
925
|
});
|
|
855
926
|
if (copyExisting) {
|
|
856
|
-
const content = fs_1.default.readFileSync(existingEnvPath, 'utf8');
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
# Add staging/production URLs:
|
|
862
|
-
# STAGING_MONGODB_URL=mongodb+srv://...
|
|
863
|
-
# STAGING_REDIS_URL=redis://...
|
|
864
|
-
# PROD_MONGODB_URL=mongodb+srv://...
|
|
865
|
-
#
|
|
866
|
-
# Git authentication:
|
|
867
|
-
# GIT_TOKEN=ghp_xxxxxxxxxxxx
|
|
868
|
-
|
|
869
|
-
`;
|
|
870
|
-
fs_1.default.writeFileSync(envPath, header + content);
|
|
871
|
-
console.log(chalk_1.default.green(`✔ Created ${ENV_FILENAME} from ${path_1.default.basename(existingEnvPath)}`));
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
else {
|
|
875
|
-
const createEnv = nonInteractive ? true : await prompts.confirm({
|
|
876
|
-
message: `Create ${ENV_FILENAME} template?`,
|
|
877
|
-
default: true,
|
|
878
|
-
});
|
|
879
|
-
if (createEnv) {
|
|
880
|
-
const template = generateEnvTemplate(projectName, config);
|
|
881
|
-
fs_1.default.writeFileSync(envPath, template);
|
|
882
|
-
console.log(chalk_1.default.green(`✔ Created ${ENV_FILENAME} template`));
|
|
927
|
+
const content = fs_1.default.readFileSync(existingEnvPath, 'utf8').trim();
|
|
928
|
+
segregatedContent += `# === root ===\n`;
|
|
929
|
+
segregatedContent += `# From ${path_1.default.basename(existingEnvPath)}\n`;
|
|
930
|
+
segregatedContent += content;
|
|
931
|
+
segregatedContent += '\n\n';
|
|
883
932
|
}
|
|
884
933
|
}
|
|
885
934
|
}
|
|
886
|
-
//
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
}
|
|
896
|
-
fs_1.default.writeFileSync(envPath, content.trim() + '\n' + extraSection);
|
|
935
|
+
// Add END marker
|
|
936
|
+
segregatedContent += `# === END ===\n`;
|
|
937
|
+
// Write the file
|
|
938
|
+
fs_1.default.writeFileSync(envPath, segregatedContent);
|
|
939
|
+
const sectionCount = (segregatedContent.match(/# === [^=]+ ===/g) || []).length - 2; // Exclude GLOBAL and END
|
|
940
|
+
if (sectionCount > 0) {
|
|
941
|
+
console.log(chalk_1.default.green(`✔ Created ${ENV_FILENAME} with ${sectionCount} app section(s)`));
|
|
942
|
+
}
|
|
943
|
+
else {
|
|
944
|
+
console.log(chalk_1.default.green(`✔ Created ${ENV_FILENAME}`));
|
|
897
945
|
}
|
|
898
946
|
// Add to .gitignore
|
|
899
947
|
const gitignorePath = path_1.default.join(process.cwd(), '.gitignore');
|
|
@@ -915,6 +963,19 @@ function generateEnvTemplate(projectName, config) {
|
|
|
915
963
|
'# DO NOT COMMIT THIS FILE',
|
|
916
964
|
'',
|
|
917
965
|
'# ============================================',
|
|
966
|
+
'# API URL CONFIGURATION',
|
|
967
|
+
'# ============================================',
|
|
968
|
+
'# Use ${API_URL} in your app env vars (e.g., VITE_API_BASE_URL=${API_URL})',
|
|
969
|
+
'# At create time, ${API_URL} expands based on profile:',
|
|
970
|
+
'# - connect_to: staging → uses STAGING_API_URL',
|
|
971
|
+
'# - connect_to: production → uses PRODUCTION_API_URL',
|
|
972
|
+
'# - local/no connect_to → uses LOCAL_API_URL',
|
|
973
|
+
'',
|
|
974
|
+
'LOCAL_API_URL=http://localhost:3050',
|
|
975
|
+
'STAGING_API_URL=https://api.staging.example.com',
|
|
976
|
+
'# PRODUCTION_API_URL=https://api.example.com',
|
|
977
|
+
'',
|
|
978
|
+
'# ============================================',
|
|
918
979
|
'# STAGING ENVIRONMENT',
|
|
919
980
|
'# ============================================',
|
|
920
981
|
'',
|
|
@@ -957,6 +1018,15 @@ function generateEnvTemplate(projectName, config) {
|
|
|
957
1018
|
'STRIPE_SECRET_KEY=sk_test_xxx',
|
|
958
1019
|
'STRIPE_WEBHOOK_SECRET=whsec_xxx',
|
|
959
1020
|
'',
|
|
1021
|
+
'# ============================================',
|
|
1022
|
+
'# APPLICATION ENV VARS',
|
|
1023
|
+
'# ============================================',
|
|
1024
|
+
'# Use ${API_URL} for dynamic API URLs',
|
|
1025
|
+
'',
|
|
1026
|
+
'# Example:',
|
|
1027
|
+
'# VITE_API_BASE_URL=${API_URL}',
|
|
1028
|
+
'# NEXT_PUBLIC_API_URL=${API_URL}',
|
|
1029
|
+
'',
|
|
960
1030
|
];
|
|
961
1031
|
return lines.join('\n');
|
|
962
1032
|
}
|
|
@@ -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
|
+
});
|