d3ployer 0.0.11 → 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 CHANGED
@@ -37,7 +37,7 @@ export default defineConfig({
37
37
  },
38
38
  },
39
39
  scenarios: {
40
- deploy: ['upload', 'symlinks', 'dep:install', 'restart'],
40
+ deploy: ['upload', 'symlinks', 'install:packages', 'restart'],
41
41
  },
42
42
  });
43
43
  ```
@@ -45,10 +45,11 @@ export default defineConfig({
45
45
  Then deploy:
46
46
 
47
47
  ```bash
48
- dpl deploy # run "deploy" scenario on all servers
49
- dpl deploy prod # run on specific server(s)
50
- dpl upload # run a single task
51
- dpl list # list available scenarios, tasks, and servers
48
+ dpl deploy # run "deploy" scenario on all servers
49
+ dpl deploy prod # run on specific server(s)
50
+ dpl upload # run a single task
51
+ dpl deploy --skip install:packages # skip specific tasks (comma-separated)
52
+ dpl list # list available scenarios, tasks, and servers
52
53
  ```
53
54
 
54
55
  ## CLI
@@ -59,6 +60,7 @@ dpl list List scenarios, tasks, and servers
59
60
 
60
61
  Options:
61
62
  -c, --config <path> Path to deployer.config.ts
63
+ --skip <tasks> Comma-separated list of tasks to skip
62
64
  ```
63
65
 
64
66
  If `<name>` matches a scenario, it runs all tasks in that scenario sequentially. Otherwise it runs the matching task directly.
