@akash-chowdhury-24/deployhub 1.0.16 → 2.0.2
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 +264 -59
- package/package.json +4 -3
- package/src/commands/doctor.js +168 -170
- package/src/commands/init.js +80 -371
- package/src/commands/rollback.js +1 -1
- package/src/core/config.js +17 -13
- package/src/core/stages.js +0 -11
- package/src/deployment/deployment-env.js +625 -0
- package/src/deployment/index.js +0 -72
- package/src/deployment/init-helpers.js +389 -0
- package/src/deployment/init-prompts.js +432 -0
- package/src/deployment/providers/azure-vm.js +85 -2
- package/src/deployment/providers/docker.js +101 -8
- package/src/deployment/providers/ec2.js +88 -2
- package/src/deployment/providers/gcp-vm.js +89 -2
- package/src/deployment/providers/kubernetes.js +104 -7
- package/src/deployment/providers/ssh.js +21 -4
- package/src/utils/github-actions.js +28 -131
- package/src/utils/rollback/engine.js +69 -0
- package/src/deployment/providers/platforms/_shared.js +0 -167
- package/src/deployment/providers/platforms/aws-amplify.js +0 -367
- package/src/deployment/providers/platforms/azure-static-web-apps.js +0 -68
- package/src/deployment/providers/platforms/cloudflare-pages.js +0 -103
- package/src/deployment/providers/platforms/firebase-app-hosting.js +0 -95
- package/src/deployment/providers/platforms/firebase-hosting.js +0 -99
- package/src/deployment/providers/platforms/index.js +0 -44
- package/src/deployment/providers/platforms/netlify.js +0 -102
- package/src/deployment/providers/platforms/vercel.js +0 -92
- package/src/rollback/engine.js +0 -102
- package/src/utils/firebase-config-generator.js +0 -35
- package/src/utils/init-platform.js +0 -236
- package/src/utils/platform-env.js +0 -132
package/src/commands/init.js
CHANGED
|
@@ -2,7 +2,7 @@ import inquirer from 'inquirer';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import fs from 'fs-extra';
|
|
4
4
|
import path from 'path';
|
|
5
|
-
import { saveConfig } from '../core/config.js';
|
|
5
|
+
import { saveConfig, appendEnv } from '../core/config.js';
|
|
6
6
|
import { detectFrontend, detectBackend } from '../detectors/index.js';
|
|
7
7
|
import {
|
|
8
8
|
getFrontendInfo,
|
|
@@ -18,16 +18,15 @@ import {
|
|
|
18
18
|
addDeployhubToPackageJson,
|
|
19
19
|
DEFAULT_NPM_CLI_SOURCE,
|
|
20
20
|
} from '../utils/github-actions.js';
|
|
21
|
-
import {
|
|
22
|
-
promptPlatformQuestions,
|
|
23
|
-
appendPlatformEnvExample,
|
|
24
|
-
showPlatformComparison,
|
|
25
|
-
suggestHealthUrl,
|
|
26
|
-
PLATFORM_CHOICES,
|
|
27
|
-
} from '../utils/init-platform.js';
|
|
28
21
|
import { printAuthorFooter } from '../utils/author.js';
|
|
29
|
-
import { ensureFirebaseJson } from '../utils/firebase-config-generator.js';
|
|
30
22
|
import { generateNginxConfig } from '../utils/nginx.js';
|
|
23
|
+
import {
|
|
24
|
+
promptServerDeployment,
|
|
25
|
+
buildServerEnvEntry,
|
|
26
|
+
getDockerEnvSecrets,
|
|
27
|
+
SSH_BASED,
|
|
28
|
+
} from '../deployment/init-prompts.js';
|
|
29
|
+
import { printDeploymentNextSteps } from '../deployment/deployment-env.js';
|
|
31
30
|
|
|
32
31
|
const FRONTEND_CHOICES = [
|
|
33
32
|
{ name: 'React', value: 'react' },
|
|
@@ -159,136 +158,6 @@ async function promptBuildSettings(defaults, side) {
|
|
|
159
158
|
return inquirer.prompt(questions);
|
|
160
159
|
}
|
|
161
160
|
|
|
162
|
-
const SERVER_DEPLOY_TYPES = [
|
|
163
|
-
'ssh',
|
|
164
|
-
'docker',
|
|
165
|
-
'ec2',
|
|
166
|
-
'azure-vm',
|
|
167
|
-
'gcp-vm',
|
|
168
|
-
'kubernetes',
|
|
169
|
-
];
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* @param {string} projectName
|
|
173
|
-
* @param {'frontend'|'backend'|'both'} projectType
|
|
174
|
-
* @param {Record<string, unknown>|null} backendConfig
|
|
175
|
-
* @param {Record<string, unknown>|null} singleConfig
|
|
176
|
-
*/
|
|
177
|
-
async function promptServerDeployment(projectName, projectType, backendConfig, singleConfig) {
|
|
178
|
-
return inquirer.prompt([
|
|
179
|
-
{
|
|
180
|
-
type: 'list',
|
|
181
|
-
name: 'deployType',
|
|
182
|
-
message: 'Deployment type:',
|
|
183
|
-
choices: SERVER_DEPLOY_TYPES,
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
type: 'input',
|
|
187
|
-
name: 'envName',
|
|
188
|
-
message: 'Environment name:',
|
|
189
|
-
default: 'production',
|
|
190
|
-
},
|
|
191
|
-
{
|
|
192
|
-
type: 'input',
|
|
193
|
-
name: 'host',
|
|
194
|
-
message: 'Host (for SSH-based targets):',
|
|
195
|
-
when: (a) => ['ssh', 'ec2', 'azure-vm', 'gcp-vm'].includes(a.deployType),
|
|
196
|
-
},
|
|
197
|
-
{
|
|
198
|
-
type: 'input',
|
|
199
|
-
name: 'user',
|
|
200
|
-
message: 'SSH user:',
|
|
201
|
-
when: (a) => ['ssh', 'ec2', 'azure-vm', 'gcp-vm'].includes(a.deployType),
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
type: 'input',
|
|
205
|
-
name: 'deployPath',
|
|
206
|
-
message: 'Deploy path:',
|
|
207
|
-
default: `/var/www/${projectName}`,
|
|
208
|
-
when: (a) =>
|
|
209
|
-
['ssh', 'ec2', 'azure-vm', 'gcp-vm'].includes(a.deployType) &&
|
|
210
|
-
projectType !== 'both',
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
type: 'input',
|
|
214
|
-
name: 'frontendDeployPath',
|
|
215
|
-
message: 'Frontend deploy path:',
|
|
216
|
-
default: `/var/www/${projectName}/public`,
|
|
217
|
-
when: (a) =>
|
|
218
|
-
['ssh', 'ec2', 'azure-vm', 'gcp-vm'].includes(a.deployType) &&
|
|
219
|
-
projectType === 'both',
|
|
220
|
-
},
|
|
221
|
-
{
|
|
222
|
-
type: 'input',
|
|
223
|
-
name: 'backendDeployPath',
|
|
224
|
-
message: 'Backend deploy path:',
|
|
225
|
-
default: `/var/www/${projectName}/api`,
|
|
226
|
-
when: (a) =>
|
|
227
|
-
['ssh', 'ec2', 'azure-vm', 'gcp-vm'].includes(a.deployType) &&
|
|
228
|
-
projectType === 'both',
|
|
229
|
-
},
|
|
230
|
-
{
|
|
231
|
-
type: 'input',
|
|
232
|
-
name: 'appName',
|
|
233
|
-
message: 'App name (for PM2):',
|
|
234
|
-
default: projectType === 'both' ? `${projectName}-api` : projectName,
|
|
235
|
-
when: (a) =>
|
|
236
|
-
['ssh', 'ec2', 'azure-vm', 'gcp-vm'].includes(a.deployType) &&
|
|
237
|
-
(projectType === 'backend' || projectType === 'both'),
|
|
238
|
-
},
|
|
239
|
-
{
|
|
240
|
-
type: 'input',
|
|
241
|
-
name: 'healthUrl',
|
|
242
|
-
message: 'Health check URL (optional):',
|
|
243
|
-
},
|
|
244
|
-
]);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* @param {Awaited<ReturnType<typeof promptServerDeployment>>} deployAnswers
|
|
249
|
-
* @param {'frontend'|'backend'|'both'} projectType
|
|
250
|
-
* @param {string} projectName
|
|
251
|
-
* @param {Record<string, unknown>|null} backendConfig
|
|
252
|
-
* @param {Record<string, unknown>|null} singleConfig
|
|
253
|
-
*/
|
|
254
|
-
function buildServerEnvEntry(
|
|
255
|
-
deployAnswers,
|
|
256
|
-
projectType,
|
|
257
|
-
projectName,
|
|
258
|
-
backendConfig,
|
|
259
|
-
singleConfig
|
|
260
|
-
) {
|
|
261
|
-
/** @type {Record<string, unknown>} */
|
|
262
|
-
const envEntry = {
|
|
263
|
-
deploymentType: 'server',
|
|
264
|
-
type: deployAnswers.deployType,
|
|
265
|
-
host: deployAnswers.host || '',
|
|
266
|
-
user: deployAnswers.user || '',
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
if (projectType === 'both') {
|
|
270
|
-
envEntry.frontendDeployPath =
|
|
271
|
-
deployAnswers.frontendDeployPath || `/var/www/${projectName}/public`;
|
|
272
|
-
envEntry.backendDeployPath =
|
|
273
|
-
deployAnswers.backendDeployPath || `/var/www/${projectName}/api`;
|
|
274
|
-
envEntry.appName = deployAnswers.appName || `${projectName}-api`;
|
|
275
|
-
envEntry.framework = backendConfig?.framework || 'express';
|
|
276
|
-
envEntry.path = envEntry.backendDeployPath;
|
|
277
|
-
envEntry.backendDeploymentType = 'server';
|
|
278
|
-
} else if (projectType === 'backend') {
|
|
279
|
-
envEntry.deployPath = deployAnswers.deployPath || `/var/www/${projectName}`;
|
|
280
|
-
envEntry.path = envEntry.deployPath;
|
|
281
|
-
envEntry.appName = deployAnswers.appName || projectName;
|
|
282
|
-
envEntry.framework = singleConfig?.framework || 'express';
|
|
283
|
-
envEntry.port = singleConfig?.port || 3000;
|
|
284
|
-
} else {
|
|
285
|
-
envEntry.deployPath = deployAnswers.deployPath || `/var/www/${projectName}`;
|
|
286
|
-
envEntry.path = envEntry.deployPath;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
return envEntry;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
161
|
/**
|
|
293
162
|
* @param {Record<string, unknown>} config
|
|
294
163
|
* @param {Record<string, Record<string, unknown>>} environments
|
|
@@ -297,24 +166,11 @@ function buildServerEnvEntry(
|
|
|
297
166
|
async function generateProjectScaffold(config, environments, cwd) {
|
|
298
167
|
const envList = Object.values(environments);
|
|
299
168
|
|
|
300
|
-
const
|
|
169
|
+
const usesFrontendSsh = envList.some(
|
|
301
170
|
(env) =>
|
|
302
|
-
env.
|
|
171
|
+
SSH_BASED.includes(env.type) || env.deploymentType === 'server'
|
|
303
172
|
);
|
|
304
173
|
|
|
305
|
-
if (usesFirebase && !(await fs.pathExists(path.join(cwd, 'firebase.json')))) {
|
|
306
|
-
const buildOutput =
|
|
307
|
-
config.frontend?.buildOutput || config.buildOutput || 'dist';
|
|
308
|
-
await ensureFirebaseJson(buildOutput, cwd);
|
|
309
|
-
console.log(chalk.gray(' • firebase.json (auto-generated)'));
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
const usesFrontendSsh = envList.some((env) => {
|
|
313
|
-
if (env.frontendDeploymentType === 'platform') return false;
|
|
314
|
-
if (env.deploymentType === 'platform' && !env.type) return false;
|
|
315
|
-
return env.type === 'ssh' || env.deploymentType === 'server';
|
|
316
|
-
});
|
|
317
|
-
|
|
318
174
|
const isFrontendProject =
|
|
319
175
|
config.projectType === 'frontend' || config.projectType === 'both';
|
|
320
176
|
|
|
@@ -453,18 +309,19 @@ export function registerInitCommand(program) {
|
|
|
453
309
|
/** @type {string[]} */
|
|
454
310
|
const deploy = [];
|
|
455
311
|
let healthUrl = '';
|
|
312
|
+
/** @type {string|undefined} */
|
|
313
|
+
let primaryDeployType;
|
|
314
|
+
/** @type {Record<string, string>|null} */
|
|
315
|
+
let dockerEnvToAppend = null;
|
|
456
316
|
|
|
457
317
|
if (answers.configureDeploy) {
|
|
458
|
-
const buildOutput =
|
|
459
|
-
frontendConfig?.buildOutput || singleConfig?.buildOutput || 'dist';
|
|
460
|
-
|
|
461
318
|
if (projectType === 'backend') {
|
|
462
319
|
const deployAnswers = await promptServerDeployment(
|
|
463
320
|
projectName,
|
|
464
321
|
projectType,
|
|
465
|
-
backendConfig
|
|
466
|
-
singleConfig
|
|
322
|
+
backendConfig
|
|
467
323
|
);
|
|
324
|
+
primaryDeployType = deployAnswers.deployType;
|
|
468
325
|
deploy.push(deployAnswers.envName);
|
|
469
326
|
environments[deployAnswers.envName] = buildServerEnvEntry(
|
|
470
327
|
deployAnswers,
|
|
@@ -478,218 +335,61 @@ export function registerInitCommand(program) {
|
|
|
478
335
|
} else if (singleConfig?.port) {
|
|
479
336
|
healthUrl = `http://localhost:${singleConfig.port}/health`;
|
|
480
337
|
}
|
|
338
|
+
dockerEnvToAppend = getDockerEnvSecrets(deployAnswers);
|
|
481
339
|
} else if (projectType === 'frontend') {
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
]);
|
|
499
|
-
|
|
500
|
-
const { envName } = await inquirer.prompt([
|
|
501
|
-
{
|
|
502
|
-
type: 'input',
|
|
503
|
-
name: 'envName',
|
|
504
|
-
message: 'Environment name:',
|
|
505
|
-
default: 'production',
|
|
506
|
-
},
|
|
507
|
-
]);
|
|
508
|
-
deploy.push(envName);
|
|
509
|
-
|
|
510
|
-
if (deployMethod === 'platform') {
|
|
511
|
-
showPlatformComparison();
|
|
512
|
-
const { platform } = await inquirer.prompt([
|
|
513
|
-
{
|
|
514
|
-
type: 'list',
|
|
515
|
-
name: 'platform',
|
|
516
|
-
message: 'Select platform:',
|
|
517
|
-
choices: PLATFORM_CHOICES,
|
|
518
|
-
},
|
|
519
|
-
]);
|
|
520
|
-
|
|
521
|
-
const platformConfig = await promptPlatformQuestions(
|
|
522
|
-
platform,
|
|
523
|
-
projectName,
|
|
524
|
-
buildOutput,
|
|
525
|
-
cwd
|
|
526
|
-
);
|
|
527
|
-
await appendPlatformEnvExample(platform, cwd);
|
|
528
|
-
|
|
529
|
-
const { healthUrlInput } = await inquirer.prompt([
|
|
530
|
-
{
|
|
531
|
-
type: 'input',
|
|
532
|
-
name: 'healthUrlInput',
|
|
533
|
-
message: 'Health check URL (optional):',
|
|
534
|
-
default: suggestHealthUrl(platform, projectName),
|
|
535
|
-
},
|
|
536
|
-
]);
|
|
537
|
-
|
|
538
|
-
environments[envName] = {
|
|
539
|
-
deploymentType: 'platform',
|
|
540
|
-
...platformConfig,
|
|
541
|
-
};
|
|
542
|
-
healthUrl = healthUrlInput || suggestHealthUrl(platform, projectName);
|
|
543
|
-
} else {
|
|
544
|
-
const deployAnswers = await promptServerDeployment(
|
|
545
|
-
projectName,
|
|
546
|
-
projectType,
|
|
547
|
-
backendConfig,
|
|
548
|
-
singleConfig
|
|
549
|
-
);
|
|
550
|
-
deployAnswers.envName = envName;
|
|
551
|
-
environments[envName] = buildServerEnvEntry(
|
|
552
|
-
deployAnswers,
|
|
553
|
-
projectType,
|
|
554
|
-
projectName,
|
|
555
|
-
backendConfig,
|
|
556
|
-
singleConfig
|
|
557
|
-
);
|
|
558
|
-
if (deployAnswers.healthUrl) healthUrl = deployAnswers.healthUrl;
|
|
340
|
+
const deployAnswers = await promptServerDeployment(
|
|
341
|
+
projectName,
|
|
342
|
+
projectType,
|
|
343
|
+
backendConfig
|
|
344
|
+
);
|
|
345
|
+
primaryDeployType = deployAnswers.deployType;
|
|
346
|
+
deploy.push(deployAnswers.envName);
|
|
347
|
+
environments[deployAnswers.envName] = buildServerEnvEntry(
|
|
348
|
+
deployAnswers,
|
|
349
|
+
projectType,
|
|
350
|
+
projectName,
|
|
351
|
+
backendConfig,
|
|
352
|
+
singleConfig
|
|
353
|
+
);
|
|
354
|
+
if (deployAnswers.healthUrl) {
|
|
355
|
+
healthUrl = deployAnswers.healthUrl;
|
|
559
356
|
}
|
|
357
|
+
dockerEnvToAppend = getDockerEnvSecrets(deployAnswers);
|
|
560
358
|
} else {
|
|
561
|
-
const
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
name: 'Managed platform (Vercel, Netlify, Cloudflare Pages, etc.)',
|
|
569
|
-
value: 'platform',
|
|
570
|
-
},
|
|
571
|
-
{
|
|
572
|
-
name: 'Self-hosted server (SSH, Docker, EC2, etc.)',
|
|
573
|
-
value: 'server',
|
|
574
|
-
},
|
|
575
|
-
],
|
|
576
|
-
},
|
|
577
|
-
]);
|
|
578
|
-
|
|
579
|
-
const { envName } = await inquirer.prompt([
|
|
580
|
-
{
|
|
581
|
-
type: 'input',
|
|
582
|
-
name: 'envName',
|
|
583
|
-
message: 'Environment name:',
|
|
584
|
-
default: 'production',
|
|
585
|
-
},
|
|
586
|
-
]);
|
|
587
|
-
deploy.push(envName);
|
|
359
|
+
const backendDeployAnswers = await promptServerDeployment(
|
|
360
|
+
projectName,
|
|
361
|
+
'both',
|
|
362
|
+
backendConfig
|
|
363
|
+
);
|
|
364
|
+
primaryDeployType = backendDeployAnswers.deployType;
|
|
365
|
+
deploy.push(backendDeployAnswers.envName);
|
|
588
366
|
|
|
589
367
|
/** @type {Record<string, unknown>} */
|
|
590
|
-
const envEntry = {
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
]);
|
|
602
|
-
|
|
603
|
-
const platformConfig = await promptPlatformQuestions(
|
|
604
|
-
platform,
|
|
368
|
+
const envEntry = {
|
|
369
|
+
backendDeploymentType: 'server',
|
|
370
|
+
frontendDeploymentType: 'server',
|
|
371
|
+
deploymentType: 'server',
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
Object.assign(
|
|
375
|
+
envEntry,
|
|
376
|
+
buildServerEnvEntry(
|
|
377
|
+
backendDeployAnswers,
|
|
378
|
+
'both',
|
|
605
379
|
projectName,
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
)
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
Object.assign(envEntry, {
|
|
612
|
-
frontendDeploymentType: 'platform',
|
|
613
|
-
deploymentType: 'platform',
|
|
614
|
-
...platformConfig,
|
|
615
|
-
});
|
|
616
|
-
healthUrl = suggestHealthUrl(platform, projectName);
|
|
617
|
-
} else {
|
|
618
|
-
envEntry.frontendDeploymentType = 'server';
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
console.log(chalk.gray('\nBackend will be deployed to a self-hosted server.'));
|
|
622
|
-
const backendDeployAnswers = await inquirer.prompt([
|
|
623
|
-
{
|
|
624
|
-
type: 'list',
|
|
625
|
-
name: 'deployType',
|
|
626
|
-
message: 'Backend deployment type:',
|
|
627
|
-
choices: SERVER_DEPLOY_TYPES,
|
|
628
|
-
default: 'ssh',
|
|
629
|
-
},
|
|
630
|
-
{
|
|
631
|
-
type: 'input',
|
|
632
|
-
name: 'host',
|
|
633
|
-
message: 'Host (for SSH-based targets):',
|
|
634
|
-
when: (a) => ['ssh', 'ec2', 'azure-vm', 'gcp-vm'].includes(a.deployType),
|
|
635
|
-
},
|
|
636
|
-
{
|
|
637
|
-
type: 'input',
|
|
638
|
-
name: 'user',
|
|
639
|
-
message: 'SSH user:',
|
|
640
|
-
when: (a) => ['ssh', 'ec2', 'azure-vm', 'gcp-vm'].includes(a.deployType),
|
|
641
|
-
},
|
|
642
|
-
{
|
|
643
|
-
type: 'input',
|
|
644
|
-
name: 'backendDeployPath',
|
|
645
|
-
message: 'Backend deploy path:',
|
|
646
|
-
default: `/var/www/${projectName}/api`,
|
|
647
|
-
},
|
|
648
|
-
{
|
|
649
|
-
type: 'input',
|
|
650
|
-
name: 'appName',
|
|
651
|
-
message: 'App name (for PM2):',
|
|
652
|
-
default: `${projectName}-api`,
|
|
653
|
-
},
|
|
654
|
-
]);
|
|
655
|
-
|
|
656
|
-
Object.assign(envEntry, {
|
|
657
|
-
type: backendDeployAnswers.deployType,
|
|
658
|
-
host: backendDeployAnswers.host || '',
|
|
659
|
-
user: backendDeployAnswers.user || '',
|
|
660
|
-
backendDeployPath:
|
|
661
|
-
backendDeployAnswers.backendDeployPath || `/var/www/${projectName}/api`,
|
|
662
|
-
appName: backendDeployAnswers.appName || `${projectName}-api`,
|
|
663
|
-
framework: backendConfig?.framework || 'express',
|
|
664
|
-
path: backendDeployAnswers.backendDeployPath || `/var/www/${projectName}/api`,
|
|
665
|
-
});
|
|
666
|
-
|
|
667
|
-
if (frontendDeployMethod === 'server') {
|
|
668
|
-
const { frontendDeployPath } = await inquirer.prompt([
|
|
669
|
-
{
|
|
670
|
-
type: 'input',
|
|
671
|
-
name: 'frontendDeployPath',
|
|
672
|
-
message: 'Frontend deploy path:',
|
|
673
|
-
default: `/var/www/${projectName}/public`,
|
|
674
|
-
},
|
|
675
|
-
]);
|
|
676
|
-
envEntry.frontendDeployPath = frontendDeployPath;
|
|
677
|
-
}
|
|
380
|
+
backendConfig,
|
|
381
|
+
singleConfig
|
|
382
|
+
)
|
|
383
|
+
);
|
|
678
384
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
name: 'healthUrlInput',
|
|
683
|
-
message: 'Health check URL (optional):',
|
|
684
|
-
default: healthUrl || '',
|
|
685
|
-
},
|
|
686
|
-
]);
|
|
687
|
-
if (healthUrlInput) healthUrl = healthUrlInput;
|
|
688
|
-
else if (!healthUrl && backendConfig?.port) {
|
|
385
|
+
if (backendDeployAnswers.healthUrl) {
|
|
386
|
+
healthUrl = backendDeployAnswers.healthUrl;
|
|
387
|
+
} else if (backendConfig?.port) {
|
|
689
388
|
healthUrl = `http://localhost:${backendConfig.port}/health`;
|
|
690
389
|
}
|
|
691
390
|
|
|
692
|
-
environments[envName] = envEntry;
|
|
391
|
+
environments[backendDeployAnswers.envName] = envEntry;
|
|
392
|
+
dockerEnvToAppend = getDockerEnvSecrets(backendDeployAnswers);
|
|
693
393
|
}
|
|
694
394
|
}
|
|
695
395
|
|
|
@@ -742,6 +442,9 @@ export function registerInitCommand(program) {
|
|
|
742
442
|
}
|
|
743
443
|
|
|
744
444
|
await saveConfig(config, cwd);
|
|
445
|
+
if (dockerEnvToAppend) {
|
|
446
|
+
await appendEnv(dockerEnvToAppend, cwd);
|
|
447
|
+
}
|
|
745
448
|
await addDeployhubToPackageJson(cliSource, cwd);
|
|
746
449
|
await writeWorkflowFile(
|
|
747
450
|
config.storage,
|
|
@@ -766,22 +469,28 @@ export function registerInitCommand(program) {
|
|
|
766
469
|
const secrets = getRequiredSecrets(config.storage, deploy, environments, config);
|
|
767
470
|
|
|
768
471
|
console.log('');
|
|
769
|
-
|
|
472
|
+
if (primaryDeployType) {
|
|
473
|
+
console.log(chalk.green.bold(`✔ Config generated for ${primaryDeployType} deployment.`));
|
|
474
|
+
printDeploymentNextSteps(primaryDeployType, secrets);
|
|
475
|
+
} else {
|
|
476
|
+
console.log(chalk.green.bold('✓ DeployHub initialized successfully!'));
|
|
477
|
+
console.log('');
|
|
478
|
+
console.log(chalk.bold('Next steps:'));
|
|
479
|
+
console.log(' 1. Copy .env.example to .env and fill in credentials');
|
|
480
|
+
if (secrets.length > 0) {
|
|
481
|
+
console.log(' 2. Add these secrets to GitHub (Settings → Secrets):');
|
|
482
|
+
secrets.forEach((s) => console.log(` • ${s}`));
|
|
483
|
+
}
|
|
484
|
+
console.log(` ${secrets.length > 0 ? '3' : '2'}. Run ${chalk.cyan('deployhub doctor')} to verify your setup`);
|
|
485
|
+
console.log(` ${secrets.length > 0 ? '4' : '3'}. Push to main — GitHub Actions will run ${chalk.cyan('deployhub build')} automatically`);
|
|
486
|
+
}
|
|
487
|
+
|
|
770
488
|
console.log('');
|
|
771
489
|
console.log(chalk.bold('Generated files:'));
|
|
772
490
|
console.log(' • deployhub.config.json');
|
|
773
491
|
console.log(' • .github/workflows/deployhub.yml');
|
|
774
492
|
console.log(' • .env.example');
|
|
775
493
|
console.log('');
|
|
776
|
-
console.log(chalk.bold('Next steps:'));
|
|
777
|
-
console.log(' 1. Copy .env.example to .env and fill in credentials');
|
|
778
|
-
if (secrets.length > 0) {
|
|
779
|
-
console.log(' 2. Add these secrets to GitHub (Settings → Secrets):');
|
|
780
|
-
secrets.forEach((s) => console.log(` • ${s}`));
|
|
781
|
-
}
|
|
782
|
-
console.log(` ${secrets.length > 0 ? '3' : '2'}. Run ${chalk.cyan('deployhub doctor')} to verify your setup`);
|
|
783
|
-
console.log(` ${secrets.length > 0 ? '4' : '3'}. Push to main — GitHub Actions will run ${chalk.cyan('deployhub build')} automatically`);
|
|
784
|
-
console.log('');
|
|
785
494
|
printAuthorFooter();
|
|
786
495
|
});
|
|
787
496
|
}
|
package/src/commands/rollback.js
CHANGED
package/src/core/config.js
CHANGED
|
@@ -14,25 +14,29 @@ const SideConfigSchema = z.object({
|
|
|
14
14
|
|
|
15
15
|
const EnvironmentSchema = z.object({
|
|
16
16
|
type: z.string().optional(),
|
|
17
|
-
deploymentType: z.enum(['
|
|
18
|
-
|
|
19
|
-
projectName: z.string().optional(),
|
|
20
|
-
siteId: z.string().optional(),
|
|
21
|
-
accountId: z.string().optional(),
|
|
22
|
-
appId: z.string().optional(),
|
|
23
|
-
region: z.string().optional(),
|
|
24
|
-
branch: z.string().optional(),
|
|
25
|
-
githubConnected: z.boolean().optional(),
|
|
26
|
-
resourceName: z.string().optional(),
|
|
27
|
-
projectId: z.string().optional(),
|
|
28
|
-
backendName: z.string().optional(),
|
|
29
|
-
frontendDeploymentType: z.enum(['platform', 'server']).optional(),
|
|
17
|
+
deploymentType: z.enum(['server']).optional(),
|
|
18
|
+
frontendDeploymentType: z.enum(['server']).optional(),
|
|
30
19
|
backendDeploymentType: z.enum(['server']).optional(),
|
|
31
20
|
host: z.string().optional(),
|
|
32
21
|
user: z.string().optional(),
|
|
33
22
|
path: z.string().optional(),
|
|
34
23
|
deployPath: z.string().optional(),
|
|
35
24
|
keyPath: z.string().optional(),
|
|
25
|
+
sshPort: z.number().optional(),
|
|
26
|
+
ec2InstanceId: z.string().optional(),
|
|
27
|
+
awsRegion: z.string().optional(),
|
|
28
|
+
azureSubscriptionId: z.string().optional(),
|
|
29
|
+
azureResourceGroup: z.string().optional(),
|
|
30
|
+
azureVmName: z.string().optional(),
|
|
31
|
+
gcpProjectId: z.string().optional(),
|
|
32
|
+
gcpZone: z.string().optional(),
|
|
33
|
+
gcpInstanceName: z.string().optional(),
|
|
34
|
+
kubeconfig: z.string().optional(),
|
|
35
|
+
kubeContext: z.string().optional(),
|
|
36
|
+
kubeNamespace: z.string().optional(),
|
|
37
|
+
dockerImageName: z.string().optional(),
|
|
38
|
+
dockerRegistryUrl: z.string().optional(),
|
|
39
|
+
dockerHost: z.string().optional(),
|
|
36
40
|
appName: z.string().optional(),
|
|
37
41
|
framework: z.string().optional(),
|
|
38
42
|
port: z.number().optional(),
|
package/src/core/stages.js
CHANGED
|
@@ -5,8 +5,6 @@ import { uploadToAll } from '../storage/index.js';
|
|
|
5
5
|
import { deployToAll } from '../deployment/index.js';
|
|
6
6
|
import { sendNotifications } from '../notifications/index.js';
|
|
7
7
|
import axios from 'axios';
|
|
8
|
-
import fs from 'fs-extra';
|
|
9
|
-
import path from 'path';
|
|
10
8
|
import { getProjectVersion } from '../utils/version.js';
|
|
11
9
|
|
|
12
10
|
/**
|
|
@@ -149,15 +147,6 @@ export function buildPipelineStages(config, cwd, state) {
|
|
|
149
147
|
const deployed = await deployToAll(ctx.config, artifactDir);
|
|
150
148
|
ctx.state.deployedTargets = deployed;
|
|
151
149
|
|
|
152
|
-
const deploymentPath = path.join(artifactDir, 'deployment.json');
|
|
153
|
-
if (await fs.pathExists(deploymentPath)) {
|
|
154
|
-
const data = await fs.readJson(deploymentPath);
|
|
155
|
-
const last = data.lastDeployment;
|
|
156
|
-
if (last?.deployUrl || last?.deploymentUrl) {
|
|
157
|
-
ctx.state.lastDeployUrl = last.deployUrl || last.deploymentUrl;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
150
|
await repackArtifactZip(artifactDir);
|
|
162
151
|
const zipPath = /** @type {string} */ (ctx.state.zipPath);
|
|
163
152
|
if (zipPath) {
|