@tamyla/clodo-framework 4.0.6 → 4.0.8
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/CHANGELOG.md +17 -0
- package/dist/lib/shared/cloudflare/ops.js +59 -39
- package/dist/lib/shared/deployment/workflows/interactive-deployment-coordinator.js +8 -0
- package/dist/lib/shared/deployment/workflows/interactive-validation.js +2 -2
- package/dist/orchestration/multi-domain-orchestrator.js +18 -5
- package/dist/simple-api.js +3 -0
- package/dist/utils/deployment/wrangler-config-manager.js +31 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
## [4.0.8](https://github.com/tamylaa/clodo-framework/compare/v4.0.7...v4.0.8) (2025-12-09)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* respect user-provided worker and database names in deployment ([04ffc38](https://github.com/tamylaa/clodo-framework/commit/04ffc382ab910d4d260ba5c3f2e421763587948a))
|
|
7
|
+
|
|
8
|
+
## [4.0.7](https://github.com/tamylaa/clodo-framework/compare/v4.0.6...v4.0.7) (2025-12-09)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* correct dynamic import paths in interactive-validation.js ([acbadae](https://github.com/tamylaa/clodo-framework/commit/acbadaefc10cf7efab9dd263f7a40f8db18fccb4))
|
|
14
|
+
* correct import path in interactive-validation.js ([8355677](https://github.com/tamylaa/clodo-framework/commit/83556777a0faa1a85766c0bf4795428d8ba807e6))
|
|
15
|
+
* correct remaining dynamic import paths in interactive-validation.js ([837f0a4](https://github.com/tamylaa/clodo-framework/commit/837f0a48489dbde6b644944950232ec0f424639f))
|
|
16
|
+
* correct Windows PowerShell environment variable handling ([f3c5354](https://github.com/tamylaa/clodo-framework/commit/f3c535449f744a2448b4c1b7d98fc62853a8eaab))
|
|
17
|
+
|
|
1
18
|
## [4.0.6](https://github.com/tamylaa/clodo-framework/compare/v4.0.5...v4.0.6) (2025-12-09)
|
|
2
19
|
|
|
3
20
|
|
|
@@ -171,12 +171,18 @@ export async function listWorkers(options = {}) {
|
|
|
171
171
|
|
|
172
172
|
// Fallback to CLI-based operation
|
|
173
173
|
try {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
174
|
+
if (apiToken) {
|
|
175
|
+
const command = process.platform === 'win32' ? `powershell -Command "$env:CLOUDFLARE_API_TOKEN = '${apiToken}'; npx wrangler list"` : `CLOUDFLARE_API_TOKEN=${apiToken} npx wrangler list`;
|
|
176
|
+
const {
|
|
177
|
+
stdout: list
|
|
178
|
+
} = await executeWithRateLimit(command, 'workers');
|
|
179
|
+
return list;
|
|
180
|
+
} else {
|
|
181
|
+
const {
|
|
182
|
+
stdout: list
|
|
183
|
+
} = await executeWithRateLimit('npx wrangler list', 'workers');
|
|
184
|
+
return list;
|
|
185
|
+
}
|
|
180
186
|
} catch (error) {
|
|
181
187
|
throw new Error(`Failed to list workers: ${error.message}`);
|
|
182
188
|
}
|
|
@@ -244,40 +250,40 @@ export async function deploySecret(key, value, env = 'production', options = {})
|
|
|
244
250
|
}
|
|
245
251
|
|
|
246
252
|
// Fallback to CLI-based operation with API token if available
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
253
|
+
if (apiToken) {
|
|
254
|
+
const command = process.platform === 'win32' ? `powershell -Command "$env:CLOUDFLARE_API_TOKEN = '${apiToken}'; Write-Output '${value}' | npx wrangler secret put ${key} --env ${env}"` : `CLOUDFLARE_API_TOKEN=${apiToken} echo "${value}" | npx wrangler secret put ${key} --env ${env}`;
|
|
255
|
+
await executeWithRateLimit(command, 'workers', 5);
|
|
256
|
+
} else {
|
|
257
|
+
const command = process.platform === 'win32' ? `powershell -Command "Write-Output '${value}' | npx wrangler secret put ${key} --env ${env}"` : `echo "${value}" | npx wrangler secret put ${key} --env ${env}`;
|
|
251
258
|
await executeWithRateLimit(command, 'workers', 5);
|
|
252
|
-
} catch (error) {
|
|
253
|
-
throw new Error(`Secret deployment failed: ${error.message}`);
|
|
254
259
|
}
|
|
255
260
|
}
|
|
256
261
|
export async function deleteSecret(key, env = 'production', options = {}) {
|
|
257
262
|
const {
|
|
258
263
|
apiToken
|
|
259
264
|
} = options;
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
try {
|
|
265
|
+
if (apiToken) {
|
|
266
|
+
const command = process.platform === 'win32' ? `powershell -Command "$env:CLOUDFLARE_API_TOKEN = '${apiToken}'; npx wrangler secret delete ${key} --env ${env}"` : `CLOUDFLARE_API_TOKEN=${apiToken} npx wrangler secret delete ${key} --env ${env}`;
|
|
263
267
|
await executeWithRateLimit(command, 'workers');
|
|
264
|
-
}
|
|
265
|
-
|
|
268
|
+
} else {
|
|
269
|
+
await executeWithRateLimit(`npx wrangler secret delete ${key} --env ${env}`, 'workers');
|
|
266
270
|
}
|
|
267
271
|
}
|
|
268
272
|
export async function listSecrets(env = 'production', options = {}) {
|
|
269
273
|
const {
|
|
270
274
|
apiToken
|
|
271
275
|
} = options;
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
try {
|
|
276
|
+
if (apiToken) {
|
|
277
|
+
const command = process.platform === 'win32' ? `powershell -Command "$env:CLOUDFLARE_API_TOKEN = '${apiToken}'; npx wrangler secret list --env ${env}"` : `CLOUDFLARE_API_TOKEN=${apiToken} npx wrangler secret list --env ${env}`;
|
|
275
278
|
const {
|
|
276
279
|
stdout: list
|
|
277
280
|
} = await executeWithRateLimit(command, 'workers');
|
|
278
281
|
return list;
|
|
279
|
-
}
|
|
280
|
-
|
|
282
|
+
} else {
|
|
283
|
+
const {
|
|
284
|
+
stdout: list
|
|
285
|
+
} = await executeWithRateLimit(`npx wrangler secret list --env ${env}`, 'workers');
|
|
286
|
+
return list;
|
|
281
287
|
}
|
|
282
288
|
}
|
|
283
289
|
export async function listDatabases(options = {}) {
|
|
@@ -346,16 +352,26 @@ export async function createDatabase(name, options = {}) {
|
|
|
346
352
|
|
|
347
353
|
// Fallback to CLI-based operation
|
|
348
354
|
try {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
355
|
+
if (apiToken) {
|
|
356
|
+
const command = process.platform === 'win32' ? `powershell -Command "$env:CLOUDFLARE_API_TOKEN = '${apiToken}'; npx wrangler d1 create ${name}"` : `CLOUDFLARE_API_TOKEN=${apiToken} npx wrangler d1 create ${name}`;
|
|
357
|
+
const {
|
|
358
|
+
stdout: output
|
|
359
|
+
} = await executeWithRateLimit(command, 'd1');
|
|
360
|
+
const idMatch = output.match(/database_id = "([^"]+)"/);
|
|
361
|
+
if (!idMatch) {
|
|
362
|
+
throw new Error('Could not extract database ID from creation output');
|
|
363
|
+
}
|
|
364
|
+
return idMatch[1];
|
|
365
|
+
} else {
|
|
366
|
+
const {
|
|
367
|
+
stdout: output
|
|
368
|
+
} = await executeWithRateLimit(`npx wrangler d1 create ${name}`, 'd1');
|
|
369
|
+
const idMatch = output.match(/database_id = "([^"]+)"/);
|
|
370
|
+
if (!idMatch) {
|
|
371
|
+
throw new Error('Could not extract database ID from creation output');
|
|
372
|
+
}
|
|
373
|
+
return idMatch[1];
|
|
357
374
|
}
|
|
358
|
-
return idMatch[1];
|
|
359
375
|
} catch (error) {
|
|
360
376
|
throw new Error(`Database creation failed: ${error.message}`);
|
|
361
377
|
}
|
|
@@ -364,12 +380,13 @@ export async function deleteDatabase(name, options = {}) {
|
|
|
364
380
|
const {
|
|
365
381
|
apiToken
|
|
366
382
|
} = options;
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
383
|
+
if (apiToken) {
|
|
384
|
+
// For Windows PowerShell, set environment variable within the command
|
|
385
|
+
const command = process.platform === 'win32' ? `powershell -Command "$env:CLOUDFLARE_API_TOKEN = '${apiToken}'; npx wrangler d1 delete ${name} --skip-confirmation"` : `CLOUDFLARE_API_TOKEN=${apiToken} npx wrangler d1 delete ${name} --skip-confirmation`;
|
|
370
386
|
await executeWithRateLimit(command, 'd1');
|
|
371
|
-
}
|
|
372
|
-
|
|
387
|
+
} else {
|
|
388
|
+
// No API token provided, use default wrangler auth
|
|
389
|
+
await executeWithRateLimit(`npx wrangler d1 delete ${name} --skip-confirmation`, 'd1');
|
|
373
390
|
}
|
|
374
391
|
}
|
|
375
392
|
export async function runMigrations(databaseName, env = 'production', options = {}) {
|
|
@@ -380,9 +397,12 @@ export async function runMigrations(databaseName, env = 'production', options =
|
|
|
380
397
|
try {
|
|
381
398
|
await ensureMonitoringInitialized();
|
|
382
399
|
const result = await errorRecovery.executeWithRecovery(async () => {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
400
|
+
if (apiToken) {
|
|
401
|
+
const command = process.platform === 'win32' ? `powershell -Command "$env:CLOUDFLARE_API_TOKEN = '${apiToken}'; npx wrangler d1 migrations apply ${databaseName} --env ${env} --remote"` : `CLOUDFLARE_API_TOKEN=${apiToken} npx wrangler d1 migrations apply ${databaseName} --env ${env} --remote`;
|
|
402
|
+
await executeWithRateLimit(command, 'd1');
|
|
403
|
+
} else {
|
|
404
|
+
await executeWithRateLimit(`npx wrangler d1 migrations apply ${databaseName} --env ${env} --remote`, 'd1');
|
|
405
|
+
}
|
|
386
406
|
return true;
|
|
387
407
|
}, {
|
|
388
408
|
operationId: `runMigrations_${databaseName}_${env}`
|
|
@@ -209,11 +209,19 @@ export class InteractiveDeploymentCoordinator {
|
|
|
209
209
|
async executeDeployment() {
|
|
210
210
|
console.log('🚀 Phase 6: Executing Deployment');
|
|
211
211
|
console.log('─'.repeat(50));
|
|
212
|
+
const workerName = this.deploymentState.config.worker?.name;
|
|
213
|
+
const databaseName = this.deploymentState.resources.database?.name;
|
|
214
|
+
console.log(` 🔍 DEBUG: workerName from state: "${workerName}"`);
|
|
215
|
+
console.log(` 🔍 DEBUG: databaseName from state: "${databaseName}"`);
|
|
212
216
|
const result = await Clodo.deploy({
|
|
213
217
|
servicePath: this.deploymentState.config.servicePath,
|
|
214
218
|
environment: this.deploymentState.config.environment,
|
|
215
219
|
domain: this.deploymentState.config.credentials.zoneName,
|
|
216
220
|
serviceName: this.options.serviceName,
|
|
221
|
+
workerName: workerName,
|
|
222
|
+
// Pass the collected worker name
|
|
223
|
+
databaseName: databaseName,
|
|
224
|
+
// Pass the collected database name
|
|
217
225
|
dryRun: this.deploymentState.config.dryRun,
|
|
218
226
|
credentials: this.deploymentState.config.credentials
|
|
219
227
|
});
|
|
@@ -129,7 +129,7 @@ export class InteractiveValidationWorkflow {
|
|
|
129
129
|
console.log('===========================');
|
|
130
130
|
const {
|
|
131
131
|
askChoice
|
|
132
|
-
} = await import('
|
|
132
|
+
} = await import('../../utils/interactive-prompts.js');
|
|
133
133
|
const choice = await askChoice('How would you like to handle the existing worker?', ['🔄 Overwrite/Update - Deploy new version (recommended)', '📝 Rename - Create with a different worker name', '🔍 Compare - Show differences before deciding', '💾 Backup & Update - Create backup before overwriting', '❌ Cancel - Stop deployment'], 0);
|
|
134
134
|
switch (choice) {
|
|
135
135
|
case 0:
|
|
@@ -168,7 +168,7 @@ export class InteractiveValidationWorkflow {
|
|
|
168
168
|
async promptForNewWorkerName(currentName) {
|
|
169
169
|
const {
|
|
170
170
|
askUser
|
|
171
|
-
} = await import('
|
|
171
|
+
} = await import('../../utils/interactive-prompts.js');
|
|
172
172
|
let newName = await askUser(`Enter new worker name (current: ${currentName})`);
|
|
173
173
|
newName = newName.trim();
|
|
174
174
|
if (!newName) {
|
|
@@ -36,6 +36,8 @@ export class MultiDomainOrchestrator {
|
|
|
36
36
|
this.parallelDeployments = options.parallelDeployments || 3;
|
|
37
37
|
this.servicePath = options.servicePath || process.cwd();
|
|
38
38
|
this.serviceName = options.serviceName || 'data-service'; // Service name for custom domain (e.g., 'data-service', 'auth-service')
|
|
39
|
+
this.workerName = options.workerName; // Specific worker name to use (optional)
|
|
40
|
+
this.databaseName = options.databaseName; // Specific database name to use (optional)
|
|
39
41
|
|
|
40
42
|
// Wrangler config path - allows using customer-specific wrangler.toml files
|
|
41
43
|
// If not specified, wrangler uses the default wrangler.toml in servicePath
|
|
@@ -299,7 +301,7 @@ export class MultiDomainOrchestrator {
|
|
|
299
301
|
console.log(` 🗄️ Setting up database for ${domain}`);
|
|
300
302
|
if (this.dryRun) {
|
|
301
303
|
console.log(` � DRY RUN: Would create database for ${domain}`);
|
|
302
|
-
const databaseName = `${domain.replace(/\./g, '-')}-${this.environment}-db`;
|
|
304
|
+
const databaseName = this.databaseName || `${domain.replace(/\./g, '-')}-${this.environment}-db`;
|
|
303
305
|
return {
|
|
304
306
|
databaseName,
|
|
305
307
|
databaseId: 'dry-run-id',
|
|
@@ -308,7 +310,8 @@ export class MultiDomainOrchestrator {
|
|
|
308
310
|
}
|
|
309
311
|
try {
|
|
310
312
|
// Create D1 database using Cloudflare ops
|
|
311
|
-
|
|
313
|
+
// Use provided database name, or generate one based on domain/environment
|
|
314
|
+
const databaseName = this.databaseName || `${domain.replace(/\./g, '-')}-${this.environment}-db`;
|
|
312
315
|
|
|
313
316
|
// Check if database already exists
|
|
314
317
|
console.log(` � Checking if database exists: ${databaseName}`);
|
|
@@ -540,11 +543,13 @@ export class MultiDomainOrchestrator {
|
|
|
540
543
|
// 2. Root wrangler.toml is ephemeral (reflects current active deployment)
|
|
541
544
|
if (this.cloudflareZoneName) {
|
|
542
545
|
console.log(` 🔧 Preparing customer config for zone: ${this.cloudflareZoneName}`);
|
|
546
|
+
console.log(` 🔍 DEBUG: this.workerName is "${this.workerName}"`);
|
|
543
547
|
|
|
544
548
|
// Generate or update customer config with current deployment parameters
|
|
545
549
|
const customerConfigPath = await this.wranglerConfigManager.generateCustomerConfig(this.cloudflareZoneName, {
|
|
546
550
|
accountId: this.cloudflareAccountId,
|
|
547
|
-
environment: this.environment
|
|
551
|
+
environment: this.environment,
|
|
552
|
+
workerName: this.workerName // Pass specific worker name if provided
|
|
548
553
|
});
|
|
549
554
|
|
|
550
555
|
// Copy customer config to root (ephemeral working copy for this deployment)
|
|
@@ -763,12 +768,14 @@ export class MultiDomainOrchestrator {
|
|
|
763
768
|
}
|
|
764
769
|
|
|
765
770
|
/**
|
|
766
|
-
*
|
|
767
|
-
* @param {Object} options -
|
|
771
|
+
* Deploy a single service to a specific domain/environment
|
|
772
|
+
* @param {Object} options - Deployment options
|
|
768
773
|
* @param {string} options.servicePath - Path to service directory
|
|
769
774
|
* @param {string} options.environment - Target environment
|
|
770
775
|
* @param {string} options.domain - Specific domain to deploy to (used as zone name and domain suffix)
|
|
771
776
|
* @param {string} options.serviceName - Service name for URL generation (e.g., 'data-service', 'auth-service')
|
|
777
|
+
* @param {string} options.workerName - Specific worker name to use
|
|
778
|
+
* @param {string} options.databaseName - Specific database name to use
|
|
772
779
|
* @param {boolean} options.dryRun - Simulate deployment
|
|
773
780
|
* @param {Object} options.credentials - Cloudflare credentials
|
|
774
781
|
* @returns {Promise<Object>} Deployment result
|
|
@@ -779,6 +786,8 @@ export class MultiDomainOrchestrator {
|
|
|
779
786
|
environment = 'production',
|
|
780
787
|
domain,
|
|
781
788
|
serviceName,
|
|
789
|
+
workerName,
|
|
790
|
+
databaseName,
|
|
782
791
|
dryRun = false,
|
|
783
792
|
credentials = {}
|
|
784
793
|
} = options;
|
|
@@ -790,6 +799,10 @@ export class MultiDomainOrchestrator {
|
|
|
790
799
|
servicePath,
|
|
791
800
|
serviceName,
|
|
792
801
|
// Pass through serviceName if provided
|
|
802
|
+
workerName,
|
|
803
|
+
// Pass through workerName if provided
|
|
804
|
+
databaseName,
|
|
805
|
+
// Pass through databaseName if provided
|
|
793
806
|
cloudflareToken: credentials.token,
|
|
794
807
|
cloudflareAccountId: credentials.accountId,
|
|
795
808
|
cloudflareZoneId: credentials.zoneId,
|
package/dist/simple-api.js
CHANGED
|
@@ -34,6 +34,9 @@ export class Clodo {
|
|
|
34
34
|
* @param {string} options.servicePath - Path to service directory
|
|
35
35
|
* @param {string} options.environment - Target environment
|
|
36
36
|
* @param {string} options.domain - Specific domain to deploy to
|
|
37
|
+
* @param {string} options.serviceName - Service name for URL generation
|
|
38
|
+
* @param {string} options.workerName - Specific worker name to use
|
|
39
|
+
* @param {string} options.databaseName - Specific database name to use
|
|
37
40
|
* @param {boolean} options.dryRun - Simulate deployment
|
|
38
41
|
* @returns {Promise<Object>} Deployment result
|
|
39
42
|
*/
|
|
@@ -127,13 +127,16 @@ export class WranglerConfigManager {
|
|
|
127
127
|
* @param {Object} params - Deployment parameters
|
|
128
128
|
* @param {string} params.accountId - Cloudflare account ID
|
|
129
129
|
* @param {string} params.environment - Deployment environment (production, staging, development)
|
|
130
|
+
* @param {string} params.workerName - Specific worker name to use (optional, overrides automatic naming)
|
|
130
131
|
* @returns {Promise<string>} Path to generated/updated customer config
|
|
131
132
|
*/
|
|
132
133
|
async generateCustomerConfig(zoneName, params = {}) {
|
|
133
134
|
const {
|
|
134
135
|
accountId,
|
|
135
|
-
environment = 'production'
|
|
136
|
+
environment = 'production',
|
|
137
|
+
workerName
|
|
136
138
|
} = params;
|
|
139
|
+
console.log(` 🔍 DEBUG: generateCustomerConfig called with workerName: ${workerName}`);
|
|
137
140
|
if (!zoneName) {
|
|
138
141
|
throw new Error('Zone name is required to generate customer config');
|
|
139
142
|
}
|
|
@@ -186,16 +189,25 @@ export class WranglerConfigManager {
|
|
|
186
189
|
console.log(` ✅ Set account_id to ${accountId} in customer config`);
|
|
187
190
|
}
|
|
188
191
|
|
|
189
|
-
// Update worker name based on zone
|
|
192
|
+
// Update worker name based on zone or provided name
|
|
190
193
|
// Extract base service name and apply zone-based naming
|
|
191
194
|
const customerPrefix = zoneName.split('.')[0]; // clodo.dev → clodo
|
|
192
195
|
|
|
193
196
|
// Update root-level worker name
|
|
194
197
|
if (config.name) {
|
|
195
|
-
|
|
196
|
-
|
|
198
|
+
let newWorkerName;
|
|
199
|
+
console.log(` 🔍 DEBUG: config.name is "${config.name}", workerName param is "${workerName}"`);
|
|
200
|
+
if (workerName) {
|
|
201
|
+
// Use the provided worker name directly
|
|
202
|
+
newWorkerName = workerName;
|
|
203
|
+
console.log(` ✅ Set worker name to ${newWorkerName} in customer config (provided)`);
|
|
204
|
+
} else {
|
|
205
|
+
// Apply automatic zone-based naming
|
|
206
|
+
const baseName = config.name.replace(/^[^-]+-/, ''); // Remove existing prefix
|
|
207
|
+
newWorkerName = `${customerPrefix}-${baseName}`;
|
|
208
|
+
console.log(` ✅ Set worker name to ${newWorkerName} in customer config (auto-generated)`);
|
|
209
|
+
}
|
|
197
210
|
config.name = newWorkerName;
|
|
198
|
-
console.log(` ✅ Set worker name to ${newWorkerName} in customer config`);
|
|
199
211
|
}
|
|
200
212
|
|
|
201
213
|
// Update SERVICE_DOMAIN in environment variables (all environments)
|
|
@@ -227,9 +239,20 @@ export class WranglerConfigManager {
|
|
|
227
239
|
if (config.env) {
|
|
228
240
|
for (const [envName, envConfig] of Object.entries(config.env)) {
|
|
229
241
|
if (envConfig.name) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
242
|
+
let newEnvWorkerName;
|
|
243
|
+
if (workerName) {
|
|
244
|
+
// Derive environment-specific name from provided worker name
|
|
245
|
+
const baseName = workerName.replace(/-service$/, ''); // Remove -service suffix if present
|
|
246
|
+
const envSuffix = envName === 'production' ? '' : `-${envName}`;
|
|
247
|
+
newEnvWorkerName = `${baseName}${envSuffix}`;
|
|
248
|
+
console.log(` ✅ Set [env.${envName}] worker name to ${newEnvWorkerName} (derived from provided name)`);
|
|
249
|
+
} else {
|
|
250
|
+
// Apply automatic zone-based naming
|
|
251
|
+
const baseName = envConfig.name.replace(/^[^-]+-/, ''); // Remove existing prefix
|
|
252
|
+
newEnvWorkerName = `${customerPrefix}-${baseName}`;
|
|
253
|
+
console.log(` ✅ Set [env.${envName}] worker name to ${newEnvWorkerName} (auto-generated)`);
|
|
254
|
+
}
|
|
255
|
+
envConfig.name = newEnvWorkerName;
|
|
233
256
|
}
|
|
234
257
|
if (envConfig.vars) {
|
|
235
258
|
updateServiceDomain(envConfig.vars);
|