@@ -119,20 +121,33 @@ packageManager: {
119
121
 
120
122
  ### `pm2`
121
123
 
122
- Set to `false` to disable the built-in PM2 task. When enabled (default), the `pm2:setup` task auto-detects `pm2.config.*` files and runs `pm2 start`.
124
+ Configure PM2 integration. Set to `false` to disable entirely. The `setup:pm2` task auto-detects `pm2.config.*` files. Post-deploy log streaming is configured via `pm2.logs`.
123
125
 
124
- ### `dockerCompose`
125
-
126
- Set to `false` to disable the built-in Docker Compose task. When enabled (default), the `docker:setup` task auto-detects compose files and runs `docker compose up -d --build`.
126
+ ```ts
127
+ pm2: {
128
+ logs: {
129
+ time: 5, // seconds to stream (default: 3)
130
+ lines: 50, // log lines to show (default: 25)
131
+ },
132
+ // logs: false — disable log streaming only
133
+ }
134
+ // pm2: false — disable PM2 entirely
135
+ ```
127
136
 
128
- ### `logs`
137
+ ### `dockerCompose`
129
138
 
130
- Configure post-deploy log streaming. The `stream:logs` task will stream PM2 or Docker Compose logs for the configured duration. Set to `false` to disable.
139
+ Configure Docker Compose integration. Set to `false` to disable entirely. The `setup:docker` task auto-detects compose files. Specify custom compose files via `configFiles`.
131
140
 
132
141
  ```ts
133
- logs: {
134
- time: 5, // seconds to stream logs (default: 3)
142
+ dockerCompose: {
143
+ configFiles: ['docker-compose.prod.yml'], // optional, defaults to auto-detect
144
+ logs: {
145
+ time: 5, // seconds to stream (default: 3)
146
+ lines: 50, // log lines to show (default: 25)
147
+ },
148
+ // logs: false — disable log streaming only
135
149
  }
150
+ // dockerCompose: false — disable Docker Compose entirely
136
151
  ```
137
152
 
138
153
  ### `tasks`
@@ -141,7 +156,7 @@ Custom task functions receive a `TaskContext` and `Placeholders`:
141
156
 
142
157
  ```ts
143
158
  tasks: {
144
- migrate: async (ctx, ph) => {
159
+ migrate: async (ctx, ph, task) => {
145
160
  await ctx.run('npm run migrate');
146
161
  },
147
162
  }
@@ -154,7 +169,7 @@ tasks: {
154
169
  myTask: {
155
170
  name: 'My Task',
156
171
  skip: async (ctx) => !someCondition ? 'Reason to skip' : false,
157
- task: async (ctx, ph) => { /* ... */ },
172
+ task: async (ctx, ph, task) => { /* ... */ },
158
173
  config: { /* passed as ctx.taskConfig */ },
159
174
  },
160
175
  }
@@ -187,7 +202,7 @@ Named sequences of tasks:
187
202
 
188
203
  ```ts
189
204
  scenarios: {
190
- deploy: ['upload', 'symlinks', 'dep:install', 'restart'],
205
+ deploy: ['upload', 'symlinks', 'install:packages', 'restart'],
191
206
  }
192
207
  ```
193
208
 
@@ -197,30 +212,31 @@ Or as objects with a custom name:
197
212
  scenarios: {
198
213
  deploy: {
199
214
  name: 'Deploy',
200
- tasks: ['upload', 'symlinks', 'dep:install', 'restart'],
215
+ tasks: ['upload', 'symlinks', 'install:packages', 'restart'],
201
216
  },
202
217
  }
203
218
  ```
204
219
 
205
220
  ## Built-in tasks
206
221
 
207
- | Task | Description |
208
- | ------------------ | -------------------------------------------------------- |
209
- | `upload` | Rsync files to the remote server |
210
- | `download` | Rsync files from the remote server (uses task config) |
211
- | `symlinks` | Create configured symlinks on the remote server |
212
- | `dep:install` | Install dependencies via npm/yarn/pnpm |
213
- | `pm2:setup` | Start/restart PM2 processes (auto-detects pm2.config.*) |
214
- | `docker:setup` | Run docker compose up (auto-detects compose files) |
215
- | `clear:target` | Remove the entire deploy path (with confirmation prompt) |
216
- | `print:deployment` | Print deployment info (date, files, disk usage) |
217
- | `stream:logs` | Stream PM2/Docker Compose logs for a few seconds |
222
+ | Task | Description |
223
+ | ------------------- | -------------------------------------------------------- |
224
+ | `upload` | Rsync files to the remote server |
225
+ | `download` | Rsync files from the remote server (uses task config) |
226
+ | `symlinks` | Create configured symlinks on the remote server |
227
+ | `install:packages` | Install dependencies via npm/yarn/pnpm |
228
+ | `setup:pm2` | Start/restart PM2 processes (auto-detects pm2.config.*) |
229
+ | `setup:docker` | Run docker compose up (auto-detects compose files) |
230
+ | `clear:target` | Remove the entire deploy path (with confirmation prompt) |
231
+ | `print:deployment` | Print deployment info (date, files, disk usage) |
232
+ | `print:logs:pm2` | Stream PM2 logs for a configured duration |
233
+ | `print:logs:docker` | Stream Docker Compose logs for a configured duration |
218
234
 
219
235
  ### Default `deploy` scenario
220
236
 
221
- The built-in `deploy` scenario runs: `upload` → `symlinks` → `dep:install` → `pm2:setup` → `docker:setup` → `print:deployment` → `stream:logs`
237
+ The built-in `deploy` scenario runs: `upload` → `symlinks` → `install:packages` → `setup:pm2` → `setup:docker` → `print:deployment` → `print:logs:pm2` → `print:logs:docker`
222
238
 
223
- Tasks with skip conditions will be automatically skipped when not applicable (e.g. `pm2:setup` skips if no PM2 config file exists).
239
+ Tasks with skip conditions will be automatically skipped when not applicable (e.g. `setup:pm2` skips if no PM2 config file exists).
224
240
 
225
241
  ## Requirements
226
242
 
package/dist/bin.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "d3ployer",
3
- "version": "0.0.11",
3
+ "version": "0.0.12",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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>;
@@ -1,303 +0,0 @@
1
- import confirm from '@inquirer/confirm';
2
- import chalk from 'chalk';
3
- import path from 'node:path';
4
- import { Exception } from './utils/index.js';
5
- export function buildRsyncCommand(server, source, dest, files, options = {}) {
6
- const { delete: useDelete = true, } = options;
7
- const args = ['rsync', '-avz', '--progress=info2'];
8
- if (useDelete) {
9
- args.push('--delete');
10
- }
11
- // ssh shell
12
- const sshParts = ['ssh'];
13
- if (server.port && server.port !== 22) {
14
- sshParts.push(`-p ${server.port}`);
15
- }
16
- if (server.authMethod === 'key' && server.privateKey) {
17
- sshParts.push(`-i ${server.privateKey}`);
18
- }
19
- sshParts.push('-o StrictHostKeyChecking=no');
20
- args.push('-e', `"${sshParts.join(' ')}"`);
21
- // include/exclude
22
- if (files.exclude) {
23
- for (const pattern of files.exclude) {
24
- args.push(`--exclude="${pattern}"`);
25
- }
26
- }
27
- if (files.include) {
28
- for (const pattern of files.include) {
29
- args.push(`--include="${pattern}"`);
30
- }
31
- args.push('--exclude="*"');
32
- }
33
- args.push(source, dest);
34
- return args.join(' ');
35
- }
36
- const uploadSkip = (ctx) => {
37
- const files = ctx.config.files;
38
- return !files
39
- ? 'No files configuration defined'
40
- : false;
41
- };
42
- const uploadTask = async (ctx, ph) => {
43
- const files = ctx.config.files;
44
- const localBase = files.basePath?.startsWith('/')
45
- ? files.basePath
46
- : path.resolve(ctx.config.rootDir, files.basePath ?? '.');
47
- const remotePath = ph.deployPath;
48
- const dest = `${ctx.server.username}@${ctx.server.host}:${remotePath}`;
49
- const source = localBase.endsWith('/') ? localBase : localBase + '/';
50
- await ctx.run(`mkdir -p ${remotePath}`);
51
- const command = buildRsyncCommand(ctx.server, source, dest, files, {
52
- delete: true,
53
- });
54
- console.log(chalk.grey(command));
55
- await ctx.runLocal(command);
56
- };
57
- export const downloadSkip = (ctx) => {
58
- const files = ctx.taskConfig;
59
- return !files
60
- ? 'No files configuration provided in task config'
61
- : false;
62
- };
63
- export const downloadTask = async (ctx, ph) => {
64
- const files = ctx.taskConfig;
65
- if (!files) {
66
- throw new Exception('No files configuration provided in task config', 1784523741234);
67
- }
68
- const localBase = files.basePath?.startsWith('/')
69
- ? files.basePath
70
- : path.resolve(ctx.config.rootDir, files.basePath ?? '.');
71
- const remotePath = ph.deployPath;
72
- const source = `${ctx.server.username}@${ctx.server.host}:${remotePath}/`;
73
- const dest = localBase.endsWith('/') ? localBase : localBase + '/';
74
- const command = buildRsyncCommand(ctx.server, source, dest, files, {
75
- delete: false,
76
- });
77
- console.log(chalk.grey(command));
78
- await ctx.runLocal(command);
79
- };
80
- const symlinksSkip = (ctx) => {
81
- const symlinks = ctx.config.symlinks;
82
- return !symlinks || symlinks.length === 0
83
- ? 'No symlinks defined in config'
84
- : false;
85
- };
86
- const symlinksTask = async (ctx, ph) => {
87
- const symlinks = ctx.config.symlinks;
88
- for (const link of symlinks) {
89
- const target = link.target.startsWith('/')
90
- ? link.target
91
- : `${ph.deployPath}/${link.target}`;
92
- const path = link.path.startsWith('/')
93
- ? link.path
94
- : `${ph.deployPath}/${link.path}`;
95
- await ctx.run(`ln -sfn ${target} ${path}`);
96
- }
97
- };
98
- const depInstallSkip = (ctx) => {
99
- if (ctx.server.packageManager !== undefined) {
100
- return ctx.server.packageManager === false
101
- ? 'Package manager disabled for server'
102
- : false;
103
- }
104
- if (ctx.config.packageManager !== undefined) {
105
- return ctx.config.packageManager === false
106
- ? 'Package manager disabled in config'
107
- : false;
108
- }
109
- return false;
110
- };
111
- const depInstallTask = async (ctx) => {
112
- const config = {
113
- manager: 'npm',
114
- productionOnly: true,
115
- ...ctx.config.packageManager,
116
- ...ctx.server.packageManager,
117
- };
118
- let cmd = `${config.manager} install`;
119
- if (config.productionOnly) {
120
- if (config.manager === 'npm') {
121
- cmd += ' --omit=dev';
122
- }
123
- else if (config.manager === 'yarn') {
124
- cmd += ' --production';
125
- }
126
- else if (config.manager === 'pnpm') {
127
- cmd += ' --prod';
128
- }
129
- else {
130
- throw new Exception(`Unsupported package manager "${config.manager}"`, 1774823752134);
131
- }
132
- }
133
- await ctx.run(cmd);
134
- };
135
- const pm2SetupSkip = async (ctx) => {
136
- if (ctx.config.pm2 === false) {
137
- return 'PM2 disabled';
138
- }
139
- const pm2ConfigExists = await ctx.test('test -f pm2.config.*');
140
- if (!pm2ConfigExists) {
141
- return 'PM2 config not found';
142
- }
143
- return false;
144
- };
145
- const pm2SetupTask = async (ctx) => {
146
- await ctx.run('pm2 start pm2.config.* --update-env');
147
- await ctx.run('pm2 save');
148
- };
149
- function buildDockerComposeTestCmd(dockerComposeConfig) {
150
- if (dockerComposeConfig === false) {
151
- return 'false';
152
- }
153
- const configFiles = dockerComposeConfig.configFiles ?? [
154
- 'docker-compose.yml',
155
- 'docker-compose.yaml',
156
- 'compose.yml',
157
- 'compose.yaml',
158
- ];
159
- const testCmdPart = configFiles.map(f => `-f ${f}`);
160
- return `test ${testCmdPart.join(' -o ')}`;
161
- }
162
- const dockerSetupSkip = async (ctx) => {
163
- if (ctx.config.dockerCompose === false) {
164
- return 'Docker Compose disabled';
165
- }
166
- const testCmd = buildDockerComposeTestCmd(ctx.config.dockerCompose);
167
- const composeExists = await ctx.test(testCmd);
168
- if (!composeExists) {
169
- return 'Docker Compose config not found';
170
- }
171
- return false;
172
- };
173
- const dockerSetupTask = async (ctx) => {
174
- if (ctx.config.dockerCompose === false) {
175
- return;
176
- }
177
- const configFiles = ctx.config.dockerCompose?.configFiles ?? [];
178
- const options = configFiles.map(f => `-f ${f}`).join(' ');
179
- await ctx.run(`docker compose ${options} down --remove-orphans`);
180
- await ctx.run(`docker compose ${options} up -d --build`);
181
- };
182
- const clearTargetTask = async (ctx, ph) => {
183
- const confirmed = await confirm({
184
- message: chalk.red(`Remove entire deploy path ${ph.deployPath} on ${ctx.server.host}?`),
185
- default: false,
186
- });
187
- if (!confirmed) {
188
- console.log(chalk.yellow('Skipped clearing target'));
189
- return;
190
- }
191
- await ctx.run(`rm -rf ${ph.deployPath}`);
192
- };
193
- const printDeploymentTask = async (ctx, ph) => {
194
- await ctx.run('date');
195
- console.log(chalk.cyan('Deployment directory'), ph.deployPath);
196
- await ctx.run('ls -la .');
197
- console.log(chalk.cyan('Directory size'));
198
- await ctx.run('du -hd 1 .');
199
- };
200
- const logsStreamSkip = async (ctx) => {
201
- if (ctx.config.logs === false) {
202
- return 'Logs streaming disabled';
203
- }
204
- const hasPm2 = ctx.config.pm2 !== false && await ctx.test('test -f pm2.config.*');
205
- let hasDocker = false;
206
- if (ctx.config.dockerCompose) {
207
- const testCmd = buildDockerComposeTestCmd(ctx.config.dockerCompose);
208
- hasDocker = await ctx.test(testCmd);
209
- }
210
- if (!hasPm2 && !hasDocker) {
211
- return 'No PM2 or Docker Compose detected';
212
- }
213
- return false;
214
- };
215
- const logsStreamTask = async (ctx) => {
216
- const logsConfig = {
217
- time: 3,
218
- ...ctx.config.logs,
219
- };
220
- const time = logsConfig.time;
221
- const hasPm2 = ctx.config.pm2 !== false
222
- && await ctx.test('test -f pm2.config.*');
223
- let hasDocker = false;
224
- if (ctx.config.dockerCompose !== false) {
225
- const testCmd = buildDockerComposeTestCmd(ctx.config.dockerCompose);
226
- hasDocker = await ctx.test(testCmd);
227
- }
228
- if (hasPm2) {
229
- const pm2ConfigRaw = await ctx.run('cat pm2.config.*', { printOutput: false });
230
- const nameMatch = pm2ConfigRaw.stdout.match(/name: ['"](?<name>.+?)['"]/);
231
- const name = nameMatch.groups?.name ?? 'all';
232
- console.log(chalk.cyan(`Streaming PM2 logs for ${time}s...`));
233
- await ctx.run(`timeout ${time} pm2 logs "${name}" || true`, { printOutput: true, ignoreError: true });
234
- }
235
- else if (hasDocker && ctx.config.dockerCompose) {
236
- const configFiles = ctx.config.dockerCompose.configFiles ?? [];
237
- const options = configFiles.map(f => `-f ${f}`).join(' ');
238
- console.log(chalk.cyan(`Streaming Docker Compose logs for ${time}s...`));
239
- await ctx.run(`timeout ${time} docker compose ${options} logs --tail=10 -f || true`, {
240
- printOutput: true,
241
- ignoreError: true,
242
- });
243
- }
244
- };
245
- export const defaultTasks = {
246
- clearTarget: {
247
- name: 'Clear target',
248
- task: clearTargetTask,
249
- },
250
- upload: {
251
- name: 'Upload files',
252
- skip: uploadSkip,
253
- task: uploadTask,
254
- },
255
- download: {
256
- name: 'Download files',
257
- skip: downloadSkip,
258
- task: downloadTask,
259
- },
260
- symlinks: {
261
- name: 'Create symlinks',
262
- skip: symlinksSkip,
263
- task: symlinksTask,
264
- },
265
- depInstall: {
266
- name: 'Install dependencies',
267
- skip: depInstallSkip,
268
- task: depInstallTask,
269
- },
270
- pm2Setup: {
271
- name: 'PM2 setup',
272
- skip: pm2SetupSkip,
273
- task: pm2SetupTask,
274
- },
275
- dockerSetup: {
276
- name: 'Docker Compose setup',
277
- skip: dockerSetupSkip,
278
- task: dockerSetupTask,
279
- },
280
- printDeployment: {
281
- name: 'Print deployment info',
282
- task: printDeploymentTask,
283
- },
284
- logsStream: {
285
- name: 'Logs stream',
286
- skip: logsStreamSkip,
287
- task: logsStreamTask,
288
- },
289
- };
290
- export const defaultScenarios = {
291
- deploy: {
292
- name: 'Deploy',
293
- tasks: [
294
- 'upload',
295
- 'symlinks',
296
- 'dep:install',
297
- 'pm2:setup',
298
- 'docker:setup',
299
- 'print:deployment',
300
- 'logs:stream',
301
- ],
302
- },
303
- };
@@ -1,5 +0,0 @@
1
- import type { TaskFn, TaskSkipFn } from '../def.js';
2
- export declare const printLogsPm2Skip: TaskSkipFn;
3
- export declare const printLogsDockerSkip: TaskSkipFn;
4
- export declare const printLogsPm2Task: TaskFn;
5
- export declare const printLogsDockerComposeTask: TaskFn;
@@ -1,69 +0,0 @@
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 printLogsDockerComposeTask = 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
- };