genbox 1.0.92 → 1.0.94
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/create.js +36 -3
- package/dist/commands/init.js +48 -3
- package/dist/profile-resolver.js +12 -4
- package/dist/scanner/compose-parser.js +28 -3
- package/dist/scanner/index.js +3 -0
- package/dist/utils/env-parser.js +56 -0
- package/package.json +1 -1
package/dist/commands/create.js
CHANGED
|
@@ -796,11 +796,15 @@ function buildPayload(resolved, config, publicKey, privateKey, configLoader) {
|
|
|
796
796
|
const appPath = config.apps[app.name]?.path || app.name;
|
|
797
797
|
const repoPath = resolved.repos.find(r => r.name === app.name)?.path ||
|
|
798
798
|
(resolved.repos[0]?.path ? `${resolved.repos[0].path}/${appPath}` : `/home/dev/${config.project.name}/${appPath}`);
|
|
799
|
+
// Get runner type to determine infrastructure URL prefix (docker vs host)
|
|
800
|
+
const runner = config.apps[app.name]?.runner || 'pm2';
|
|
801
|
+
const infraUrlMap = (0, utils_1.buildInfraUrlMap)(envVarsFromFile, runner);
|
|
802
|
+
const combinedUrlMap = { ...serviceUrlMap, ...infraUrlMap };
|
|
799
803
|
const servicesSections = Array.from(sections.keys()).filter(s => s.startsWith(`${app.name}/`));
|
|
800
804
|
if (servicesSections.length > 0) {
|
|
801
805
|
for (const serviceSectionName of servicesSections) {
|
|
802
806
|
const serviceName = serviceSectionName.split('/')[1];
|
|
803
|
-
const serviceEnvContent = (0, utils_1.buildAppEnvContent)(sections, serviceSectionName,
|
|
807
|
+
const serviceEnvContent = (0, utils_1.buildAppEnvContent)(sections, serviceSectionName, combinedUrlMap);
|
|
804
808
|
const stagingName = `${app.name}-${serviceName}.env`;
|
|
805
809
|
const targetPath = `${repoPath}/apps/${serviceName}/.env`;
|
|
806
810
|
files.push({
|
|
@@ -812,7 +816,7 @@ function buildPayload(resolved, config, publicKey, privateKey, configLoader) {
|
|
|
812
816
|
}
|
|
813
817
|
}
|
|
814
818
|
else {
|
|
815
|
-
const appEnvContent = (0, utils_1.buildAppEnvContent)(sections, app.name,
|
|
819
|
+
const appEnvContent = (0, utils_1.buildAppEnvContent)(sections, app.name, combinedUrlMap);
|
|
816
820
|
files.push({
|
|
817
821
|
path: `/home/dev/.env-staging/${app.name}.env`,
|
|
818
822
|
content: appEnvContent,
|
|
@@ -934,10 +938,39 @@ function generateSetupScript(resolved, config, envFilesToMove = []) {
|
|
|
934
938
|
lines.push(' \' "$file" | grep -v "^$" | head -n -1');
|
|
935
939
|
lines.push(' }');
|
|
936
940
|
lines.push('');
|
|
941
|
+
lines.push(' # Function to expand infrastructure URL placeholders based on runner type');
|
|
942
|
+
lines.push(' # Docker apps use DOCKER_* values, PM2/host apps use HOST_* values');
|
|
943
|
+
lines.push(' expand_infra_urls() {');
|
|
944
|
+
lines.push(' local envfile="$1"');
|
|
945
|
+
lines.push(' local runner="$2"');
|
|
946
|
+
lines.push(' if [ "$runner" = "docker" ]; then');
|
|
947
|
+
lines.push(' # For docker apps, replace ${VAR} with DOCKER_VAR value');
|
|
948
|
+
lines.push(' for var in MONGODB_URI MONGO_URI REDIS_URL REDIS_URI RABBITMQ_URL AMQP_URL DATABASE_URL DATABASE_URI; do');
|
|
949
|
+
lines.push(' docker_val=$(grep "^DOCKER_${var}=" /home/dev/.env.genbox 2>/dev/null | cut -d= -f2-)');
|
|
950
|
+
lines.push(' if [ -n "$docker_val" ]; then');
|
|
951
|
+
lines.push(' sed -i "s|\\${${var}}|${docker_val}|g" "$envfile"');
|
|
952
|
+
lines.push(' fi');
|
|
953
|
+
lines.push(' done');
|
|
954
|
+
lines.push(' else');
|
|
955
|
+
lines.push(' # For PM2/host apps, replace ${VAR} with HOST_VAR value');
|
|
956
|
+
lines.push(' for var in MONGODB_URI MONGO_URI REDIS_URL REDIS_URI RABBITMQ_URL AMQP_URL DATABASE_URL DATABASE_URI; do');
|
|
957
|
+
lines.push(' host_val=$(grep "^HOST_${var}=" /home/dev/.env.genbox 2>/dev/null | cut -d= -f2-)');
|
|
958
|
+
lines.push(' if [ -n "$host_val" ]; then');
|
|
959
|
+
lines.push(' sed -i "s|\\${${var}}|${host_val}|g" "$envfile"');
|
|
960
|
+
lines.push(' fi');
|
|
961
|
+
lines.push(' done');
|
|
962
|
+
lines.push(' fi');
|
|
963
|
+
lines.push(' }');
|
|
964
|
+
lines.push('');
|
|
937
965
|
for (const { stagingName, targetPath } of envFilesToMove) {
|
|
938
|
-
|
|
966
|
+
// Determine runner type for this app
|
|
967
|
+
// stagingName could be "appName" or "appName/serviceName" for service sections
|
|
968
|
+
const appName = stagingName.includes('/') ? stagingName.split('/')[0] : stagingName;
|
|
969
|
+
const runner = config.apps[appName]?.runner || 'pm2';
|
|
970
|
+
lines.push(` # Create .env for ${stagingName} (runner: ${runner})`);
|
|
939
971
|
lines.push(` mkdir -p "$(dirname "${targetPath}")"`);
|
|
940
972
|
lines.push(` extract_section "${stagingName}" /home/dev/.env.genbox > "${targetPath}"`);
|
|
973
|
+
lines.push(` expand_infra_urls "${targetPath}" "${runner}"`);
|
|
941
974
|
lines.push(` echo " Created ${targetPath}"`);
|
|
942
975
|
}
|
|
943
976
|
lines.push('');
|
package/dist/commands/init.js
CHANGED
|
@@ -162,12 +162,19 @@ function convertScanToDetected(scan, root) {
|
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
|
+
// Map to store git info for directories with docker apps (to preserve repo info)
|
|
166
|
+
const dockerDirGitInfo = new Map();
|
|
165
167
|
// Convert apps from package.json (skip if docker app builds from same directory)
|
|
166
168
|
const isMultiRepo = scan.structure.type === 'hybrid';
|
|
167
169
|
for (const app of scan.apps) {
|
|
168
170
|
// Skip if there's a docker app building from this directory
|
|
169
171
|
const normalizedPath = app.path.replace(/^\.?\/?/, '').replace(/\/$/, '');
|
|
170
172
|
if (dockerBuildContexts.has(normalizedPath)) {
|
|
173
|
+
// Save git info for this directory so docker apps can use it
|
|
174
|
+
const gitInfo = detectGitForDirectory(path_1.default.join(root, app.path));
|
|
175
|
+
if (gitInfo) {
|
|
176
|
+
dockerDirGitInfo.set(normalizedPath, gitInfo);
|
|
177
|
+
}
|
|
171
178
|
continue; // Docker app takes precedence
|
|
172
179
|
}
|
|
173
180
|
const mappedType = mapAppType(app.type);
|
|
@@ -187,14 +194,17 @@ function convertScanToDetected(scan, root) {
|
|
|
187
194
|
framework: app.framework,
|
|
188
195
|
framework_source: app.framework ? 'package.json dependencies' : undefined,
|
|
189
196
|
commands: app.scripts ? {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
197
|
+
// Use script NAMES (not raw commands) - PM2 runs `pnpm run <name>`
|
|
198
|
+
dev: app.scripts.dev ? 'dev' : undefined,
|
|
199
|
+
build: app.scripts.build ? 'build' : undefined,
|
|
200
|
+
start: app.scripts.start ? 'start' : undefined,
|
|
193
201
|
} : undefined,
|
|
194
202
|
dependencies: app.dependencies,
|
|
195
203
|
git: appGit,
|
|
196
204
|
};
|
|
197
205
|
}
|
|
206
|
+
// Store dockerDirGitInfo for use when adding docker apps
|
|
207
|
+
const _dockerDirGitInfo = dockerDirGitInfo;
|
|
198
208
|
// Convert infrastructure (keep all, no deduplication - user selects by source file)
|
|
199
209
|
if (scan.compose) {
|
|
200
210
|
detected.infrastructure = [];
|
|
@@ -205,6 +215,7 @@ function convertScanToDetected(scan, root) {
|
|
|
205
215
|
image: db.image || 'unknown',
|
|
206
216
|
port: db.ports?.[0]?.host || 0,
|
|
207
217
|
source: db.sourceFile || 'docker-compose.yml',
|
|
218
|
+
...(db.replicaSet && { replicaSet: db.replicaSet }),
|
|
208
219
|
});
|
|
209
220
|
}
|
|
210
221
|
for (const cache of scan.compose.caches || []) {
|
|
@@ -263,6 +274,9 @@ function convertScanToDetected(scan, root) {
|
|
|
263
274
|
continue; // Same source, skip duplicate
|
|
264
275
|
}
|
|
265
276
|
}
|
|
277
|
+
// Look up git info for this docker app's build context
|
|
278
|
+
const buildContext = dockerApp.build?.context?.replace(/^\.?\/?/, '').replace(/\/$/, '') || '';
|
|
279
|
+
const dockerAppGit = _dockerDirGitInfo.get(buildContext);
|
|
266
280
|
detected.apps[detectedAppKey] = {
|
|
267
281
|
path: dockerApp.build?.context || '.',
|
|
268
282
|
type: mappedType,
|
|
@@ -278,6 +292,7 @@ function convertScanToDetected(scan, root) {
|
|
|
278
292
|
port: dockerApp.ports?.[0]?.host,
|
|
279
293
|
port_source: `${dockerApp.sourceFile || 'docker-compose.yml'} ports`,
|
|
280
294
|
source: dockerApp.sourceFile || 'docker-compose',
|
|
295
|
+
git: dockerAppGit,
|
|
281
296
|
};
|
|
282
297
|
}
|
|
283
298
|
}
|
|
@@ -1408,6 +1423,36 @@ function generateEnvFile(projectName, detected, envVars, serviceUrlMappings) {
|
|
|
1408
1423
|
}
|
|
1409
1424
|
}
|
|
1410
1425
|
}
|
|
1426
|
+
// Add infrastructure URL mappings (HOST_ vs DOCKER_ prefixes)
|
|
1427
|
+
// These are used to differentiate between PM2 apps (use HOST_*) and Docker apps (use DOCKER_*)
|
|
1428
|
+
if (detected.infrastructure && detected.infrastructure.length > 0) {
|
|
1429
|
+
content += `\n# Infrastructure URL Configuration\n`;
|
|
1430
|
+
content += `# HOST_* = for PM2/host-based apps (uses localhost)\n`;
|
|
1431
|
+
content += `# DOCKER_* = for Docker apps (uses docker network hostnames)\n\n`;
|
|
1432
|
+
for (const infra of detected.infrastructure) {
|
|
1433
|
+
const name = infra.name.toLowerCase();
|
|
1434
|
+
const port = infra.port;
|
|
1435
|
+
if (infra.type === 'database' && infra.image?.includes('mongo')) {
|
|
1436
|
+
// MongoDB - use project name as database name
|
|
1437
|
+
const dbName = projectName.replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
|
|
1438
|
+
content += `HOST_MONGODB_URI=mongodb://localhost:${port}/${dbName}\n`;
|
|
1439
|
+
content += `DOCKER_MONGODB_URI=mongodb://${name}:27017/${dbName}\n`;
|
|
1440
|
+
content += `MONGODB_URI=\${MONGODB_URI}\n\n`;
|
|
1441
|
+
}
|
|
1442
|
+
else if (infra.type === 'cache' && infra.image?.includes('redis')) {
|
|
1443
|
+
// Redis
|
|
1444
|
+
content += `HOST_REDIS_URL=redis://localhost:${port}\n`;
|
|
1445
|
+
content += `DOCKER_REDIS_URL=redis://${name}:6379\n`;
|
|
1446
|
+
content += `REDIS_URL=\${REDIS_URL}\n\n`;
|
|
1447
|
+
}
|
|
1448
|
+
else if (infra.type === 'queue' && infra.image?.includes('rabbitmq')) {
|
|
1449
|
+
// RabbitMQ
|
|
1450
|
+
content += `HOST_RABBITMQ_URL=amqp://localhost:${port}\n`;
|
|
1451
|
+
content += `DOCKER_RABBITMQ_URL=amqp://${name}:5672\n`;
|
|
1452
|
+
content += `RABBITMQ_URL=\${RABBITMQ_URL}\n\n`;
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1411
1456
|
// Add GIT_TOKEN placeholder if not present
|
|
1412
1457
|
if (!envVars['GIT_TOKEN']) {
|
|
1413
1458
|
content += `\n# Git authentication\n# GIT_TOKEN=ghp_xxxxxxxxxxxx\n`;
|
package/dist/profile-resolver.js
CHANGED
|
@@ -98,8 +98,8 @@ class ProfileResolver {
|
|
|
98
98
|
// Step 3: Resolve dependencies
|
|
99
99
|
const { apps, infrastructure, dependencyWarnings } = await this.resolveDependencies(config, selectedApps, options, profile);
|
|
100
100
|
warnings.push(...dependencyWarnings);
|
|
101
|
-
// Step 4: Determine database mode
|
|
102
|
-
const database = await this.resolveDatabaseMode(config, options, profile);
|
|
101
|
+
// Step 4: Determine database mode (pass infrastructure for replicaSet detection)
|
|
102
|
+
const database = await this.resolveDatabaseMode(config, options, profile, infrastructure);
|
|
103
103
|
// Step 5: Determine size
|
|
104
104
|
const size = options.size || profile.size || this.inferSize(apps, infrastructure);
|
|
105
105
|
// Step 6: Build resolved config
|
|
@@ -320,7 +320,7 @@ class ProfileResolver {
|
|
|
320
320
|
/**
|
|
321
321
|
* Resolve database mode
|
|
322
322
|
*/
|
|
323
|
-
async resolveDatabaseMode(config, options, profile) {
|
|
323
|
+
async resolveDatabaseMode(config, options, profile, infrastructure) {
|
|
324
324
|
// Load env vars from .env.genbox (where init stores MongoDB URLs)
|
|
325
325
|
const envVars = this.configLoader.loadEnvVars(process.cwd());
|
|
326
326
|
// Helper to get MongoDB URL from .env.genbox
|
|
@@ -335,6 +335,9 @@ class ProfileResolver {
|
|
|
335
335
|
// Custom environments: {NAME}_MONGODB_URL
|
|
336
336
|
return envVars[`${source.toUpperCase()}_MONGODB_URL`];
|
|
337
337
|
};
|
|
338
|
+
// Extract replicaSet from MongoDB infrastructure config if present
|
|
339
|
+
const mongoInfra = infrastructure.find(i => i.type === 'database' && i.image?.toLowerCase().includes('mongo'));
|
|
340
|
+
const replicaSet = mongoInfra?.replicaSet;
|
|
338
341
|
// CLI flag takes precedence
|
|
339
342
|
if (options.db) {
|
|
340
343
|
// Normalize 'fresh' to 'local' (fresh is the UI name, local is internal)
|
|
@@ -344,6 +347,7 @@ class ProfileResolver {
|
|
|
344
347
|
mode: dbMode,
|
|
345
348
|
source,
|
|
346
349
|
url: dbMode === 'copy' ? getMongoUrl(source) : undefined,
|
|
350
|
+
replicaSet,
|
|
347
351
|
};
|
|
348
352
|
}
|
|
349
353
|
// Profile setting
|
|
@@ -354,6 +358,7 @@ class ProfileResolver {
|
|
|
354
358
|
url: profile.database.mode === 'copy' && profile.database.source
|
|
355
359
|
? getMongoUrl(profile.database.source)
|
|
356
360
|
: undefined,
|
|
361
|
+
replicaSet,
|
|
357
362
|
};
|
|
358
363
|
}
|
|
359
364
|
// If default_connection is set, use remote
|
|
@@ -365,11 +370,13 @@ class ProfileResolver {
|
|
|
365
370
|
mode: 'remote',
|
|
366
371
|
source: profileConnection,
|
|
367
372
|
url: mongoUrl,
|
|
373
|
+
replicaSet,
|
|
368
374
|
};
|
|
369
375
|
}
|
|
370
376
|
// Interactive mode
|
|
371
377
|
if (!options.yes) {
|
|
372
|
-
|
|
378
|
+
const interactiveResult = await this.selectDatabaseModeInteractive(config);
|
|
379
|
+
return { ...interactiveResult, replicaSet };
|
|
373
380
|
}
|
|
374
381
|
// Default
|
|
375
382
|
const defaultMode = config.defaults?.database?.mode || 'local';
|
|
@@ -378,6 +385,7 @@ class ProfileResolver {
|
|
|
378
385
|
mode: defaultMode,
|
|
379
386
|
source: defaultSource,
|
|
380
387
|
url: defaultMode === 'copy' && defaultSource ? getMongoUrl(defaultSource) : undefined,
|
|
388
|
+
replicaSet,
|
|
381
389
|
};
|
|
382
390
|
}
|
|
383
391
|
/**
|
|
@@ -194,21 +194,46 @@ class ComposeParser {
|
|
|
194
194
|
return files;
|
|
195
195
|
}
|
|
196
196
|
normalizeService(name, config, root, composeDir, sourceFile) {
|
|
197
|
+
const command = this.normalizeCommand(config.command);
|
|
198
|
+
const environment = this.normalizeEnvironment(config.environment);
|
|
199
|
+
const image = config.image;
|
|
197
200
|
return {
|
|
198
201
|
name,
|
|
199
|
-
image
|
|
202
|
+
image,
|
|
200
203
|
build: this.normalizeBuild(config.build, root, composeDir),
|
|
201
204
|
ports: this.normalizePorts(config.ports),
|
|
202
|
-
environment
|
|
205
|
+
environment,
|
|
203
206
|
envFile: this.normalizeEnvFile(config.env_file),
|
|
204
207
|
dependsOn: this.normalizeDependsOn(config.depends_on),
|
|
205
208
|
volumes: this.normalizeVolumes(config.volumes),
|
|
206
209
|
healthcheck: this.normalizeHealthcheck(config.healthcheck),
|
|
207
|
-
command
|
|
210
|
+
command,
|
|
208
211
|
labels: this.normalizeLabels(config.labels),
|
|
209
212
|
sourceFile,
|
|
213
|
+
replicaSet: this.detectReplicaSet(image, command, environment),
|
|
210
214
|
};
|
|
211
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Detect MongoDB replica set configuration from command or environment
|
|
218
|
+
*/
|
|
219
|
+
detectReplicaSet(image, command, environment) {
|
|
220
|
+
// Only check MongoDB services
|
|
221
|
+
if (!image || !DATABASE_PATTERNS[0].test(image)) {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
// Check command for --replSet flag
|
|
225
|
+
if (command) {
|
|
226
|
+
const replSetMatch = command.match(/--replSet[=\s]+(\S+)/);
|
|
227
|
+
if (replSetMatch) {
|
|
228
|
+
return replSetMatch[1];
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Check environment for MONGO_REPLICA_SET
|
|
232
|
+
if (environment.MONGO_REPLICA_SET) {
|
|
233
|
+
return environment.MONGO_REPLICA_SET;
|
|
234
|
+
}
|
|
235
|
+
return undefined;
|
|
236
|
+
}
|
|
212
237
|
normalizeBuild(build, root, composeDir) {
|
|
213
238
|
if (!build)
|
|
214
239
|
return undefined;
|
package/dist/scanner/index.js
CHANGED
|
@@ -133,6 +133,9 @@ class ProjectScanner {
|
|
|
133
133
|
type: 'backend', // Will be refined by framework detection
|
|
134
134
|
scripts,
|
|
135
135
|
});
|
|
136
|
+
// Also scan first-level subdirectories for additional apps
|
|
137
|
+
const subApps = await this.discoverMultiRepoApps(root, exclude);
|
|
138
|
+
apps.push(...subApps);
|
|
136
139
|
}
|
|
137
140
|
return apps;
|
|
138
141
|
}
|
package/dist/utils/env-parser.js
CHANGED
|
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
exports.parseEnvGenboxSections = parseEnvGenboxSections;
|
|
8
8
|
exports.buildServiceUrlMap = buildServiceUrlMap;
|
|
9
9
|
exports.buildAppEnvContent = buildAppEnvContent;
|
|
10
|
+
exports.buildInfraUrlMap = buildInfraUrlMap;
|
|
10
11
|
exports.parseEnvVarsFromSection = parseEnvVarsFromSection;
|
|
11
12
|
/**
|
|
12
13
|
* Parse .env.genbox file into segregated sections
|
|
@@ -106,6 +107,61 @@ function buildAppEnvContent(sections, appName, serviceUrlMap) {
|
|
|
106
107
|
.trim();
|
|
107
108
|
return envContent;
|
|
108
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Infrastructure variable patterns that need HOST_ vs DOCKER_ prefixes
|
|
112
|
+
* These are database/cache/queue connection strings that differ between
|
|
113
|
+
* host-based apps (PM2) and containerized apps (Docker)
|
|
114
|
+
*/
|
|
115
|
+
const INFRA_VAR_PATTERNS = [
|
|
116
|
+
/^(MONGODB|MONGO)_URI$/i,
|
|
117
|
+
/^(MONGODB|MONGO)_URL$/i,
|
|
118
|
+
/^(REDIS)_URL$/i,
|
|
119
|
+
/^(REDIS)_URI$/i,
|
|
120
|
+
/^(RABBITMQ|RABBIT|AMQP)_URL$/i,
|
|
121
|
+
/^(RABBITMQ|RABBIT|AMQP)_URI$/i,
|
|
122
|
+
/^(POSTGRES|POSTGRESQL|PG)_URL$/i,
|
|
123
|
+
/^(POSTGRES|POSTGRESQL|PG)_URI$/i,
|
|
124
|
+
/^DATABASE_URL$/i,
|
|
125
|
+
/^DATABASE_URI$/i,
|
|
126
|
+
];
|
|
127
|
+
/**
|
|
128
|
+
* Build a map of infrastructure URL variables based on runner type
|
|
129
|
+
* For docker apps: use DOCKER_* prefixed values (e.g., mongodb://mongodb:27017)
|
|
130
|
+
* For host/PM2 apps: use HOST_* prefixed values (e.g., mongodb://localhost:27018)
|
|
131
|
+
*
|
|
132
|
+
* Example .env.genbox:
|
|
133
|
+
* HOST_MONGODB_URI=mongodb://localhost:27018/myapp
|
|
134
|
+
* DOCKER_MONGODB_URI=mongodb://mongodb:27017/myapp
|
|
135
|
+
* MONGODB_URI=${MONGODB_URI} # Placeholder that gets expanded
|
|
136
|
+
*/
|
|
137
|
+
function buildInfraUrlMap(envVarsFromFile, runner) {
|
|
138
|
+
const urlMap = {};
|
|
139
|
+
const useDocker = runner === 'docker';
|
|
140
|
+
const prefix = useDocker ? 'DOCKER_' : 'HOST_';
|
|
141
|
+
// Find all infrastructure variables that have HOST_ or DOCKER_ prefixes
|
|
142
|
+
const infraVarNames = new Set();
|
|
143
|
+
for (const key of Object.keys(envVarsFromFile)) {
|
|
144
|
+
const match = key.match(/^(HOST|DOCKER)_(.+)$/);
|
|
145
|
+
if (match) {
|
|
146
|
+
const varName = match[2];
|
|
147
|
+
// Check if it's an infrastructure variable
|
|
148
|
+
if (INFRA_VAR_PATTERNS.some(pattern => pattern.test(varName))) {
|
|
149
|
+
infraVarNames.add(varName);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Build mapping: VARNAME → value from appropriate prefix
|
|
154
|
+
for (const varName of infraVarNames) {
|
|
155
|
+
const prefixedKey = `${prefix}${varName}`;
|
|
156
|
+
const fallbackKey = useDocker ? `HOST_${varName}` : `DOCKER_${varName}`;
|
|
157
|
+
// Use prefixed value if available, otherwise fall back
|
|
158
|
+
const value = envVarsFromFile[prefixedKey] || envVarsFromFile[fallbackKey];
|
|
159
|
+
if (value) {
|
|
160
|
+
urlMap[varName] = value;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return urlMap;
|
|
164
|
+
}
|
|
109
165
|
/**
|
|
110
166
|
* Parse env vars from a section content string
|
|
111
167
|
*/
|