d3ployer 0.0.10 → 0.0.12
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 +46 -30
- package/dist/bin.js +0 -0
- package/dist/cli.js +9 -4
- package/dist/config.d.ts +0 -1
- package/dist/config.js +27 -35
- package/dist/def.d.ts +26 -20
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/renderer.d.ts +23 -0
- package/dist/renderer.js +153 -0
- package/dist/runner.d.ts +8 -4
- package/dist/runner.js +36 -28
- package/dist/tasks/clearTarget.d.ts +2 -0
- package/dist/tasks/clearTarget.js +15 -0
- package/dist/tasks/download.d.ts +3 -0
- package/dist/tasks/download.js +27 -0
- package/dist/tasks/index.d.ts +6 -0
- package/dist/tasks/index.js +76 -0
- package/dist/tasks/installPackages.d.ts +3 -0
- package/dist/tasks/installPackages.js +38 -0
- package/dist/tasks/printDeployment.d.ts +2 -0
- package/dist/tasks/printDeployment.js +8 -0
- package/dist/tasks/printLogs.d.ts +5 -0
- package/dist/tasks/printLogs.js +69 -0
- package/dist/tasks/setupDocker.d.ts +4 -0
- package/dist/tasks/setupDocker.js +33 -0
- package/dist/tasks/setupPm2.d.ts +3 -0
- package/dist/tasks/setupPm2.js +14 -0
- package/dist/tasks/symlinks.d.ts +3 -0
- package/dist/tasks/symlinks.js +18 -0
- package/dist/tasks/upload.d.ts +7 -0
- package/dist/tasks/upload.js +54 -0
- package/package.json +4 -4
- package/dist/defaultTasks.d.ts +0 -9
- package/dist/defaultTasks.js +0 -303
package/dist/runner.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
1
|
import { Listr } from 'listr2';
|
|
2
|
+
import { CustomRenderer } from './renderer.js';
|
|
3
3
|
import { spawn } from 'node:child_process';
|
|
4
4
|
import { createSSHConnection } from './connection.js';
|
|
5
5
|
import { Exception } from './utils/Exception.js';
|
|
@@ -148,32 +148,32 @@ function buildTaskContext(serverName, server, ssh, config) {
|
|
|
148
148
|
};
|
|
149
149
|
}
|
|
150
150
|
export function resolveServers(config, serverNames) {
|
|
151
|
-
const allEntries = Object.entries(config.servers);
|
|
152
151
|
if (!serverNames || serverNames.length === 0) {
|
|
153
|
-
return
|
|
152
|
+
return config.servers;
|
|
154
153
|
}
|
|
155
|
-
const result =
|
|
154
|
+
const result = {};
|
|
156
155
|
for (const name of serverNames) {
|
|
157
156
|
const server = config.servers[name];
|
|
158
157
|
if (!server) {
|
|
159
158
|
const available = Object.keys(config.servers).join(', ');
|
|
160
159
|
throw new Exception(`Server "${name}" not found. Available: ${available}`, 1774742073310);
|
|
161
160
|
}
|
|
162
|
-
result
|
|
161
|
+
result[name] = server;
|
|
163
162
|
}
|
|
164
163
|
return result;
|
|
165
164
|
}
|
|
166
165
|
export function resolveTaskDefs(taskNames, allTasks) {
|
|
167
|
-
return
|
|
166
|
+
return Object.fromEntries(taskNames
|
|
167
|
+
.map(name => {
|
|
168
168
|
const def = allTasks[name];
|
|
169
169
|
if (!def) {
|
|
170
170
|
const available = Object.keys(allTasks).join(', ');
|
|
171
171
|
throw new Exception(`Task "${name}" not found. Available: ${available}`, 1774742082083);
|
|
172
172
|
}
|
|
173
173
|
return [name, def];
|
|
174
|
-
});
|
|
174
|
+
}));
|
|
175
175
|
}
|
|
176
|
-
function buildServerListr(serverName, server, config, tasks) {
|
|
176
|
+
function buildServerListr(serverName, server, config, tasks, skipTasks = []) {
|
|
177
177
|
return new Listr([
|
|
178
178
|
{
|
|
179
179
|
task: async (ctx) => {
|
|
@@ -184,17 +184,21 @@ function buildServerListr(serverName, server, config, tasks) {
|
|
|
184
184
|
ctx.ph = buildPlaceholders(serverName, server);
|
|
185
185
|
},
|
|
186
186
|
},
|
|
187
|
-
...
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
return
|
|
187
|
+
...Object.entries(tasks)
|
|
188
|
+
.map(([key, taskDef]) => ({
|
|
189
|
+
title: taskDef.name,
|
|
190
|
+
skip: (ctx) => {
|
|
191
|
+
if (skipTasks.includes(key)) {
|
|
192
|
+
return 'Skipped via --skip';
|
|
193
193
|
}
|
|
194
|
-
: undefined,
|
|
195
|
-
task: async (ctx) => {
|
|
196
194
|
ctx.taskCtx.taskConfig = taskDef.config;
|
|
197
|
-
return taskDef.
|
|
195
|
+
return taskDef.skip
|
|
196
|
+
? taskDef.skip(ctx.taskCtx, ctx.ph)
|
|
197
|
+
: false;
|
|
198
|
+
},
|
|
199
|
+
task: async (ctx, task) => {
|
|
200
|
+
ctx.taskCtx.taskConfig = taskDef.config;
|
|
201
|
+
return taskDef.task(ctx.taskCtx, ctx.ph, task);
|
|
198
202
|
},
|
|
199
203
|
})),
|
|
200
204
|
{
|
|
@@ -206,10 +210,10 @@ function buildServerListr(serverName, server, config, tasks) {
|
|
|
206
210
|
},
|
|
207
211
|
], {
|
|
208
212
|
concurrent: false,
|
|
209
|
-
renderer:
|
|
213
|
+
renderer: CustomRenderer,
|
|
210
214
|
});
|
|
211
215
|
}
|
|
212
|
-
export async function runScenario(config, scenarioName, serverNames) {
|
|
216
|
+
export async function runScenario(config, scenarioName, serverNames, options = {}) {
|
|
213
217
|
const scenarioDef = config.scenarios?.[scenarioName];
|
|
214
218
|
if (!scenarioDef) {
|
|
215
219
|
const available = Object.keys(config.scenarios ?? {}).join(', ') || 'none';
|
|
@@ -218,16 +222,18 @@ export async function runScenario(config, scenarioName, serverNames) {
|
|
|
218
222
|
const allTasks = config.tasks ?? {};
|
|
219
223
|
const tasks = resolveTaskDefs(scenarioDef.tasks, allTasks);
|
|
220
224
|
const servers = resolveServers(config, serverNames);
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
225
|
+
const skipTasks = options.skip ?? [];
|
|
226
|
+
const listr = new Listr(Object.entries(servers)
|
|
227
|
+
.map(([name, server]) => ({
|
|
228
|
+
title: `${scenarioDef.name} to ${name} (${server.host})`,
|
|
229
|
+
task: () => buildServerListr(name, server, config, tasks, skipTasks),
|
|
224
230
|
})), {
|
|
225
231
|
concurrent: false,
|
|
226
|
-
renderer:
|
|
232
|
+
renderer: CustomRenderer,
|
|
227
233
|
});
|
|
228
234
|
await listr.run();
|
|
229
235
|
}
|
|
230
|
-
export async function runTask(config, taskName, serverNames) {
|
|
236
|
+
export async function runTask(config, taskName, serverNames, options = {}) {
|
|
231
237
|
const allTasks = config.tasks ?? {};
|
|
232
238
|
const taskDef = allTasks[taskName];
|
|
233
239
|
if (!taskDef) {
|
|
@@ -235,12 +241,14 @@ export async function runTask(config, taskName, serverNames) {
|
|
|
235
241
|
throw new Exception(`Task "${taskName}" not found. Available: ${available}`, 1774742100356);
|
|
236
242
|
}
|
|
237
243
|
const servers = resolveServers(config, serverNames);
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
244
|
+
const skipTasks = options.skip ?? [];
|
|
245
|
+
const listr = new Listr(Object.entries(servers)
|
|
246
|
+
.map(([name, server]) => ({
|
|
247
|
+
title: `${taskDef.name} to ${name} (${server.host})`,
|
|
248
|
+
task: () => buildServerListr(name, server, config, { [taskName]: taskDef }, skipTasks),
|
|
241
249
|
})), {
|
|
242
250
|
concurrent: false,
|
|
243
|
-
renderer:
|
|
251
|
+
renderer: CustomRenderer,
|
|
244
252
|
});
|
|
245
253
|
await listr.run();
|
|
246
254
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ListrEnquirerPromptAdapter } from '@listr2/prompt-adapter-enquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
export const clearTargetTask = async (ctx, ph, task) => {
|
|
4
|
+
const confirmed = await task.prompt(ListrEnquirerPromptAdapter).run({
|
|
5
|
+
type: 'Confirm',
|
|
6
|
+
message: chalk.red(`Remove entire deploy path ${ph.deployPath} on ${ctx.server.host}?`),
|
|
7
|
+
initial: false,
|
|
8
|
+
});
|
|
9
|
+
console.log();
|
|
10
|
+
if (!confirmed) {
|
|
11
|
+
task.skip('User cancelled');
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
await ctx.run(`rm -rf ${ph.deployPath}`);
|
|
15
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { Exception } from '../utils/index.js';
|
|
4
|
+
import { buildRsyncCommand } from './upload.js';
|
|
5
|
+
export const downloadSkip = (ctx) => {
|
|
6
|
+
const files = ctx.taskConfig;
|
|
7
|
+
return !files
|
|
8
|
+
? 'No files configuration provided in task config'
|
|
9
|
+
: false;
|
|
10
|
+
};
|
|
11
|
+
export const downloadTask = async (ctx, ph) => {
|
|
12
|
+
const files = ctx.taskConfig;
|
|
13
|
+
if (!files) {
|
|
14
|
+
throw new Exception('No files configuration provided in task config', 1784523741234);
|
|
15
|
+
}
|
|
16
|
+
const localBase = files.basePath?.startsWith('/')
|
|
17
|
+
? files.basePath
|
|
18
|
+
: path.resolve(ctx.config.rootDir, files.basePath ?? '.');
|
|
19
|
+
const remotePath = ph.deployPath;
|
|
20
|
+
const source = `${ctx.server.username}@${ctx.server.host}:${remotePath}/`;
|
|
21
|
+
const dest = localBase.endsWith('/') ? localBase : localBase + '/';
|
|
22
|
+
const command = buildRsyncCommand(ctx.server, source, dest, files, {
|
|
23
|
+
delete: false,
|
|
24
|
+
});
|
|
25
|
+
console.log(chalk.grey(command));
|
|
26
|
+
await ctx.runLocal(command);
|
|
27
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ScenarioDef, TaskDef } from '../def.js';
|
|
2
|
+
export { buildRsyncCommand } from './upload.js';
|
|
3
|
+
export type { RsyncOptions } from './upload.js';
|
|
4
|
+
export { downloadSkip, downloadTask } from './download.js';
|
|
5
|
+
export declare const defaultTasks: Record<string, TaskDef>;
|
|
6
|
+
export declare const defaultScenarios: Record<string, ScenarioDef>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { clearTargetTask } from './clearTarget.js';
|
|
2
|
+
import { downloadSkip, downloadTask } from './download.js';
|
|
3
|
+
import { installPackagesSkip, installPackagesTask } from './installPackages.js';
|
|
4
|
+
import { printDeploymentTask } from './printDeployment.js';
|
|
5
|
+
import { printLogsDockerSkip, printLogsDockerTask, printLogsPm2Skip, printLogsPm2Task } from './printLogs.js';
|
|
6
|
+
import { setupDockerSkip, setupDockerTask } from './setupDocker.js';
|
|
7
|
+
import { setupPm2Skip, setupPm2Task } from './setupPm2.js';
|
|
8
|
+
import { symlinksSkip, symlinksTask } from './symlinks.js';
|
|
9
|
+
import { uploadSkip, uploadTask } from './upload.js';
|
|
10
|
+
export { buildRsyncCommand } from './upload.js';
|
|
11
|
+
export { downloadSkip, downloadTask } from './download.js';
|
|
12
|
+
export const defaultTasks = {
|
|
13
|
+
'clear:target': {
|
|
14
|
+
name: 'Clear target',
|
|
15
|
+
task: clearTargetTask,
|
|
16
|
+
},
|
|
17
|
+
upload: {
|
|
18
|
+
name: 'Upload files',
|
|
19
|
+
skip: uploadSkip,
|
|
20
|
+
task: uploadTask,
|
|
21
|
+
},
|
|
22
|
+
download: {
|
|
23
|
+
name: 'Download files',
|
|
24
|
+
skip: downloadSkip,
|
|
25
|
+
task: downloadTask,
|
|
26
|
+
},
|
|
27
|
+
symlinks: {
|
|
28
|
+
name: 'Create symlinks',
|
|
29
|
+
skip: symlinksSkip,
|
|
30
|
+
task: symlinksTask,
|
|
31
|
+
},
|
|
32
|
+
'install:packages': {
|
|
33
|
+
name: 'Install packages',
|
|
34
|
+
skip: installPackagesSkip,
|
|
35
|
+
task: installPackagesTask,
|
|
36
|
+
},
|
|
37
|
+
'setup:pm2': {
|
|
38
|
+
name: 'PM2 setup',
|
|
39
|
+
skip: setupPm2Skip,
|
|
40
|
+
task: setupPm2Task,
|
|
41
|
+
},
|
|
42
|
+
'setup:docker': {
|
|
43
|
+
name: 'Docker Compose setup',
|
|
44
|
+
skip: setupDockerSkip,
|
|
45
|
+
task: setupDockerTask,
|
|
46
|
+
},
|
|
47
|
+
'print:deployment': {
|
|
48
|
+
name: 'Print deployment info',
|
|
49
|
+
task: printDeploymentTask,
|
|
50
|
+
},
|
|
51
|
+
'print:logs:pm2': {
|
|
52
|
+
name: 'Print PM2 logs',
|
|
53
|
+
skip: printLogsPm2Skip,
|
|
54
|
+
task: printLogsPm2Task,
|
|
55
|
+
},
|
|
56
|
+
'print:logs:docker': {
|
|
57
|
+
name: 'Print docker logs',
|
|
58
|
+
skip: printLogsDockerSkip,
|
|
59
|
+
task: printLogsDockerTask,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
export const defaultScenarios = {
|
|
63
|
+
deploy: {
|
|
64
|
+
name: 'Deploy',
|
|
65
|
+
tasks: [
|
|
66
|
+
'upload',
|
|
67
|
+
'symlinks',
|
|
68
|
+
'install:packages',
|
|
69
|
+
'setup:pm2',
|
|
70
|
+
'setup:docker',
|
|
71
|
+
'print:deployment',
|
|
72
|
+
'print:logs:pm2',
|
|
73
|
+
'print:logs:docker',
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Exception } from '../utils/index.js';
|
|
2
|
+
export const installPackagesSkip = (ctx) => {
|
|
3
|
+
if (ctx.server.packageManager !== undefined) {
|
|
4
|
+
return ctx.server.packageManager === false
|
|
5
|
+
? 'Package manager disabled for server'
|
|
6
|
+
: false;
|
|
7
|
+
}
|
|
8
|
+
if (ctx.config.packageManager !== undefined) {
|
|
9
|
+
return ctx.config.packageManager === false
|
|
10
|
+
? 'Package manager disabled in config'
|
|
11
|
+
: false;
|
|
12
|
+
}
|
|
13
|
+
return false;
|
|
14
|
+
};
|
|
15
|
+
export const installPackagesTask = async (ctx) => {
|
|
16
|
+
const config = {
|
|
17
|
+
manager: 'npm',
|
|
18
|
+
productionOnly: true,
|
|
19
|
+
...ctx.config.packageManager,
|
|
20
|
+
...ctx.server.packageManager,
|
|
21
|
+
};
|
|
22
|
+
let cmd = `${config.manager} install`;
|
|
23
|
+
if (config.productionOnly) {
|
|
24
|
+
if (config.manager === 'npm') {
|
|
25
|
+
cmd += ' --omit=dev';
|
|
26
|
+
}
|
|
27
|
+
else if (config.manager === 'yarn') {
|
|
28
|
+
cmd += ' --production';
|
|
29
|
+
}
|
|
30
|
+
else if (config.manager === 'pnpm') {
|
|
31
|
+
cmd += ' --prod';
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
throw new Exception(`Unsupported package manager "${config.manager}"`, 1774823752134);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
await ctx.run(cmd);
|
|
38
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
export const printDeploymentTask = async (ctx, ph) => {
|
|
3
|
+
await ctx.run('date');
|
|
4
|
+
console.log(chalk.cyan('Deployment directory'), ph.deployPath);
|
|
5
|
+
await ctx.run('ls -la .');
|
|
6
|
+
console.log(chalk.cyan('Directory size'));
|
|
7
|
+
await ctx.run('du -hd 1 .');
|
|
8
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { buildDockerComposeTestCmd } from './setupDocker.js';
|
|
3
|
+
export const printLogsPm2Skip = async (ctx) => {
|
|
4
|
+
if (ctx.config.pm2 === false
|
|
5
|
+
|| ctx.config.pm2.logs === false) {
|
|
6
|
+
return 'Logs disabled';
|
|
7
|
+
}
|
|
8
|
+
const hasPm2 = await ctx.test('test -f pm2.config.*');
|
|
9
|
+
if (!hasPm2) {
|
|
10
|
+
return 'No PM2 detected';
|
|
11
|
+
}
|
|
12
|
+
return false;
|
|
13
|
+
};
|
|
14
|
+
export const printLogsDockerSkip = async (ctx) => {
|
|
15
|
+
if (ctx.config.dockerCompose === false
|
|
16
|
+
|| ctx.config.dockerCompose.logs === false) {
|
|
17
|
+
return 'Logs disabled';
|
|
18
|
+
}
|
|
19
|
+
const testCmd = buildDockerComposeTestCmd(ctx.config.dockerCompose);
|
|
20
|
+
const hasDocker = await ctx.test(testCmd);
|
|
21
|
+
if (!hasDocker) {
|
|
22
|
+
return 'No Docker Compose detected';
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
};
|
|
26
|
+
export const printLogsPm2Task = async (ctx) => {
|
|
27
|
+
if (ctx.config.pm2 === false
|
|
28
|
+
|| ctx.config.pm2.logs === false) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const logsConfig = {
|
|
32
|
+
time: 3,
|
|
33
|
+
lines: 25,
|
|
34
|
+
...ctx.config.pm2.logs,
|
|
35
|
+
};
|
|
36
|
+
const hasPm2 = await ctx.test('test -f pm2.config.*');
|
|
37
|
+
if (hasPm2) {
|
|
38
|
+
const pm2ConfigRaw = await ctx.run('cat pm2.config.*', { printOutput: false });
|
|
39
|
+
const nameMatch = pm2ConfigRaw.stdout.match(/name: ['"](?<name>.+?)['"]/);
|
|
40
|
+
const name = nameMatch.groups?.name ?? 'all';
|
|
41
|
+
console.log(chalk.cyan(`Streaming PM2 logs for ${logsConfig.time}s...`));
|
|
42
|
+
await ctx.run(`timeout ${logsConfig.time} pm2 logs --lines=${logsConfig.lines} "${name}" || true`, {
|
|
43
|
+
printOutput: true,
|
|
44
|
+
ignoreError: true,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
export const printLogsDockerTask = async (ctx) => {
|
|
49
|
+
if (ctx.config.dockerCompose === false
|
|
50
|
+
|| ctx.config.dockerCompose.logs === false) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const logsConfig = {
|
|
54
|
+
time: 3,
|
|
55
|
+
lines: 25,
|
|
56
|
+
...ctx.config.dockerCompose.logs,
|
|
57
|
+
};
|
|
58
|
+
const testCmd = buildDockerComposeTestCmd(ctx.config.dockerCompose);
|
|
59
|
+
const hasDocker = await ctx.test(testCmd);
|
|
60
|
+
if (hasDocker) {
|
|
61
|
+
const configFiles = ctx.config.dockerCompose.configFiles ?? [];
|
|
62
|
+
const options = configFiles.map(f => `-f ${f}`).join(' ');
|
|
63
|
+
console.log(chalk.cyan(`Streaming Docker Compose logs for ${logsConfig.time}s...`));
|
|
64
|
+
await ctx.run(`timeout ${logsConfig.time} docker compose ${options} logs --tail=${logsConfig.lines} -f || true`, {
|
|
65
|
+
printOutput: true,
|
|
66
|
+
ignoreError: true,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DockerComposeConfig, TaskFn, TaskSkipFn } from '../def.js';
|
|
2
|
+
export declare function buildDockerComposeTestCmd(dockerComposeConfig: DockerComposeConfig | false): string;
|
|
3
|
+
export declare const setupDockerSkip: TaskSkipFn;
|
|
4
|
+
export declare const setupDockerTask: TaskFn;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function buildDockerComposeTestCmd(dockerComposeConfig) {
|
|
2
|
+
if (dockerComposeConfig === false) {
|
|
3
|
+
return 'false';
|
|
4
|
+
}
|
|
5
|
+
const configFiles = dockerComposeConfig.configFiles ?? [
|
|
6
|
+
'docker-compose.yml',
|
|
7
|
+
'docker-compose.yaml',
|
|
8
|
+
'compose.yml',
|
|
9
|
+
'compose.yaml',
|
|
10
|
+
];
|
|
11
|
+
const testCmdPart = configFiles.map(f => `-f ${f}`);
|
|
12
|
+
return `test ${testCmdPart.join(' -o ')}`;
|
|
13
|
+
}
|
|
14
|
+
export const setupDockerSkip = async (ctx) => {
|
|
15
|
+
if (ctx.config.dockerCompose === false) {
|
|
16
|
+
return 'Docker Compose disabled';
|
|
17
|
+
}
|
|
18
|
+
const testCmd = buildDockerComposeTestCmd(ctx.config.dockerCompose);
|
|
19
|
+
const composeExists = await ctx.test(testCmd);
|
|
20
|
+
if (!composeExists) {
|
|
21
|
+
return 'Docker Compose config not found';
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
};
|
|
25
|
+
export const setupDockerTask = async (ctx) => {
|
|
26
|
+
if (ctx.config.dockerCompose === false) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const configFiles = ctx.config.dockerCompose?.configFiles ?? [];
|
|
30
|
+
const options = configFiles.map(f => `-f ${f}`).join(' ');
|
|
31
|
+
await ctx.run(`docker compose ${options} down --remove-orphans`);
|
|
32
|
+
await ctx.run(`docker compose ${options} up -d --build`);
|
|
33
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const setupPm2Skip = async (ctx) => {
|
|
2
|
+
if (ctx.config.pm2 === false) {
|
|
3
|
+
return 'PM2 disabled';
|
|
4
|
+
}
|
|
5
|
+
const pm2ConfigExists = await ctx.test('test -f pm2.config.*');
|
|
6
|
+
if (!pm2ConfigExists) {
|
|
7
|
+
return 'PM2 config not found';
|
|
8
|
+
}
|
|
9
|
+
return false;
|
|
10
|
+
};
|
|
11
|
+
export const setupPm2Task = async (ctx) => {
|
|
12
|
+
await ctx.run('pm2 start pm2.config.* --update-env');
|
|
13
|
+
await ctx.run('pm2 save');
|
|
14
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const symlinksSkip = (ctx) => {
|
|
2
|
+
const symlinks = ctx.config.symlinks;
|
|
3
|
+
return !symlinks || symlinks.length === 0
|
|
4
|
+
? 'No symlinks defined in config'
|
|
5
|
+
: false;
|
|
6
|
+
};
|
|
7
|
+
export const symlinksTask = async (ctx, ph) => {
|
|
8
|
+
const symlinks = ctx.config.symlinks;
|
|
9
|
+
for (const link of symlinks) {
|
|
10
|
+
const target = link.target.startsWith('/')
|
|
11
|
+
? link.target
|
|
12
|
+
: `${ph.deployPath}/${link.target}`;
|
|
13
|
+
const path = link.path.startsWith('/')
|
|
14
|
+
? link.path
|
|
15
|
+
: `${ph.deployPath}/${link.path}`;
|
|
16
|
+
await ctx.run(`ln -sfn ${target} ${path}`);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FilesConfig, ServerConfig, TaskFn, TaskSkipFn } from '../def.js';
|
|
2
|
+
export type RsyncOptions = {
|
|
3
|
+
delete?: boolean;
|
|
4
|
+
};
|
|
5
|
+
export declare function buildRsyncCommand(server: ServerConfig, source: string, dest: string, files: FilesConfig, options?: RsyncOptions): string;
|
|
6
|
+
export declare const uploadSkip: TaskSkipFn;
|
|
7
|
+
export declare const uploadTask: TaskFn;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export function buildRsyncCommand(server, source, dest, files, options = {}) {
|
|
4
|
+
const { delete: useDelete = true, } = options;
|
|
5
|
+
const args = ['rsync', '-avz', '--progress=info2'];
|
|
6
|
+
if (useDelete) {
|
|
7
|
+
args.push('--delete');
|
|
8
|
+
}
|
|
9
|
+
// ssh shell
|
|
10
|
+
const sshParts = ['ssh'];
|
|
11
|
+
if (server.port && server.port !== 22) {
|
|
12
|
+
sshParts.push(`-p ${server.port}`);
|
|
13
|
+
}
|
|
14
|
+
if (server.authMethod === 'key' && server.privateKey) {
|
|
15
|
+
sshParts.push(`-i ${server.privateKey}`);
|
|
16
|
+
}
|
|
17
|
+
sshParts.push('-o StrictHostKeyChecking=no');
|
|
18
|
+
args.push('-e', `"${sshParts.join(' ')}"`);
|
|
19
|
+
// include/exclude
|
|
20
|
+
if (files.exclude) {
|
|
21
|
+
for (const pattern of files.exclude) {
|
|
22
|
+
args.push(`--exclude="${pattern}"`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (files.include) {
|
|
26
|
+
for (const pattern of files.include) {
|
|
27
|
+
args.push(`--include="${pattern}"`);
|
|
28
|
+
}
|
|
29
|
+
args.push('--exclude="*"');
|
|
30
|
+
}
|
|
31
|
+
args.push(source, dest);
|
|
32
|
+
return args.join(' ');
|
|
33
|
+
}
|
|
34
|
+
export const uploadSkip = (ctx) => {
|
|
35
|
+
const files = ctx.config.files;
|
|
36
|
+
return !files
|
|
37
|
+
? 'No files configuration defined'
|
|
38
|
+
: false;
|
|
39
|
+
};
|
|
40
|
+
export const uploadTask = async (ctx, ph) => {
|
|
41
|
+
const files = ctx.config.files;
|
|
42
|
+
const localBase = files.basePath?.startsWith('/')
|
|
43
|
+
? files.basePath
|
|
44
|
+
: path.resolve(ctx.config.rootDir, files.basePath ?? '.');
|
|
45
|
+
const remotePath = ph.deployPath;
|
|
46
|
+
const dest = `${ctx.server.username}@${ctx.server.host}:${remotePath}`;
|
|
47
|
+
const source = localBase.endsWith('/') ? localBase : localBase + '/';
|
|
48
|
+
await ctx.run(`mkdir -p ${remotePath}`);
|
|
49
|
+
const command = buildRsyncCommand(ctx.server, source, dest, files, {
|
|
50
|
+
delete: true,
|
|
51
|
+
});
|
|
52
|
+
console.log(chalk.grey(command));
|
|
53
|
+
await ctx.runLocal(command);
|
|
54
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "d3ployer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -25,11 +25,12 @@
|
|
|
25
25
|
"prepare": "husky"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@
|
|
28
|
+
"@listr2/prompt-adapter-enquirer": "^4.2.1",
|
|
29
29
|
"chalk": "^5.6.2",
|
|
30
30
|
"commander": "^14.0.3",
|
|
31
|
+
"enquirer": "^2.4.1",
|
|
31
32
|
"listr2": "^10.2.1",
|
|
32
|
-
"lodash-es": "^4.
|
|
33
|
+
"lodash-es": "^4.18.1",
|
|
33
34
|
"ssh2-promise": "^1.0.3"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
@@ -38,7 +39,6 @@
|
|
|
38
39
|
"@tsconfig/node24": "^24.0.4",
|
|
39
40
|
"@types/chai": "^5.2.3",
|
|
40
41
|
"@types/chai-as-promised": "^8.0.2",
|
|
41
|
-
"@types/lodash-es": "^4.17.12",
|
|
42
42
|
"@types/mocha": "^10.0.10",
|
|
43
43
|
"@types/node": "^24.10.13",
|
|
44
44
|
"c8": "^10.1.3",
|
package/dist/defaultTasks.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { FilesConfig, ScenarioDef, ServerConfig, TaskDef, TaskFn, TaskSkipFn } from './def.js';
|
|
2
|
-
export type RsyncOptions = {
|
|
3
|
-
delete?: boolean;
|
|
4
|
-
};
|
|
5
|
-
export declare function buildRsyncCommand(server: ServerConfig, source: string, dest: string, files: FilesConfig, options?: RsyncOptions): string;
|
|
6
|
-
export declare const downloadSkip: TaskSkipFn;
|
|
7
|
-
export declare const downloadTask: TaskFn;
|
|
8
|
-
export declare const defaultTasks: Record<string, TaskDef>;
|
|
9
|
-
export declare const defaultScenarios: Record<string, ScenarioDef>;
|