@akash-chowdhury-24/deployhub 2.0.0 → 2.0.3
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 +227 -5
- package/package.json +4 -3
- package/src/commands/deploy.js +1 -1
- package/src/commands/doctor.js +244 -30
- package/src/commands/init.js +66 -223
- package/src/commands/rollback.js +1 -1
- package/src/core/config.js +15 -0
- package/src/deployment/deployment-env.js +625 -0
- package/src/deployment/init-helpers.js +471 -0
- package/src/deployment/init-prompts.js +434 -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 +70 -42
- package/src/utils/github-actions.js +24 -37
- package/src/{rollback → utils/rollback}/engine.js +6 -6
- package/src/utils/shell-quote.js +64 -0
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import fs from 'fs-extra';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { getWorkflowHeaderComment } from './author.js';
|
|
4
|
+
import {
|
|
5
|
+
generateDeploymentEnvSection,
|
|
6
|
+
getDeploymentSecretKeys,
|
|
7
|
+
DEPLOYMENT_ENV_KEYS,
|
|
8
|
+
} from '../deployment/deployment-env.js';
|
|
4
9
|
|
|
5
10
|
/** @typedef {'aws'|'azure'|'gcp'|'gdrive'|'dropbox'|'local'|'ftp'|'ssh'} ProviderEnvKey */
|
|
6
11
|
|
|
@@ -22,23 +27,27 @@ const PROVIDER_ENV_MAP = {
|
|
|
22
27
|
dropbox: ['DROPBOX_ACCESS_TOKEN'],
|
|
23
28
|
local: [],
|
|
24
29
|
ftp: ['FTP_HOST', 'FTP_USER', 'FTP_PASSWORD'],
|
|
25
|
-
ssh:
|
|
30
|
+
ssh: DEPLOYMENT_ENV_KEYS.ssh,
|
|
31
|
+
docker: DEPLOYMENT_ENV_KEYS.docker,
|
|
32
|
+
ec2: DEPLOYMENT_ENV_KEYS.ec2,
|
|
33
|
+
'azure-vm': DEPLOYMENT_ENV_KEYS['azure-vm'],
|
|
34
|
+
'gcp-vm': DEPLOYMENT_ENV_KEYS['gcp-vm'],
|
|
35
|
+
kubernetes: DEPLOYMENT_ENV_KEYS.kubernetes,
|
|
26
36
|
};
|
|
27
37
|
|
|
28
|
-
const BACKEND_SSH_ENV_VARS = [
|
|
29
|
-
'SSH_DEPLOY_PATH',
|
|
30
|
-
'SSH_APP_NAME',
|
|
31
|
-
'SSH_PORT',
|
|
32
|
-
];
|
|
33
|
-
|
|
34
38
|
const PROVIDER_LABELS = {
|
|
35
39
|
aws: 'AWS S3',
|
|
36
40
|
azure: 'Azure Blob',
|
|
37
|
-
gcp: 'GCP',
|
|
41
|
+
gcp: 'GCP Storage',
|
|
38
42
|
gdrive: 'Google Drive',
|
|
39
43
|
dropbox: 'Dropbox',
|
|
40
44
|
ftp: 'FTP',
|
|
41
45
|
ssh: 'SSH Deployment',
|
|
46
|
+
docker: 'Docker Deployment',
|
|
47
|
+
ec2: 'AWS EC2 Deployment',
|
|
48
|
+
'azure-vm': 'Azure VM Deployment',
|
|
49
|
+
'gcp-vm': 'GCP VM Deployment',
|
|
50
|
+
kubernetes: 'Kubernetes Deployment',
|
|
42
51
|
};
|
|
43
52
|
|
|
44
53
|
const ENV_VAR_DEFAULTS = {
|
|
@@ -46,6 +55,7 @@ const ENV_VAR_DEFAULTS = {
|
|
|
46
55
|
FTP_PORT: '21',
|
|
47
56
|
FTP_PATH: '/uploads',
|
|
48
57
|
SSH_DEPLOY_PATH: '/var/www/app',
|
|
58
|
+
SSH_SSH_PORT: '22',
|
|
49
59
|
SMTP_PORT: '587',
|
|
50
60
|
};
|
|
51
61
|
|
|
@@ -227,19 +237,10 @@ export function generateWorkflowYaml(
|
|
|
227
237
|
const env = environments[envName];
|
|
228
238
|
if (!env) continue;
|
|
229
239
|
|
|
230
|
-
const keys =
|
|
240
|
+
const keys = getDeploymentSecretKeys(env.type, config);
|
|
231
241
|
for (const key of keys) {
|
|
232
242
|
envVars.add(`${key}: \${{ secrets.${key} }}`);
|
|
233
243
|
}
|
|
234
|
-
|
|
235
|
-
if (env.type === 'ssh' && config) {
|
|
236
|
-
const projectType = config.projectType || 'frontend';
|
|
237
|
-
if (projectType === 'backend' || projectType === 'both') {
|
|
238
|
-
for (const key of BACKEND_SSH_ENV_VARS) {
|
|
239
|
-
envVars.add(`${key}: \${{ secrets.${key} }}`);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
244
|
}
|
|
244
245
|
|
|
245
246
|
const envBlock = Array.from(envVars)
|
|
@@ -412,15 +413,8 @@ export function getRequiredSecrets(
|
|
|
412
413
|
const env = environments[envName];
|
|
413
414
|
if (!env) continue;
|
|
414
415
|
|
|
415
|
-
const keys =
|
|
416
|
+
const keys = getDeploymentSecretKeys(env.type, config);
|
|
416
417
|
keys.forEach((k) => secrets.add(k));
|
|
417
|
-
|
|
418
|
-
if (env.type === 'ssh' && config) {
|
|
419
|
-
const projectType = config.projectType || 'frontend';
|
|
420
|
-
if (projectType === 'backend' || projectType === 'both') {
|
|
421
|
-
BACKEND_SSH_ENV_VARS.forEach((k) => secrets.add(k));
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
418
|
}
|
|
425
419
|
|
|
426
420
|
return Array.from(secrets);
|
|
@@ -481,18 +475,11 @@ export function generateEnvExampleContent(
|
|
|
481
475
|
|
|
482
476
|
for (const envName of deployEnvironments) {
|
|
483
477
|
const env = environments[envName];
|
|
484
|
-
if (!env) continue;
|
|
485
|
-
|
|
486
|
-
const keys = PROVIDER_ENV_MAP[env.type] || [];
|
|
487
|
-
if (keys.length > 0) {
|
|
488
|
-
addSection(PROVIDER_LABELS[env.type] || env.type, keys);
|
|
489
|
-
}
|
|
478
|
+
if (!env?.type) continue;
|
|
490
479
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
addSection('SSH Deployment (backend)', BACKEND_SSH_ENV_VARS);
|
|
495
|
-
}
|
|
480
|
+
const deploySection = generateDeploymentEnvSection(env.type, config, environments);
|
|
481
|
+
if (deploySection) {
|
|
482
|
+
sections.push(`${deploySection}\n`);
|
|
496
483
|
}
|
|
497
484
|
}
|
|
498
485
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { downloadFromFirst } from '
|
|
2
|
-
import { getDeploymentProvider } from '
|
|
3
|
-
import { extractArtifact } from '
|
|
4
|
-
import { createLogger } from '
|
|
1
|
+
import { downloadFromFirst } from '../../storage/index.js';
|
|
2
|
+
import { getDeploymentProvider } from '../../deployment/index.js';
|
|
3
|
+
import { extractArtifact } from '../../artifact/engine.js';
|
|
4
|
+
import { createLogger } from '../../logger/index.js';
|
|
5
5
|
import fs from 'fs-extra';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* @param {import('
|
|
9
|
+
* @param {import('../../core/config.js').DeployHubConfig} config
|
|
10
10
|
* @param {string} artifactDir
|
|
11
11
|
* @param {string} envName
|
|
12
12
|
*/
|
|
@@ -24,7 +24,7 @@ async function rollbackTarget(config, artifactDir, envName) {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* @param {import('
|
|
27
|
+
* @param {import('../../core/config.js').DeployHubConfig} config
|
|
28
28
|
* @param {string} version
|
|
29
29
|
* @param {string} [cwd]
|
|
30
30
|
*/
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wrap a value for safe interpolation into a POSIX shell command string.
|
|
3
|
+
* Uses single quotes; embedded single quotes are escaped as '\''.
|
|
4
|
+
* @param {string} value
|
|
5
|
+
* @returns {string}
|
|
6
|
+
*/
|
|
7
|
+
export function shellQuote(value) {
|
|
8
|
+
if (value == null) return "''";
|
|
9
|
+
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} command
|
|
14
|
+
* @param {number|null|undefined} code
|
|
15
|
+
* @param {string} [stderr]
|
|
16
|
+
* @param {string} [stdout]
|
|
17
|
+
* @returns {string}
|
|
18
|
+
*/
|
|
19
|
+
export function formatRemoteCommandFailure(command, code, stderr, stdout) {
|
|
20
|
+
const cmdName = command.trim().split(/\s+/)[0] || 'command';
|
|
21
|
+
const detail = (stderr || stdout || '').trim();
|
|
22
|
+
return `Deploy failed: ${cmdName} exited with code ${code}${detail ? `: ${detail}` : ''}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Remote shell command that verifies write access to a deploy directory.
|
|
27
|
+
* @param {string} deployPath
|
|
28
|
+
* @returns {string}
|
|
29
|
+
*/
|
|
30
|
+
export function buildDeployPathWriteTestCommand(deployPath) {
|
|
31
|
+
const dir = shellQuote(deployPath);
|
|
32
|
+
const testFile = shellQuote(`${deployPath}/.deployhub-write-test`);
|
|
33
|
+
return `mkdir -p ${dir} && touch ${testFile} && rm -f ${testFile}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {string} deployPath
|
|
38
|
+
* @param {string} sshUser
|
|
39
|
+
* @param {string} [errorDetail]
|
|
40
|
+
* @returns {string}
|
|
41
|
+
*/
|
|
42
|
+
export function formatDeployPathWriteFailure(deployPath, sshUser, errorDetail) {
|
|
43
|
+
const reason = errorDetail ? ` (${errorDetail.trim()})` : '';
|
|
44
|
+
const quotedPath = shellQuote(deployPath);
|
|
45
|
+
return (
|
|
46
|
+
`Cannot write to ${deployPath} on the server${reason}.\n` +
|
|
47
|
+
` Run this on your server first:\n` +
|
|
48
|
+
` sudo mkdir -p ${quotedPath}\n` +
|
|
49
|
+
` sudo chown ${sshUser}:${sshUser} ${quotedPath}\n` +
|
|
50
|
+
` (replace ${sshUser} with your actual SSH_USER if different)`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {string} name
|
|
56
|
+
* @returns {string}
|
|
57
|
+
*/
|
|
58
|
+
export function toKebabCase(name) {
|
|
59
|
+
return name
|
|
60
|
+
.trim()
|
|
61
|
+
.replace(/\s+/g, '-')
|
|
62
|
+
.replace(/[^a-zA-Z0-9-_]/g, '')
|
|
63
|
+
.toLowerCase();
|
|
64
|
+
}
|