@swovohq/fuel 0.2.2-alpha.90 → 0.2.2-alpha.91
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/README.md +107 -107
- package/dist/bin/fuel.js +2 -2
- package/dist/cleanup/finalize.d.ts.map +1 -1
- package/dist/cleanup/finalize.js +11 -3
- package/dist/cleanup/finalize.js.map +1 -1
- package/dist/commands/config-verify.d.ts.map +1 -1
- package/dist/commands/config-verify.js.map +1 -1
- package/dist/commands/create-app.d.ts.map +1 -1
- package/dist/commands/create-app.js +6 -4
- package/dist/commands/create-app.js.map +1 -1
- package/dist/commands/infra-deploy.d.ts.map +1 -1
- package/dist/commands/infra-deploy.js +37 -20
- package/dist/commands/infra-deploy.js.map +1 -1
- package/dist/commands/infra-destroy.d.ts.map +1 -1
- package/dist/commands/infra-destroy.js +1 -1
- package/dist/commands/infra-destroy.js.map +1 -1
- package/dist/commands/infra-init.d.ts.map +1 -1
- package/dist/commands/infra-init.js +168 -67
- package/dist/commands/infra-init.js.map +1 -1
- package/dist/commands/infra-pull.d.ts.map +1 -1
- package/dist/commands/infra-pull.js +2 -1
- package/dist/commands/infra-pull.js.map +1 -1
- package/dist/commands/list-modules.d.ts.map +1 -1
- package/dist/commands/list-modules.js +10 -3
- package/dist/commands/list-modules.js.map +1 -1
- package/dist/engines/convention.d.ts.map +1 -1
- package/dist/engines/convention.js +2 -2
- package/dist/engines/convention.js.map +1 -1
- package/dist/engines/dependencies.d.ts.map +1 -1
- package/dist/engines/dependencies.js.map +1 -1
- package/dist/engines/monorepo.d.ts.map +1 -1
- package/dist/engines/monorepo.js.map +1 -1
- package/dist/engines/template-source.d.ts.map +1 -1
- package/dist/engines/template-source.js +3 -1
- package/dist/engines/template-source.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/infra/azure-defaults.js +8 -8
- package/dist/infra/azure-keyvault-client.d.ts.map +1 -1
- package/dist/infra/azure-keyvault-client.js.map +1 -1
- package/dist/infra/azure-rbac.d.ts.map +1 -1
- package/dist/infra/azure-rbac.js +3 -1
- package/dist/infra/azure-rbac.js.map +1 -1
- package/dist/infra/azure-state-storage.d.ts.map +1 -1
- package/dist/infra/azure-state-storage.js +9 -9
- package/dist/infra/azure-state-storage.js.map +1 -1
- package/dist/infra/config-loader.d.ts.map +1 -1
- package/dist/infra/config-loader.js.map +1 -1
- package/dist/infra/config-writer.js +1 -1
- package/dist/infra/credentials.d.ts.map +1 -1
- package/dist/infra/credentials.js.map +1 -1
- package/dist/infra/dependency-checker.d.ts.map +1 -1
- package/dist/infra/dependency-checker.js.map +1 -1
- package/dist/infra/deployment-detect.d.ts.map +1 -1
- package/dist/infra/deployment-detect.js.map +1 -1
- package/dist/infra/git-client.d.ts.map +1 -1
- package/dist/infra/git-client.js.map +1 -1
- package/dist/infra/github-client.d.ts.map +1 -1
- package/dist/infra/github-client.js +3 -3
- package/dist/infra/github-client.js.map +1 -1
- package/dist/infra/github-secrets-client.d.ts.map +1 -1
- package/dist/infra/github-secrets-client.js +2 -2
- package/dist/infra/github-secrets-client.js.map +1 -1
- package/dist/infra/github-token-link.d.ts.map +1 -1
- package/dist/infra/github-token-link.js +6 -2
- package/dist/infra/github-token-link.js.map +1 -1
- package/dist/infra/orchestrator.d.ts.map +1 -1
- package/dist/infra/orchestrator.js +20 -15
- package/dist/infra/orchestrator.js.map +1 -1
- package/dist/infra/port-utils.d.ts.map +1 -1
- package/dist/infra/port-utils.js.map +1 -1
- package/dist/infra/preflight.d.ts.map +1 -1
- package/dist/infra/preflight.js +9 -3
- package/dist/infra/preflight.js.map +1 -1
- package/dist/infra/s3-state-bucket.d.ts.map +1 -1
- package/dist/infra/s3-state-bucket.js +8 -4
- package/dist/infra/s3-state-bucket.js.map +1 -1
- package/dist/infra/secrets-manager-client.d.ts.map +1 -1
- package/dist/infra/secrets-manager-client.js +8 -5
- package/dist/infra/secrets-manager-client.js.map +1 -1
- package/dist/infra/subprocess.js +1 -1
- package/dist/infra/template-engine.d.ts.map +1 -1
- package/dist/infra/template-engine.js +17 -5
- package/dist/infra/template-engine.js.map +1 -1
- package/dist/infra/tofu-runner.d.ts.map +1 -1
- package/dist/infra/tofu-runner.js +8 -11
- package/dist/infra/tofu-runner.js.map +1 -1
- package/dist/infra/types.d.ts +1 -1
- package/dist/infra/types.d.ts.map +1 -1
- package/dist/infra/types.js +299 -91
- package/dist/infra/types.js.map +1 -1
- package/dist/infra-templates/TODO-azure-prod.md +16 -8
- package/dist/infra-templates/templates/azure/MANUAL_DEPLOY.md +1 -1
- package/dist/infra-templates/templates/ecs/README.md +92 -82
- package/package.json +1 -1
|
@@ -58,11 +58,16 @@ function httpsGetJson(url, headers) {
|
|
|
58
58
|
return new Promise((resolve, reject) => {
|
|
59
59
|
const req = https.get(url, { headers }, (res) => {
|
|
60
60
|
let body = '';
|
|
61
|
-
res.on('data', (chunk) => {
|
|
61
|
+
res.on('data', (chunk) => {
|
|
62
|
+
body += chunk.toString();
|
|
63
|
+
});
|
|
62
64
|
res.on('end', () => resolve({ status: res.statusCode ?? 0, body }));
|
|
63
65
|
});
|
|
64
66
|
req.on('error', reject);
|
|
65
|
-
req.setTimeout(10000, () => {
|
|
67
|
+
req.setTimeout(10000, () => {
|
|
68
|
+
req.destroy();
|
|
69
|
+
reject(new Error('Request timed out'));
|
|
70
|
+
});
|
|
66
71
|
});
|
|
67
72
|
}
|
|
68
73
|
exports.CONFIG_TEMPLATE = {
|
|
@@ -86,7 +91,7 @@ exports.CONFIG_TEMPLATE = {
|
|
|
86
91
|
db_instance_type: 'db.t3.small',
|
|
87
92
|
db_allocated_storage: 30,
|
|
88
93
|
variables: {},
|
|
89
|
-
workers: []
|
|
94
|
+
workers: []
|
|
90
95
|
},
|
|
91
96
|
{
|
|
92
97
|
name: 'my-app-web',
|
|
@@ -99,10 +104,10 @@ exports.CONFIG_TEMPLATE = {
|
|
|
99
104
|
cpu_units: 1024,
|
|
100
105
|
memory_units: 2048,
|
|
101
106
|
build_env: {
|
|
102
|
-
NEXT_PUBLIC_API_HOST: 'https://api.example.com'
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
]
|
|
107
|
+
NEXT_PUBLIC_API_HOST: 'https://api.example.com'
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
]
|
|
106
111
|
};
|
|
107
112
|
exports.CONFIG_TEMPLATE_AZURE = {
|
|
108
113
|
name: 'my-app',
|
|
@@ -111,7 +116,12 @@ exports.CONFIG_TEMPLATE_AZURE = {
|
|
|
111
116
|
location: 'eastus2',
|
|
112
117
|
monitor: { daily_data_cap_gb: 1, retention_days: 90, dashboard_count: 0 },
|
|
113
118
|
front_door: { sku_name: 'Standard_AzureFrontDoor' },
|
|
114
|
-
storage_account: {
|
|
119
|
+
storage_account: {
|
|
120
|
+
account_kind: 'BlockBlobStorage',
|
|
121
|
+
account_tier: 'Premium',
|
|
122
|
+
account_replication_type: 'LRS',
|
|
123
|
+
access_tier: 'Hot'
|
|
124
|
+
},
|
|
115
125
|
key_vault: { sku_name: 'standard' },
|
|
116
126
|
apps: [
|
|
117
127
|
{
|
|
@@ -124,9 +134,15 @@ exports.CONFIG_TEMPLATE_AZURE = {
|
|
|
124
134
|
branch: 'main',
|
|
125
135
|
database: { enabled: true, engine: 'mssql', sku_name: 'GP_S_Gen5_4' },
|
|
126
136
|
redis: { enabled: false, sku_name: 'Balanced_B0' },
|
|
127
|
-
container_app: {
|
|
137
|
+
container_app: {
|
|
138
|
+
cpu: 1,
|
|
139
|
+
memory: '2Gi',
|
|
140
|
+
concurrent_requests: 20,
|
|
141
|
+
min_replicas: 0,
|
|
142
|
+
max_replicas: 3
|
|
143
|
+
},
|
|
128
144
|
variables: {},
|
|
129
|
-
workers: []
|
|
145
|
+
workers: []
|
|
130
146
|
},
|
|
131
147
|
{
|
|
132
148
|
name: 'my-app-web',
|
|
@@ -137,10 +153,10 @@ exports.CONFIG_TEMPLATE_AZURE = {
|
|
|
137
153
|
port: 3000,
|
|
138
154
|
branch: 'main',
|
|
139
155
|
build_env: {
|
|
140
|
-
NEXT_PUBLIC_API_HOST: 'https://api.example.com'
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
]
|
|
156
|
+
NEXT_PUBLIC_API_HOST: 'https://api.example.com'
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
]
|
|
144
160
|
};
|
|
145
161
|
// --- Private prompt helpers ---
|
|
146
162
|
async function ask(rl, label, defaultVal) {
|
|
@@ -238,7 +254,7 @@ async function askGitHubOrganizationFor(rl, token) {
|
|
|
238
254
|
const { status, body } = await httpsGetJson('https://api.github.com/user/orgs', {
|
|
239
255
|
Authorization: `Bearer ${token}`,
|
|
240
256
|
'User-Agent': 'fuel-cli',
|
|
241
|
-
Accept: 'application/vnd.github+json'
|
|
257
|
+
Accept: 'application/vnd.github+json'
|
|
242
258
|
});
|
|
243
259
|
if (status === 200) {
|
|
244
260
|
const orgs = JSON.parse(body);
|
|
@@ -262,12 +278,15 @@ async function tryGetGithubUser(credentials) {
|
|
|
262
278
|
const { status, body } = await httpsGetJson('https://api.github.com/user', {
|
|
263
279
|
Authorization: `Bearer ${credentials.GITHUB_TOKEN}`,
|
|
264
280
|
'User-Agent': 'fuel-cli',
|
|
265
|
-
Accept: 'application/vnd.github+json'
|
|
281
|
+
Accept: 'application/vnd.github+json'
|
|
266
282
|
});
|
|
267
283
|
const data = JSON.parse(body);
|
|
268
284
|
if (status !== 200)
|
|
269
285
|
return { error: data.message ?? `HTTP ${status}` };
|
|
270
|
-
return {
|
|
286
|
+
return {
|
|
287
|
+
login: data.login ?? '(unknown)',
|
|
288
|
+
email: data.email ?? '(private)'
|
|
289
|
+
};
|
|
271
290
|
}
|
|
272
291
|
catch (err) {
|
|
273
292
|
return { error: err instanceof Error ? err.message : String(err) };
|
|
@@ -282,7 +301,7 @@ async function tryGetGithubOrg(credentials) {
|
|
|
282
301
|
const { status, body } = await httpsGetJson(`https://api.github.com/orgs/${encodeURIComponent(org)}`, {
|
|
283
302
|
Authorization: `Bearer ${credentials.GITHUB_TOKEN}`,
|
|
284
303
|
'User-Agent': 'fuel-cli',
|
|
285
|
-
Accept: 'application/vnd.github+json'
|
|
304
|
+
Accept: 'application/vnd.github+json'
|
|
286
305
|
});
|
|
287
306
|
const data = JSON.parse(body);
|
|
288
307
|
if (status !== 200)
|
|
@@ -301,9 +320,9 @@ async function tryGetAwsAccountId(credentials) {
|
|
|
301
320
|
...process.env,
|
|
302
321
|
AWS_ACCESS_KEY_ID: credentials.AWS_ACCESS_KEY_ID,
|
|
303
322
|
AWS_SECRET_ACCESS_KEY: credentials.AWS_SECRET_ACCESS_KEY,
|
|
304
|
-
AWS_REGION: credentials.AWS_REGION
|
|
323
|
+
AWS_REGION: credentials.AWS_REGION
|
|
305
324
|
},
|
|
306
|
-
timeout: 10000
|
|
325
|
+
timeout: 10000
|
|
307
326
|
});
|
|
308
327
|
const accountId = JSON.parse(stdout).Account ?? '';
|
|
309
328
|
return { accountId };
|
|
@@ -311,7 +330,10 @@ async function tryGetAwsAccountId(credentials) {
|
|
|
311
330
|
catch (err) {
|
|
312
331
|
const e = err;
|
|
313
332
|
const raw = e.stderr ?? e.message ?? String(err);
|
|
314
|
-
const firstLine = raw
|
|
333
|
+
const firstLine = raw
|
|
334
|
+
.split('\n')
|
|
335
|
+
.map((l) => l.trim())
|
|
336
|
+
.find((l) => l.length > 0) ?? raw;
|
|
315
337
|
return { error: firstLine };
|
|
316
338
|
}
|
|
317
339
|
}
|
|
@@ -323,20 +345,23 @@ async function tryGetAcmCertificate(arn, credentials) {
|
|
|
323
345
|
...process.env,
|
|
324
346
|
AWS_ACCESS_KEY_ID: credentials.AWS_ACCESS_KEY_ID,
|
|
325
347
|
AWS_SECRET_ACCESS_KEY: credentials.AWS_SECRET_ACCESS_KEY,
|
|
326
|
-
AWS_REGION: credentials.AWS_REGION
|
|
348
|
+
AWS_REGION: credentials.AWS_REGION
|
|
327
349
|
},
|
|
328
|
-
timeout: 10000
|
|
350
|
+
timeout: 10000
|
|
329
351
|
});
|
|
330
352
|
const cert = JSON.parse(stdout).Certificate;
|
|
331
353
|
return {
|
|
332
354
|
domainName: cert?.DomainName ?? '(unknown)',
|
|
333
|
-
status: cert?.Status ?? '(unknown)'
|
|
355
|
+
status: cert?.Status ?? '(unknown)'
|
|
334
356
|
};
|
|
335
357
|
}
|
|
336
358
|
catch (err) {
|
|
337
359
|
const e = err;
|
|
338
360
|
const raw = e.stderr ?? e.message ?? String(err);
|
|
339
|
-
const firstLine = raw
|
|
361
|
+
const firstLine = raw
|
|
362
|
+
.split('\n')
|
|
363
|
+
.map((l) => l.trim())
|
|
364
|
+
.find((l) => l.length > 0) ?? raw;
|
|
340
365
|
return { error: firstLine };
|
|
341
366
|
}
|
|
342
367
|
}
|
|
@@ -401,12 +426,12 @@ async function collectAnswers(rl) {
|
|
|
401
426
|
const credFilePath = path.resolve('.fuel-credentials');
|
|
402
427
|
if (await fs.pathExists(credFilePath)) {
|
|
403
428
|
try {
|
|
404
|
-
const existing = await fs.readJson(credFilePath);
|
|
429
|
+
const existing = (await fs.readJson(credFilePath));
|
|
405
430
|
console.log('\n Found existing .fuel-credentials');
|
|
406
431
|
const [stsResult, ghUserResult, ghOrgResult] = await Promise.all([
|
|
407
432
|
tryGetAwsAccountId(existing),
|
|
408
433
|
tryGetGithubUser(existing),
|
|
409
|
-
tryGetGithubOrg(existing)
|
|
434
|
+
tryGetGithubOrg(existing)
|
|
410
435
|
]);
|
|
411
436
|
if ('accountId' in stsResult) {
|
|
412
437
|
console.log(` AWS Account: ${chalk_1.default.green(stsResult.accountId)}`);
|
|
@@ -439,7 +464,8 @@ async function collectAnswers(rl) {
|
|
|
439
464
|
credentials = existing;
|
|
440
465
|
}
|
|
441
466
|
catch (e) {
|
|
442
|
-
if (e instanceof Error &&
|
|
467
|
+
if (e instanceof Error &&
|
|
468
|
+
e.message.startsWith('Credential verification failed'))
|
|
443
469
|
throw e;
|
|
444
470
|
console.log(' Could not read .fuel-credentials — prompting for credentials.');
|
|
445
471
|
}
|
|
@@ -456,7 +482,7 @@ async function collectAnswers(rl) {
|
|
|
456
482
|
const [stsResult, ghUserResult, ghOrgResult] = await Promise.all([
|
|
457
483
|
tryGetAwsAccountId(credentials),
|
|
458
484
|
tryGetGithubUser(credentials),
|
|
459
|
-
tryGetGithubOrg(credentials)
|
|
485
|
+
tryGetGithubOrg(credentials)
|
|
460
486
|
]);
|
|
461
487
|
if ('accountId' in stsResult) {
|
|
462
488
|
console.log(` AWS Account: ${chalk_1.default.green(stsResult.accountId)}`);
|
|
@@ -533,9 +559,18 @@ async function collectAnswers(rl) {
|
|
|
533
559
|
const cpuUnits = await askInt(rl, 'CPU units', 1024);
|
|
534
560
|
const memoryUnits = await askInt(rl, 'Memory (MB)', 2048);
|
|
535
561
|
appTypes.push({
|
|
536
|
-
suffix: 'api',
|
|
537
|
-
|
|
538
|
-
|
|
562
|
+
suffix: 'api',
|
|
563
|
+
dockerfile,
|
|
564
|
+
port,
|
|
565
|
+
healthCheckPath,
|
|
566
|
+
postgres,
|
|
567
|
+
redis,
|
|
568
|
+
cpuUnits,
|
|
569
|
+
memoryUnits,
|
|
570
|
+
dbInstanceType,
|
|
571
|
+
dbAllocatedStorage,
|
|
572
|
+
redisInstanceType,
|
|
573
|
+
variables: {}
|
|
539
574
|
});
|
|
540
575
|
}
|
|
541
576
|
if (includeWeb) {
|
|
@@ -546,10 +581,18 @@ async function collectAnswers(rl) {
|
|
|
546
581
|
const cpuUnits = await askInt(rl, 'CPU units', 1024);
|
|
547
582
|
const memoryUnits = await askInt(rl, 'Memory (MB)', 2048);
|
|
548
583
|
appTypes.push({
|
|
549
|
-
suffix: 'web',
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
584
|
+
suffix: 'web',
|
|
585
|
+
dockerfile,
|
|
586
|
+
port,
|
|
587
|
+
healthCheckPath,
|
|
588
|
+
postgres: false,
|
|
589
|
+
redis: false,
|
|
590
|
+
cpuUnits,
|
|
591
|
+
memoryUnits,
|
|
592
|
+
dbInstanceType: 'db.t3.small',
|
|
593
|
+
dbAllocatedStorage: 30,
|
|
594
|
+
redisInstanceType: 'cache.t3.small',
|
|
595
|
+
variables: {}
|
|
553
596
|
});
|
|
554
597
|
}
|
|
555
598
|
console.log('');
|
|
@@ -559,7 +602,7 @@ async function collectAnswers(rl) {
|
|
|
559
602
|
const envPresets = {
|
|
560
603
|
'production only': ['production'],
|
|
561
604
|
'dev + production': ['dev', 'production'],
|
|
562
|
-
'dev + staging + production': ['dev', 'staging', 'production']
|
|
605
|
+
'dev + staging + production': ['dev', 'staging', 'production']
|
|
563
606
|
};
|
|
564
607
|
const envChoice = await askSelect(rl, 'Environments', Object.keys(envPresets), 3);
|
|
565
608
|
const environments = envPresets[envChoice];
|
|
@@ -573,7 +616,9 @@ async function collectAnswers(rl) {
|
|
|
573
616
|
for (const appType of appTypes) {
|
|
574
617
|
domains[appType.suffix] = {};
|
|
575
618
|
for (const env of environments) {
|
|
576
|
-
const suggestion = baseDomain
|
|
619
|
+
const suggestion = baseDomain
|
|
620
|
+
? defaultDomain(projectName, appType.suffix, env, baseDomain)
|
|
621
|
+
: '';
|
|
577
622
|
const appFullName = `${projectName}-${appType.suffix}-${env}`;
|
|
578
623
|
domains[appType.suffix][env] = await ask(rl, appFullName, suggestion || undefined);
|
|
579
624
|
}
|
|
@@ -592,7 +637,16 @@ async function collectAnswers(rl) {
|
|
|
592
637
|
}
|
|
593
638
|
}
|
|
594
639
|
console.log('');
|
|
595
|
-
return {
|
|
640
|
+
return {
|
|
641
|
+
credentials,
|
|
642
|
+
projectName,
|
|
643
|
+
ssl,
|
|
644
|
+
slackWebhookUrl,
|
|
645
|
+
appTypes,
|
|
646
|
+
environments,
|
|
647
|
+
baseDomain,
|
|
648
|
+
domains
|
|
649
|
+
};
|
|
596
650
|
}
|
|
597
651
|
// --- collectAzureAnswers ---
|
|
598
652
|
async function collectAzureAnswers(rl) {
|
|
@@ -604,12 +658,12 @@ async function collectAzureAnswers(rl) {
|
|
|
604
658
|
const credFilePath = path.resolve('.fuel-credentials');
|
|
605
659
|
if (await fs.pathExists(credFilePath)) {
|
|
606
660
|
try {
|
|
607
|
-
const existing = await fs.readJson(credFilePath);
|
|
661
|
+
const existing = (await fs.readJson(credFilePath));
|
|
608
662
|
console.log('\n Found existing .fuel-credentials');
|
|
609
663
|
const [azResult, ghUserResult, ghOrgResult] = await Promise.all([
|
|
610
664
|
tryGetAzureAccountInfo(existing),
|
|
611
665
|
tryGetGithubUser(existing),
|
|
612
|
-
tryGetGithubOrg(existing)
|
|
666
|
+
tryGetGithubOrg(existing)
|
|
613
667
|
]);
|
|
614
668
|
if ('subscriptionId' in azResult) {
|
|
615
669
|
console.log(` Azure Subscription: ${chalk_1.default.green(azResult.subscriptionId)}`);
|
|
@@ -639,7 +693,8 @@ async function collectAzureAnswers(rl) {
|
|
|
639
693
|
credentials = existing;
|
|
640
694
|
}
|
|
641
695
|
catch (e) {
|
|
642
|
-
if (e instanceof Error &&
|
|
696
|
+
if (e instanceof Error &&
|
|
697
|
+
e.message.startsWith('Credential verification failed'))
|
|
643
698
|
throw e;
|
|
644
699
|
console.log(' Could not read .fuel-credentials — prompting for credentials.');
|
|
645
700
|
}
|
|
@@ -657,7 +712,7 @@ async function collectAzureAnswers(rl) {
|
|
|
657
712
|
const [azResult, ghUserResult, ghOrgResult] = await Promise.all([
|
|
658
713
|
tryGetAzureAccountInfo(credentials),
|
|
659
714
|
tryGetGithubUser(credentials),
|
|
660
|
-
tryGetGithubOrg(credentials)
|
|
715
|
+
tryGetGithubOrg(credentials)
|
|
661
716
|
]);
|
|
662
717
|
if ('subscriptionId' in azResult) {
|
|
663
718
|
console.log(` Azure Subscription: ${chalk_1.default.green(azResult.subscriptionId)}`);
|
|
@@ -710,8 +765,14 @@ async function collectAzureAnswers(rl) {
|
|
|
710
765
|
}
|
|
711
766
|
const redisEnabled = await askYesNo(rl, 'Include Redis?', false);
|
|
712
767
|
appTypes.push({
|
|
713
|
-
suffix: 'api',
|
|
714
|
-
|
|
768
|
+
suffix: 'api',
|
|
769
|
+
dockerfile,
|
|
770
|
+
port,
|
|
771
|
+
healthCheckPath,
|
|
772
|
+
databaseEnabled,
|
|
773
|
+
databaseEngine,
|
|
774
|
+
redisEnabled,
|
|
775
|
+
variables: {}
|
|
715
776
|
});
|
|
716
777
|
}
|
|
717
778
|
if (includeWeb) {
|
|
@@ -720,8 +781,14 @@ async function collectAzureAnswers(rl) {
|
|
|
720
781
|
const port = await askInt(rl, 'Port', 3000);
|
|
721
782
|
const healthCheckPath = await ask(rl, 'Health check path', '/health');
|
|
722
783
|
appTypes.push({
|
|
723
|
-
suffix: 'web',
|
|
724
|
-
|
|
784
|
+
suffix: 'web',
|
|
785
|
+
dockerfile,
|
|
786
|
+
port,
|
|
787
|
+
healthCheckPath,
|
|
788
|
+
databaseEnabled: false,
|
|
789
|
+
databaseEngine: 'mssql',
|
|
790
|
+
redisEnabled: false,
|
|
791
|
+
variables: {}
|
|
725
792
|
});
|
|
726
793
|
}
|
|
727
794
|
console.log('');
|
|
@@ -731,7 +798,7 @@ async function collectAzureAnswers(rl) {
|
|
|
731
798
|
const envPresets = {
|
|
732
799
|
'production only': ['production'],
|
|
733
800
|
'dev + production': ['dev', 'production'],
|
|
734
|
-
'dev + staging + production': ['dev', 'staging', 'production']
|
|
801
|
+
'dev + staging + production': ['dev', 'staging', 'production']
|
|
735
802
|
};
|
|
736
803
|
const envChoice = await askSelect(rl, 'Environments', Object.keys(envPresets), 3);
|
|
737
804
|
const environments = envPresets[envChoice];
|
|
@@ -745,7 +812,9 @@ async function collectAzureAnswers(rl) {
|
|
|
745
812
|
for (const appType of appTypes) {
|
|
746
813
|
domains[appType.suffix] = {};
|
|
747
814
|
for (const env of environments) {
|
|
748
|
-
const suggestion = baseDomain
|
|
815
|
+
const suggestion = baseDomain
|
|
816
|
+
? defaultDomain(projectName, appType.suffix, env, baseDomain)
|
|
817
|
+
: '';
|
|
749
818
|
const appFullName = `${projectName}-${appType.suffix}-${env}`;
|
|
750
819
|
domains[appType.suffix][env] = await ask(rl, appFullName, suggestion || undefined);
|
|
751
820
|
}
|
|
@@ -764,7 +833,16 @@ async function collectAzureAnswers(rl) {
|
|
|
764
833
|
}
|
|
765
834
|
}
|
|
766
835
|
console.log('');
|
|
767
|
-
return {
|
|
836
|
+
return {
|
|
837
|
+
credentials,
|
|
838
|
+
projectName,
|
|
839
|
+
slackWebhookUrl,
|
|
840
|
+
location,
|
|
841
|
+
appTypes,
|
|
842
|
+
environments,
|
|
843
|
+
baseDomain,
|
|
844
|
+
domains
|
|
845
|
+
};
|
|
768
846
|
}
|
|
769
847
|
// --- applyInfraInitAnswers ---
|
|
770
848
|
async function applyInfraInitAnswers(answers, opts) {
|
|
@@ -808,7 +886,7 @@ async function applyInfraInitAnswers(answers, opts) {
|
|
|
808
886
|
memory_units: appType.memoryUnits,
|
|
809
887
|
variables: {},
|
|
810
888
|
build_env: {},
|
|
811
|
-
workers: []
|
|
889
|
+
workers: []
|
|
812
890
|
};
|
|
813
891
|
if (isApi) {
|
|
814
892
|
app.postgres = appType.postgres;
|
|
@@ -841,10 +919,12 @@ async function applyInfraInitAnswers(answers, opts) {
|
|
|
841
919
|
deployment: 'ecs',
|
|
842
920
|
ssl: answers.ssl,
|
|
843
921
|
slack_webhook_url: answers.slackWebhookUrl || '',
|
|
844
|
-
apps
|
|
922
|
+
apps
|
|
845
923
|
};
|
|
846
924
|
// 4. Guard against overwriting existing config
|
|
847
|
-
const outputPath = opts.output
|
|
925
|
+
const outputPath = opts.output
|
|
926
|
+
? path.resolve(opts.output)
|
|
927
|
+
: path.resolve('fuel.yml');
|
|
848
928
|
if (await fs.pathExists(outputPath)) {
|
|
849
929
|
throw new Error(`${outputPath} already exists. Remove it first or use --output <path>.`);
|
|
850
930
|
}
|
|
@@ -899,11 +979,14 @@ async function applyAzureInfraInitAnswers(answers, opts) {
|
|
|
899
979
|
branch,
|
|
900
980
|
variables: {},
|
|
901
981
|
build_env: {},
|
|
902
|
-
workers: []
|
|
982
|
+
workers: []
|
|
903
983
|
};
|
|
904
984
|
if (isApi) {
|
|
905
985
|
app.variables = appType.variables[env] ?? {};
|
|
906
|
-
app.database = {
|
|
986
|
+
app.database = {
|
|
987
|
+
enabled: appType.databaseEnabled,
|
|
988
|
+
engine: appType.databaseEngine
|
|
989
|
+
};
|
|
907
990
|
app.redis = { enabled: appType.redisEnabled };
|
|
908
991
|
}
|
|
909
992
|
else {
|
|
@@ -916,7 +999,10 @@ async function applyAzureInfraInitAnswers(answers, opts) {
|
|
|
916
999
|
if (apiDomain)
|
|
917
1000
|
autoBuildEnv['NEXT_PUBLIC_API_HOST'] = `https://${apiDomain}`;
|
|
918
1001
|
app.build_env = { ...autoBuildEnv, ...(appType.variables[env] ?? {}) };
|
|
919
|
-
app.database = {
|
|
1002
|
+
app.database = {
|
|
1003
|
+
enabled: false,
|
|
1004
|
+
engine: azure_defaults_1.AZURE_DEFAULTS.database.engine
|
|
1005
|
+
};
|
|
920
1006
|
app.redis = { enabled: false };
|
|
921
1007
|
}
|
|
922
1008
|
app.container_app = {
|
|
@@ -924,7 +1010,7 @@ async function applyAzureInfraInitAnswers(answers, opts) {
|
|
|
924
1010
|
memory: azure_defaults_1.AZURE_DEFAULTS.containerApp.memory,
|
|
925
1011
|
concurrent_requests: azure_defaults_1.AZURE_DEFAULTS.containerApp.concurrentRequests,
|
|
926
1012
|
min_replicas: azure_defaults_1.AZURE_DEFAULTS.containerApp.minReplicas,
|
|
927
|
-
max_replicas: azure_defaults_1.AZURE_DEFAULTS.containerApp.maxReplicas
|
|
1013
|
+
max_replicas: azure_defaults_1.AZURE_DEFAULTS.containerApp.maxReplicas
|
|
928
1014
|
};
|
|
929
1015
|
apps.push(app);
|
|
930
1016
|
}
|
|
@@ -934,10 +1020,12 @@ async function applyAzureInfraInitAnswers(answers, opts) {
|
|
|
934
1020
|
deployment: 'azure',
|
|
935
1021
|
location: answers.location,
|
|
936
1022
|
slack_webhook_url: answers.slackWebhookUrl || '',
|
|
937
|
-
apps
|
|
1023
|
+
apps
|
|
938
1024
|
};
|
|
939
1025
|
// 4. Guard against overwriting existing config
|
|
940
|
-
const outputPath = opts.output
|
|
1026
|
+
const outputPath = opts.output
|
|
1027
|
+
? path.resolve(opts.output)
|
|
1028
|
+
: path.resolve('fuel.yml');
|
|
941
1029
|
if (await fs.pathExists(outputPath)) {
|
|
942
1030
|
throw new Error(`${outputPath} already exists. Remove it first or use --output <path>.`);
|
|
943
1031
|
}
|
|
@@ -961,11 +1049,13 @@ async function collectCredentialsOnly(rl, cloud) {
|
|
|
961
1049
|
const credFilePath = path.resolve('.fuel-credentials');
|
|
962
1050
|
if (await fs.pathExists(credFilePath)) {
|
|
963
1051
|
try {
|
|
964
|
-
const existing = await fs.readJson(credFilePath);
|
|
1052
|
+
const existing = (await fs.readJson(credFilePath));
|
|
965
1053
|
console.log('\n Found existing .fuel-credentials');
|
|
966
1054
|
if (cloud === 'aws') {
|
|
967
1055
|
const [stsResult, ghUserResult, ghOrgResult] = await Promise.all([
|
|
968
|
-
tryGetAwsAccountId(existing),
|
|
1056
|
+
tryGetAwsAccountId(existing),
|
|
1057
|
+
tryGetGithubUser(existing),
|
|
1058
|
+
tryGetGithubOrg(existing)
|
|
969
1059
|
]);
|
|
970
1060
|
if ('accountId' in stsResult)
|
|
971
1061
|
console.log(` AWS Account: ${chalk_1.default.green(stsResult.accountId)}`);
|
|
@@ -977,7 +1067,9 @@ async function collectCredentialsOnly(rl, cloud) {
|
|
|
977
1067
|
}
|
|
978
1068
|
else {
|
|
979
1069
|
const [azResult, ghUserResult, ghOrgResult] = await Promise.all([
|
|
980
|
-
tryGetAzureAccountInfo(existing),
|
|
1070
|
+
tryGetAzureAccountInfo(existing),
|
|
1071
|
+
tryGetGithubUser(existing),
|
|
1072
|
+
tryGetGithubOrg(existing)
|
|
981
1073
|
]);
|
|
982
1074
|
if ('subscriptionId' in azResult)
|
|
983
1075
|
console.log(` Azure Subscription: ${chalk_1.default.green(azResult.subscriptionId)}`);
|
|
@@ -995,7 +1087,8 @@ async function collectCredentialsOnly(rl, cloud) {
|
|
|
995
1087
|
}
|
|
996
1088
|
}
|
|
997
1089
|
catch (e) {
|
|
998
|
-
if (e instanceof Error &&
|
|
1090
|
+
if (e instanceof Error &&
|
|
1091
|
+
e.message.startsWith('Credential verification failed'))
|
|
999
1092
|
throw e;
|
|
1000
1093
|
console.log(' Could not read .fuel-credentials — prompting for credentials.');
|
|
1001
1094
|
}
|
|
@@ -1018,7 +1111,9 @@ async function collectCredentialsOnly(rl, cloud) {
|
|
|
1018
1111
|
console.log('\n Verifying credentials...');
|
|
1019
1112
|
if (cloud === 'aws') {
|
|
1020
1113
|
const [stsResult, ghUserResult, ghOrgResult] = await Promise.all([
|
|
1021
|
-
tryGetAwsAccountId(credentials),
|
|
1114
|
+
tryGetAwsAccountId(credentials),
|
|
1115
|
+
tryGetGithubUser(credentials),
|
|
1116
|
+
tryGetGithubOrg(credentials)
|
|
1022
1117
|
]);
|
|
1023
1118
|
if ('accountId' in stsResult)
|
|
1024
1119
|
console.log(` AWS Account: ${chalk_1.default.green(stsResult.accountId)}`);
|
|
@@ -1030,7 +1125,9 @@ async function collectCredentialsOnly(rl, cloud) {
|
|
|
1030
1125
|
}
|
|
1031
1126
|
else {
|
|
1032
1127
|
const [azResult, ghUserResult, ghOrgResult] = await Promise.all([
|
|
1033
|
-
tryGetAzureAccountInfo(credentials),
|
|
1128
|
+
tryGetAzureAccountInfo(credentials),
|
|
1129
|
+
tryGetGithubUser(credentials),
|
|
1130
|
+
tryGetGithubOrg(credentials)
|
|
1034
1131
|
]);
|
|
1035
1132
|
if ('subscriptionId' in azResult)
|
|
1036
1133
|
console.log(` Azure Subscription: ${chalk_1.default.green(azResult.subscriptionId)}`);
|
|
@@ -1057,7 +1154,9 @@ async function collectCredentialsOnly(rl, cloud) {
|
|
|
1057
1154
|
}
|
|
1058
1155
|
async function infraInit(opts) {
|
|
1059
1156
|
if (opts.template) {
|
|
1060
|
-
const outputPath = opts.output
|
|
1157
|
+
const outputPath = opts.output
|
|
1158
|
+
? path.resolve(opts.output)
|
|
1159
|
+
: path.resolve('fuel.yml');
|
|
1061
1160
|
if (await fs.pathExists(outputPath)) {
|
|
1062
1161
|
throw new Error(`${outputPath} already exists. Remove it first or use --output <path>.`);
|
|
1063
1162
|
}
|
|
@@ -1071,7 +1170,9 @@ async function infraInit(opts) {
|
|
|
1071
1170
|
}
|
|
1072
1171
|
const rl = (0, promises_1.createInterface)({ input: process.stdin, output: process.stdout });
|
|
1073
1172
|
try {
|
|
1074
|
-
const cloud = opts.azure
|
|
1173
|
+
const cloud = opts.azure
|
|
1174
|
+
? 'azure'
|
|
1175
|
+
: await askCloudChoice(rl);
|
|
1075
1176
|
if (opts.credentialsOnly) {
|
|
1076
1177
|
await collectCredentialsOnly(rl, cloud);
|
|
1077
1178
|
return;
|