@akash-chowdhury-24/deployhub 2.0.6 → 2.0.7
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/package.json +1 -1
- package/src/commands/init.js +22 -8
- package/src/deployment/deployment-env.js +101 -19
- package/src/utils/github-actions.js +54 -10
package/package.json
CHANGED
package/src/commands/init.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import { getProjectVersion } from '../utils/version.js';
|
|
14
14
|
import {
|
|
15
15
|
writeWorkflowFile,
|
|
16
|
-
|
|
16
|
+
getGithubSecretsChecklist,
|
|
17
17
|
generateEnvExampleContent,
|
|
18
18
|
addDeployhubToPackageJson,
|
|
19
19
|
DEFAULT_NPM_CLI_SOURCE,
|
|
@@ -30,7 +30,10 @@ import {
|
|
|
30
30
|
getDockerEnvSecrets,
|
|
31
31
|
SSH_BASED,
|
|
32
32
|
} from '../deployment/init-prompts.js';
|
|
33
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
printDeploymentNextSteps,
|
|
35
|
+
formatSecretChecklistLine,
|
|
36
|
+
} from '../deployment/deployment-env.js';
|
|
34
37
|
import { confirmValueIfContainsSpaces } from '../deployment/init-helpers.js';
|
|
35
38
|
|
|
36
39
|
const FRONTEND_CHOICES = [
|
|
@@ -498,23 +501,34 @@ export function registerInitCommand(program) {
|
|
|
498
501
|
);
|
|
499
502
|
await fs.writeFile(envExampleDest, envExampleContent);
|
|
500
503
|
|
|
501
|
-
const
|
|
504
|
+
const secretsChecklist = getGithubSecretsChecklist(
|
|
505
|
+
config.storage,
|
|
506
|
+
deploy,
|
|
507
|
+
environments,
|
|
508
|
+
config
|
|
509
|
+
);
|
|
502
510
|
|
|
503
511
|
console.log('');
|
|
504
512
|
if (primaryDeployType) {
|
|
505
513
|
console.log(chalk.green.bold(`✔ Config generated for ${primaryDeployType} deployment.`));
|
|
506
|
-
printDeploymentNextSteps(primaryDeployType,
|
|
514
|
+
printDeploymentNextSteps(primaryDeployType, secretsChecklist);
|
|
507
515
|
} else {
|
|
508
516
|
console.log(chalk.green.bold('✓ DeployHub initialized successfully!'));
|
|
509
517
|
console.log('');
|
|
510
518
|
console.log(chalk.bold('Next steps:'));
|
|
511
519
|
console.log(' 1. Copy .env.example to .env and fill in credentials');
|
|
512
|
-
if (
|
|
520
|
+
if (secretsChecklist.length > 0) {
|
|
513
521
|
console.log(' 2. Add these secrets to GitHub (Settings → Secrets):');
|
|
514
|
-
|
|
522
|
+
secretsChecklist.forEach((item) =>
|
|
523
|
+
console.log(` ${formatSecretChecklistLine(item)}`)
|
|
524
|
+
);
|
|
515
525
|
}
|
|
516
|
-
console.log(
|
|
517
|
-
|
|
526
|
+
console.log(
|
|
527
|
+
` ${secretsChecklist.length > 0 ? '3' : '2'}. Run ${chalk.cyan('deployhub doctor')} to verify your setup`
|
|
528
|
+
);
|
|
529
|
+
console.log(
|
|
530
|
+
` ${secretsChecklist.length > 0 ? '4' : '3'}. Push to main — GitHub Actions will run ${chalk.cyan('deployhub build')} automatically`
|
|
531
|
+
);
|
|
518
532
|
}
|
|
519
533
|
|
|
520
534
|
console.log('');
|
|
@@ -353,6 +353,8 @@ export const DEPLOYMENT_ENV_KEYS = Object.fromEntries(
|
|
|
353
353
|
);
|
|
354
354
|
|
|
355
355
|
/**
|
|
356
|
+
* Locally required env keys for doctor method-specific checks.
|
|
357
|
+
* Excludes optional and CI-only vars.
|
|
356
358
|
* @param {string} deployType
|
|
357
359
|
* @param {import('../core/config.js').DeployHubConfig} [config]
|
|
358
360
|
* @returns {string[]}
|
|
@@ -373,7 +375,16 @@ export function getDeploymentEnvKeys(deployType, config = null) {
|
|
|
373
375
|
}
|
|
374
376
|
|
|
375
377
|
/**
|
|
376
|
-
*
|
|
378
|
+
* Map a def key to the GitHub Actions secret name (SSH_KEY_PATH → SSH_KEY).
|
|
379
|
+
* @param {string} key
|
|
380
|
+
*/
|
|
381
|
+
function toGithubSecretKey(key) {
|
|
382
|
+
return key === 'SSH_KEY_PATH' ? 'SSH_KEY' : key;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Genuinely required secrets for doctor "Secrets" check and required checklist items.
|
|
387
|
+
* Includes CI-only vars (e.g. SSH_KEY) but NOT optional vars.
|
|
377
388
|
* @param {string} deployType
|
|
378
389
|
* @param {import('../core/config.js').DeployHubConfig} [config]
|
|
379
390
|
* @returns {string[]}
|
|
@@ -388,22 +399,93 @@ export function getDeploymentSecretKeys(deployType, config = null) {
|
|
|
388
399
|
|
|
389
400
|
for (const d of defs) {
|
|
390
401
|
if (d.when === 'backend' && !isBackend) continue;
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
402
|
+
if (d.when === 'optional') continue;
|
|
403
|
+
keys.push(toGithubSecretKey(d.key));
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return [...new Set(keys)];
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Broad CI-wiring list for the GitHub Actions workflow generator.
|
|
411
|
+
* Includes required, CI-only, and optional keys so `${{ secrets.X }}` is
|
|
412
|
+
* available when the user sets optional secrets — empty secrets are fine.
|
|
413
|
+
* @param {string} deployType
|
|
414
|
+
* @param {import('../core/config.js').DeployHubConfig} [config]
|
|
415
|
+
* @returns {string[]}
|
|
416
|
+
*/
|
|
417
|
+
export function getDeploymentWorkflowSecretKeys(deployType, config = null) {
|
|
418
|
+
const defs = DEPLOYMENT_ENV_DEFS[deployType] || [];
|
|
419
|
+
const projectType = config?.projectType || 'frontend';
|
|
420
|
+
const isBackend = projectType === 'backend' || projectType === 'both';
|
|
421
|
+
|
|
422
|
+
/** @type {string[]} */
|
|
423
|
+
const keys = [];
|
|
424
|
+
|
|
425
|
+
for (const d of defs) {
|
|
426
|
+
if (d.when === 'backend' && !isBackend) continue;
|
|
427
|
+
keys.push(toGithubSecretKey(d.key));
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return [...new Set(keys)];
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* @typedef {{ key: string, required: boolean, note?: string }} SecretChecklistItem
|
|
435
|
+
*/
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Labeled GitHub Secrets checklist entries for a single deploy method.
|
|
439
|
+
* @param {string} deployType
|
|
440
|
+
* @param {import('../core/config.js').DeployHubConfig} [config]
|
|
441
|
+
* @returns {SecretChecklistItem[]}
|
|
442
|
+
*/
|
|
443
|
+
export function getDeploymentSecretChecklistItems(deployType, config = null) {
|
|
444
|
+
const defs = DEPLOYMENT_ENV_DEFS[deployType] || [];
|
|
445
|
+
const projectType = config?.projectType || 'frontend';
|
|
446
|
+
const isBackend = projectType === 'backend' || projectType === 'both';
|
|
447
|
+
|
|
448
|
+
/** @type {Map<string, SecretChecklistItem>} */
|
|
449
|
+
const byKey = new Map();
|
|
450
|
+
|
|
451
|
+
for (const d of defs) {
|
|
452
|
+
if (d.when === 'backend' && !isBackend) continue;
|
|
453
|
+
|
|
454
|
+
const key = toGithubSecretKey(d.key);
|
|
455
|
+
const required = d.when !== 'optional';
|
|
456
|
+
const note =
|
|
457
|
+
d.when === 'optional'
|
|
458
|
+
? d.optionalReason
|
|
459
|
+
: d.when === 'ci'
|
|
460
|
+
? d.optionalReason || 'required for GitHub Actions CI (paste private key contents)'
|
|
461
|
+
: undefined;
|
|
462
|
+
|
|
463
|
+
const existing = byKey.get(key);
|
|
464
|
+
if (existing) {
|
|
465
|
+
// Prefer required if any def for this key is required
|
|
466
|
+
if (required && !existing.required) {
|
|
467
|
+
byKey.set(key, { key, required: true, note });
|
|
396
468
|
}
|
|
397
469
|
continue;
|
|
398
470
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
continue;
|
|
402
|
-
}
|
|
403
|
-
keys.push(d.key);
|
|
471
|
+
|
|
472
|
+
byKey.set(key, { key, required, note });
|
|
404
473
|
}
|
|
405
474
|
|
|
406
|
-
return
|
|
475
|
+
return Array.from(byKey.values());
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Format a checklist item for console output.
|
|
480
|
+
* @param {SecretChecklistItem} item
|
|
481
|
+
*/
|
|
482
|
+
export function formatSecretChecklistLine(item) {
|
|
483
|
+
if (item.required) {
|
|
484
|
+
const note = item.note ? ` — ${item.note}` : '';
|
|
485
|
+
return `• ${item.key} (required${note})`;
|
|
486
|
+
}
|
|
487
|
+
const note = item.note ? ` — ${item.note}` : '';
|
|
488
|
+
return `• ${item.key} (optional${note})`;
|
|
407
489
|
}
|
|
408
490
|
|
|
409
491
|
/**
|
|
@@ -449,6 +531,7 @@ export function generateDeploymentEnvSection(
|
|
|
449
531
|
return lines.join('\n').trimEnd();
|
|
450
532
|
}
|
|
451
533
|
|
|
534
|
+
|
|
452
535
|
/**
|
|
453
536
|
* @param {string} key
|
|
454
537
|
* @param {import('../core/config.js').DeployHubConfig} [config]
|
|
@@ -527,8 +610,7 @@ export const DEPLOYMENT_GUIDE = {
|
|
|
527
610
|
'Copy .env.example to .env and set DOCKER_IMAGE_NAME (required — e.g. myuser/myapp).',
|
|
528
611
|
'Optional in .env: DOCKER_IMAGE_TAG, DOCKER_REGISTRY_URL, DOCKER_HOST, DOCKER_TLS_VERIFY, DOCKER_CERT_PATH.',
|
|
529
612
|
'If using a private registry: also set DOCKER_REGISTRY_USERNAME and DOCKER_REGISTRY_TOKEN.',
|
|
530
|
-
'Add GitHub Secrets (Settings → Secrets and variables → Actions)
|
|
531
|
-
'Also add as GitHub Secrets if set locally: DOCKER_IMAGE_TAG, DOCKER_REGISTRY_URL, DOCKER_REGISTRY_USERNAME, DOCKER_REGISTRY_TOKEN, DOCKER_HOST, DOCKER_TLS_VERIFY, DOCKER_CERT_PATH.',
|
|
613
|
+
'Add the GitHub Secrets listed below (Settings → Secrets and variables → Actions). Local .env is NOT used by GitHub Actions — doctor only checks your machine.',
|
|
532
614
|
'Run deployhub doctor to verify Docker is reachable.',
|
|
533
615
|
'git push origin main to trigger your first deployment.',
|
|
534
616
|
],
|
|
@@ -622,9 +704,9 @@ export const DEPLOYMENT_GUIDE = {
|
|
|
622
704
|
|
|
623
705
|
/**
|
|
624
706
|
* @param {string} deployType
|
|
625
|
-
* @param {
|
|
707
|
+
* @param {SecretChecklistItem[]} [checklist]
|
|
626
708
|
*/
|
|
627
|
-
export function printDeploymentNextSteps(deployType,
|
|
709
|
+
export function printDeploymentNextSteps(deployType, checklist = []) {
|
|
628
710
|
const guide = DEPLOYMENT_GUIDE[deployType];
|
|
629
711
|
if (!guide) return;
|
|
630
712
|
|
|
@@ -635,10 +717,10 @@ export function printDeploymentNextSteps(deployType, extraSecrets = []) {
|
|
|
635
717
|
console.log(` ${i + 1}. ${step}`);
|
|
636
718
|
});
|
|
637
719
|
|
|
638
|
-
if (
|
|
720
|
+
if (checklist.length > 0) {
|
|
639
721
|
console.log('\n GitHub Secrets to add (Settings → Secrets and variables → Actions):');
|
|
640
|
-
for (const
|
|
641
|
-
console.log(`
|
|
722
|
+
for (const item of checklist) {
|
|
723
|
+
console.log(` ${formatSecretChecklistLine(item)}`);
|
|
642
724
|
}
|
|
643
725
|
}
|
|
644
726
|
}
|
|
@@ -3,7 +3,8 @@ import path from 'path';
|
|
|
3
3
|
import { getWorkflowHeaderComment } from './author.js';
|
|
4
4
|
import {
|
|
5
5
|
generateDeploymentEnvSection,
|
|
6
|
-
|
|
6
|
+
getDeploymentWorkflowSecretKeys,
|
|
7
|
+
getDeploymentSecretChecklistItems,
|
|
7
8
|
DEPLOYMENT_ENV_KEYS,
|
|
8
9
|
} from '../deployment/deployment-env.js';
|
|
9
10
|
|
|
@@ -287,7 +288,7 @@ export function generateWorkflowYaml(
|
|
|
287
288
|
const env = environments[envName];
|
|
288
289
|
if (!env) continue;
|
|
289
290
|
|
|
290
|
-
const keys =
|
|
291
|
+
const keys = getDeploymentWorkflowSecretKeys(env.type, config);
|
|
291
292
|
for (const key of keys) {
|
|
292
293
|
envVars.add(`${key}: \${{ secrets.${key} }}`);
|
|
293
294
|
}
|
|
@@ -438,10 +439,12 @@ export async function guessCliGithubRepo(cwd = process.cwd()) {
|
|
|
438
439
|
}
|
|
439
440
|
|
|
440
441
|
/**
|
|
442
|
+
* Flat list of required secret key names (for callers that only need keys).
|
|
441
443
|
* @param {string[]} storageProviders
|
|
442
444
|
* @param {string[]} deployEnvironments
|
|
443
445
|
* @param {Record<string, { type: string }>} environments
|
|
444
446
|
* @param {import('../core/config.js').DeployHubConfig} [config]
|
|
447
|
+
* @param {string|null} [cliSource]
|
|
445
448
|
* @returns {string[]}
|
|
446
449
|
*/
|
|
447
450
|
export function getRequiredSecrets(
|
|
@@ -451,28 +454,69 @@ export function getRequiredSecrets(
|
|
|
451
454
|
config = null,
|
|
452
455
|
cliSource = null
|
|
453
456
|
) {
|
|
454
|
-
|
|
455
|
-
|
|
457
|
+
return getGithubSecretsChecklist(
|
|
458
|
+
storageProviders,
|
|
459
|
+
deployEnvironments,
|
|
460
|
+
environments,
|
|
461
|
+
config,
|
|
462
|
+
cliSource
|
|
463
|
+
)
|
|
464
|
+
.filter((item) => item.required)
|
|
465
|
+
.map((item) => item.key);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Labeled GitHub Secrets checklist (required + optional) for post-init output.
|
|
470
|
+
* @param {string[]} storageProviders
|
|
471
|
+
* @param {string[]} deployEnvironments
|
|
472
|
+
* @param {Record<string, { type: string }>} environments
|
|
473
|
+
* @param {import('../core/config.js').DeployHubConfig} [config]
|
|
474
|
+
* @param {string|null} [cliSource]
|
|
475
|
+
* @returns {import('../deployment/deployment-env.js').SecretChecklistItem[]}
|
|
476
|
+
*/
|
|
477
|
+
export function getGithubSecretsChecklist(
|
|
478
|
+
storageProviders,
|
|
479
|
+
deployEnvironments,
|
|
480
|
+
environments,
|
|
481
|
+
config = null,
|
|
482
|
+
cliSource = null
|
|
483
|
+
) {
|
|
484
|
+
/** @type {Map<string, import('../deployment/deployment-env.js').SecretChecklistItem>} */
|
|
485
|
+
const byKey = new Map();
|
|
456
486
|
|
|
457
487
|
const resolvedCliSource = cliSource || config?.cli?.source;
|
|
458
488
|
if (isGithubCliSource(resolvedCliSource)) {
|
|
459
|
-
|
|
489
|
+
byKey.set(GITHUB_CLI_TOKEN_SECRET, {
|
|
490
|
+
key: GITHUB_CLI_TOKEN_SECRET,
|
|
491
|
+
required: true,
|
|
492
|
+
note: 'required when installing DeployHub CLI from a private GitHub repo',
|
|
493
|
+
});
|
|
460
494
|
}
|
|
461
495
|
|
|
462
496
|
for (const provider of storageProviders) {
|
|
463
497
|
const keys = PROVIDER_ENV_MAP[provider] || [];
|
|
464
|
-
|
|
498
|
+
for (const key of keys) {
|
|
499
|
+
byKey.set(key, { key, required: true });
|
|
500
|
+
}
|
|
465
501
|
}
|
|
466
502
|
|
|
467
503
|
for (const envName of deployEnvironments) {
|
|
468
504
|
const env = environments[envName];
|
|
469
|
-
if (!env) continue;
|
|
505
|
+
if (!env?.type) continue;
|
|
470
506
|
|
|
471
|
-
const
|
|
472
|
-
|
|
507
|
+
for (const item of getDeploymentSecretChecklistItems(env.type, config)) {
|
|
508
|
+
const existing = byKey.get(item.key);
|
|
509
|
+
if (existing) {
|
|
510
|
+
if (item.required && !existing.required) {
|
|
511
|
+
byKey.set(item.key, item);
|
|
512
|
+
}
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
byKey.set(item.key, item);
|
|
516
|
+
}
|
|
473
517
|
}
|
|
474
518
|
|
|
475
|
-
return Array.from(
|
|
519
|
+
return Array.from(byKey.values());
|
|
476
520
|
}
|
|
477
521
|
|
|
478
522
|
/**
|