d3ployer 0.0.4 → 0.0.6

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
@@ -33,11 +33,11 @@ export default defineConfig({
33
33
  ],
34
34
  tasks: {
35
35
  restart: async (ctx) => {
36
- await ctx.runRemote('systemctl restart myapp');
36
+ await ctx.run('systemctl restart myapp');
37
37
  },
38
38
  },
39
39
  scenarios: {
40
- deploy: ['upload', 'symlinks', 'depInstall', 'restart'],
40
+ deploy: ['upload', 'symlinks', 'dep:install', 'restart'],
41
41
  },
42
42
  });
43
43
  ```
@@ -69,18 +69,18 @@ If `<name>` matches a scenario, it runs all tasks in that scenario sequentially.
69
69
 
70
70
  Define target servers. Only `host` and `deployPath` are required.
71
71
 
72
- | Field | Default | Description |
73
- | --------------- | -------------------- | ------------------------------------ |
74
- | `host` | (required) | Server hostname or IP |
75
- | `deployPath` | (required) | Remote path to deploy to |
76
- | `port` | `22` | SSH port |
77
- | `username` | Current OS user | SSH username |
78
- | `authMethod` | `'agent'` | `'agent'`, `'key'`, or `'password'` |
79
- | `privateKey` | - | Path to private key (for `'key'`) |
80
- | `password` | - | SSH password (for `'password'`) |
81
- | `agent` | `SSH_AUTH_SOCK` | SSH agent socket path |
82
- | `packageManager`| - | Override package manager per server |
83
- | `initCmd` | - | Command to run on connect |
72
+ | Field | Default | Description |
73
+ | --------------- | -------------------- | ---------------------------------------- |
74
+ | `host` | (required) | Server hostname or IP |
75
+ | `deployPath` | (required) | Remote path to deploy to |
76
+ | `port` | `22` | SSH port |
77
+ | `username` | Current OS user | SSH username |
78
+ | `authMethod` | `'agent'` | `'agent'`, `'key'`, or `'password'` |
79
+ | `privateKey` | - | Path to private key (for `'key'`) |
80
+ | `password` | - | SSH password (for `'password'`) |
81
+ | `agent` | `SSH_AUTH_SOCK` | SSH agent socket path |
82
+ | `packageManager`| - | Override package manager config per server (or `false` to disable) |
83
+ | `initCmd` | - | Shell command to run before each remote command (e.g. `source ~/.nvm/nvm.sh`) |
84
84
 
85
85
  ### `files`
86
86
 
@@ -106,6 +106,25 @@ symlinks: [
106
106
 
107
107
  Relative paths are resolved against `deployPath`.
108
108
 
109
+ ### `packageManager`
110
+
111
+ Configure dependency installation. Can be set globally and/or per server. Set to `false` to disable.
112
+
113
+ ```ts
114
+ packageManager: {
115
+ manager: 'npm', // 'npm' | 'yarn' | 'pnpm' (default: 'npm')
116
+ productionOnly: true, // install production deps only (default: true)
117
+ }
118
+ ```
119
+
120
+ ### `pm2`
121
+
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`.
123
+
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`.
127
+
109
128
  ### `tasks`
110
129
 
111
130
  Custom task functions receive a `TaskContext` and `Placeholders`:
@@ -113,17 +132,39 @@ Custom task functions receive a `TaskContext` and `Placeholders`:
113
132
  ```ts
114
133
  tasks: {
115
134
  migrate: async (ctx, ph) => {
116
- await ctx.runRemote(`cd ${ph.deployPath} && npm run migrate`);
135
+ await ctx.run('npm run migrate');
117
136
  },
118
137
  }
119
138
  ```
120
139
 
140
+ Tasks can also be defined as objects with skip logic and config:
141
+
142
+ ```ts
143
+ tasks: {
144
+ myTask: {
145
+ name: 'My Task',
146
+ skip: async (ctx) => !someCondition ? 'Reason to skip' : false,
147
+ task: async (ctx, ph) => { /* ... */ },
148
+ config: { /* passed as ctx.taskConfig */ },
149
+ },
150
+ }
151
+ ```
152
+
153
+ Task keys are auto-converted from camelCase to colon:case (e.g. `depInstall` becomes `dep:install`).
154
+
121
155
  **TaskContext** provides:
122
- - `runRemote(cmd)` - execute a command on the remote server
123
- - `runLocal(cmd)` - execute a command locally
124
- - `server` - current server config
156
+ - `run(cmd, options?)` - execute a command on the remote server (auto cd's to `deployPath`)
157
+ - `test(cmd)` - execute a command on the remote server, returns `boolean`
158
+ - `runLocal(cmd, options?)` - execute a command locally
159
+ - `testLocal(cmd)` - execute a command locally, returns `boolean`
160
+ - `server` - current server config (includes `name`)
125
161
  - `ssh` - SSH2Promise connection
126
162
  - `config` - full deployer config
163
+ - `taskConfig` - per-task config from task definition
164
+
165
+ **RunOptions** (for `run` and `runLocal`):
166
+ - `printOutput` - print stdout/stderr (default: `true`)
167
+ - `ignoreError` - don't throw on non-zero exit (default: `false`)
127
168
 
128
169
  **Placeholders** provide:
129
170
  - `serverName` - name of the current server
@@ -136,22 +177,44 @@ Named sequences of tasks:
136
177
 
137
178
  ```ts
138
179
  scenarios: {
139
- deploy: ['upload', 'symlinks', 'depInstall', 'restart'],
180
+ deploy: ['upload', 'symlinks', 'dep:install', 'restart'],
181
+ }
182
+ ```
183
+
184
+ Or as objects with a custom name:
185
+
186
+ ```ts
187
+ scenarios: {
188
+ deploy: {
189
+ name: 'Deploy',
190
+ tasks: ['upload', 'symlinks', 'dep:install', 'restart'],
191
+ },
140
192
  }
141
193
  ```
142
194
 
143
195
  ## Built-in tasks
144
196
 
145
- | Task | Description |
146
- | ------------ | ---------------------------------------------- |
147
- | `upload` | Rsync files to the remote server |
148
- | `symlinks` | Create configured symlinks on the remote server |
149
- | `depInstall` | Run package manager install on the remote server|
197
+ | Task | Description |
198
+ | ------------------ | -------------------------------------------------------- |
199
+ | `upload` | Rsync files to the remote server |
200
+ | `download` | Rsync files from the remote server (uses task config) |
201
+ | `symlinks` | Create configured symlinks on the remote server |
202
+ | `dep:install` | Install dependencies via npm/yarn/pnpm |
203
+ | `pm2:setup` | Start/restart PM2 processes (auto-detects pm2.config.*) |
204
+ | `docker:setup` | Run docker compose up (auto-detects compose files) |
205
+ | `clear:target` | Remove the entire deploy path (with confirmation prompt) |
206
+ | `print:deployment` | Print deployment info (date, files, disk usage) |
207
+
208
+ ### Default `deploy` scenario
209
+
210
+ The built-in `deploy` scenario runs: `upload` → `symlinks` → `dep:install` → `pm2:setup` → `docker:setup` → `print:deployment`
211
+
212
+ Tasks with skip conditions will be automatically skipped when not applicable (e.g. `pm2:setup` skips if no PM2 config file exists).
150
213
 
151
214
  ## Requirements
152
215
 
153
216
  - Node.js (ESM)
154
- - `rsync` installed locally (for the `upload` task)
217
+ - `rsync` installed locally (for the `upload`/`download` tasks)
155
218
  - SSH access to target servers
156
219
 
157
220
  ## License
package/dist/config.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  import type { DeployerConfig, DeployerConfigInput } from './def.js';
2
+ export declare function camelToColonCase(str: string): string;
2
3
  export declare function defineConfig(input: DeployerConfigInput): DeployerConfig;
package/dist/config.js CHANGED
@@ -11,37 +11,71 @@ function resolveServer(input) {
11
11
  }
12
12
  function normalizeTask(key, input) {
13
13
  if (typeof input === 'function') {
14
- return { name: key, fn: input };
14
+ return {
15
+ name: key,
16
+ task: input,
17
+ };
15
18
  }
16
- return { name: input.name, fn: input.task };
19
+ return {
20
+ name: input.name ?? key,
21
+ task: input.task,
22
+ skip: input.skip,
23
+ config: input.config,
24
+ };
17
25
  }
18
26
  function normalizeScenario(key, input) {
19
27
  if (Array.isArray(input)) {
20
- return { name: key, tasks: input };
28
+ return {
29
+ name: key,
30
+ tasks: input,
31
+ };
21
32
  }
22
- return { name: input.name, tasks: input.tasks };
33
+ return {
34
+ name: input.name,
35
+ tasks: input.tasks,
36
+ };
37
+ }
38
+ export function camelToColonCase(str) {
39
+ return str.replace(/([a-z0-9])([A-Z])/g, '$1:$2').toLowerCase();
23
40
  }
24
41
  export function defineConfig(input) {
25
42
  const servers = {};
26
43
  for (const [name, serverInput] of Object.entries(input.servers)) {
27
44
  servers[name] = resolveServer(serverInput);
28
45
  }
29
- const tasks = { ...defaultTasks };
46
+ const tasks = {};
47
+ for (const [key, taskDef] of Object.entries(defaultTasks)) {
48
+ tasks[camelToColonCase(key)] = taskDef;
49
+ }
30
50
  if (input.tasks) {
31
51
  for (const [key, taskInput] of Object.entries(input.tasks)) {
32
- tasks[key] = normalizeTask(key, taskInput);
52
+ tasks[camelToColonCase(key)] = normalizeTask(key, taskInput);
33
53
  }
34
54
  }
35
- const scenarios = { ...defaultScenarios };
55
+ const scenarios = {};
56
+ for (const [key, scenarioDef] of Object.entries(defaultScenarios)) {
57
+ scenarios[camelToColonCase(key)] = {
58
+ ...scenarioDef,
59
+ tasks: scenarioDef.tasks.map(camelToColonCase),
60
+ };
61
+ }
36
62
  if (input.scenarios) {
37
63
  for (const [key, scenarioInput] of Object.entries(input.scenarios)) {
38
- scenarios[key] = normalizeScenario(key, scenarioInput);
64
+ const normalized = normalizeScenario(key, scenarioInput);
65
+ scenarios[camelToColonCase(key)] = {
66
+ ...normalized,
67
+ tasks: normalized.tasks.map(camelToColonCase),
68
+ };
39
69
  }
40
70
  }
41
71
  return {
42
- packageManager: 'npm',
43
72
  rootDir: '',
44
73
  ...input,
74
+ packageManager: {
75
+ manager: 'npm',
76
+ productionOnly: true,
77
+ ...input.packageManager,
78
+ },
45
79
  servers,
46
80
  tasks,
47
81
  scenarios,
package/dist/def.d.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  import type SSH2Promise from 'ssh2-promise';
2
2
  export type AuthMethod = 'key' | 'password' | 'agent';
3
+ export type PackageManager = 'npm' | 'yarn' | 'pnpm';
4
+ export type PackageManagerConfig = {
5
+ manager: PackageManager;
6
+ productionOnly?: boolean;
7
+ };
3
8
  export interface ServerConfig {
4
9
  host: string;
5
10
  port: number;
@@ -9,7 +14,7 @@ export interface ServerConfig {
9
14
  password?: string;
10
15
  agent?: string;
11
16
  deployPath: string;
12
- packageManager?: string;
17
+ packageManager?: PackageManagerConfig | false;
13
18
  initCmd?: string;
14
19
  }
15
20
  export type ServerConfigInput = Partial<ServerConfig> & Pick<ServerConfig, 'host' | 'deployPath'>;
@@ -47,15 +52,21 @@ export interface TaskContext {
47
52
  testLocal: (cmd: string) => Promise<boolean>;
48
53
  run: (cmd: string, options?: RunOptions) => Promise<ExecResult>;
49
54
  test: (cmd: string) => Promise<boolean>;
55
+ taskConfig?: any;
50
56
  }
51
57
  export type TaskFn = (ctx: TaskContext, ph: Placeholders) => Promise<void>;
58
+ export type TaskSkipFn = (ctx: TaskContext, ph: Placeholders) => Promise<boolean | string> | boolean | string;
52
59
  export interface TaskDef {
53
60
  name: string;
54
- fn: TaskFn;
61
+ task: TaskFn;
62
+ skip?: TaskSkipFn;
63
+ config?: any;
55
64
  }
56
65
  export type TaskInput = TaskFn | {
57
- name: string;
66
+ name?: string;
58
67
  task: TaskFn;
68
+ skip?: TaskSkipFn;
69
+ config?: any;
59
70
  };
60
71
  export interface ScenarioDef {
61
72
  name: string;
@@ -68,9 +79,11 @@ export type ScenarioInput = string[] | {
68
79
  export interface DeployerConfig {
69
80
  rootDir: string;
70
81
  servers: Record<string, ServerConfig>;
71
- packageManager?: string;
72
82
  files?: FilesConfig;
73
83
  symlinks?: SymlinkConfig[];
84
+ packageManager?: PackageManagerConfig | false;
85
+ pm2?: boolean;
86
+ dockerCompose?: boolean;
74
87
  tasks?: Record<string, TaskDef>;
75
88
  scenarios?: Record<string, ScenarioDef>;
76
89
  }
@@ -1,3 +1,9 @@
1
- import type { ScenarioDef, TaskDef } from './def.js';
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;
2
8
  export declare const defaultTasks: Record<string, TaskDef>;
3
9
  export declare const defaultScenarios: Record<string, ScenarioDef>;
@@ -1,8 +1,13 @@
1
- import { Exception } from './utils/index.js';
1
+ import confirm from '@inquirer/confirm';
2
2
  import chalk from 'chalk';
3
3
  import path from 'node:path';
4
- function buildRsyncCommand(server, source, dest, files) {
5
- const args = ['rsync', '-avz', '--delete', '--progress=info2'];
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
+ }
6
11
  // ssh shell
7
12
  const sshParts = ['ssh'];
8
13
  if (server.port && server.port !== 22) {
@@ -14,25 +19,28 @@ function buildRsyncCommand(server, source, dest, files) {
14
19
  sshParts.push('-o StrictHostKeyChecking=no');
15
20
  args.push('-e', `"${sshParts.join(' ')}"`);
16
21
  // include/exclude
22
+ if (files.exclude) {
23
+ for (const pattern of files.exclude) {
24
+ args.push(`--exclude=${pattern}`);
25
+ }
26
+ }
17
27
  if (files.include) {
18
28
  for (const pattern of files.include) {
19
29
  args.push(`--include=${pattern}`);
20
30
  }
21
31
  args.push('--exclude=*');
22
32
  }
23
- if (files.exclude) {
24
- for (const pattern of files.exclude) {
25
- args.push(`--exclude=${pattern}`);
26
- }
27
- }
28
33
  args.push(source, dest);
29
34
  return args.join(' ');
30
35
  }
36
+ const uploadSkip = (ctx) => {
37
+ const files = ctx.config.files;
38
+ return !files
39
+ ? 'No files configuration defined'
40
+ : false;
41
+ };
31
42
  const uploadTask = async (ctx, ph) => {
32
43
  const files = ctx.config.files;
33
- if (!files) {
34
- return;
35
- }
36
44
  const localBase = files.basePath?.startsWith('/')
37
45
  ? files.basePath
38
46
  : path.resolve(ctx.config.rootDir, files.basePath ?? '.');
@@ -40,14 +48,40 @@ const uploadTask = async (ctx, ph) => {
40
48
  const dest = `${ctx.server.username}@${ctx.server.host}:${remotePath}`;
41
49
  const source = localBase.endsWith('/') ? localBase : localBase + '/';
42
50
  await ctx.run(`mkdir -p ${remotePath}`);
43
- const command = buildRsyncCommand(ctx.server, source, dest, files);
51
+ const command = buildRsyncCommand(ctx.server, source, dest, files, {
52
+ delete: true,
53
+ });
54
+ console.log(chalk.grey(command));
44
55
  await ctx.runLocal(command);
45
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
+ const localBase = files.basePath?.startsWith('/')
66
+ ? files.basePath
67
+ : path.resolve(ctx.config.rootDir, files.basePath ?? '.');
68
+ const remotePath = ph.deployPath;
69
+ const source = `${ctx.server.username}@${ctx.server.host}:${remotePath}/`;
70
+ const dest = localBase.endsWith('/') ? localBase : localBase + '/';
71
+ const command = buildRsyncCommand(ctx.server, source, dest, files, {
72
+ delete: false,
73
+ });
74
+ console.log(chalk.grey(command));
75
+ await ctx.runLocal(command);
76
+ };
77
+ const symlinksSkip = (ctx) => {
78
+ const symlinks = ctx.config.symlinks;
79
+ return !symlinks || symlinks.length === 0
80
+ ? 'No symlinks defined in config'
81
+ : false;
82
+ };
46
83
  const symlinksTask = async (ctx, ph) => {
47
84
  const symlinks = ctx.config.symlinks;
48
- if (!symlinks || symlinks.length === 0) {
49
- return;
50
- }
51
85
  for (const link of symlinks) {
52
86
  const target = link.target.startsWith('/')
53
87
  ? link.target
@@ -58,22 +92,80 @@ const symlinksTask = async (ctx, ph) => {
58
92
  await ctx.run(`ln -sfn ${target} ${path}`);
59
93
  }
60
94
  };
95
+ const depInstallSkip = (ctx) => {
96
+ if (ctx.server.packageManager !== undefined) {
97
+ return ctx.server.packageManager === false
98
+ ? 'Package manager disabled for server'
99
+ : false;
100
+ }
101
+ if (ctx.config.packageManager !== undefined) {
102
+ return ctx.config.packageManager === false
103
+ ? 'Package manager disabled in config'
104
+ : false;
105
+ }
106
+ return false;
107
+ };
61
108
  const depInstallTask = async (ctx) => {
62
- const pm = ctx.server.packageManager ?? ctx.config.packageManager ?? 'npm';
63
- let cmd = `${pm}`;
64
- if (pm === 'npm') {
65
- cmd += ' install --production';
109
+ const config = {
110
+ manager: 'npm',
111
+ productionOnly: true,
112
+ ...ctx.config.packageManager,
113
+ ...ctx.server.packageManager,
114
+ };
115
+ let cmd = `${config.manager} install`;
116
+ if (config.productionOnly) {
117
+ if (config.manager === 'npm') {
118
+ cmd += ' --omit=dev';
119
+ }
120
+ else if (config.manager === 'yarn') {
121
+ cmd += ' --production';
122
+ }
123
+ else if (config.manager === 'pnpm') {
124
+ cmd += ' --prod';
125
+ }
126
+ else {
127
+ throw new Exception(`Unsupported package manager "${config.manager}"`, 1774823752134);
128
+ }
66
129
  }
67
- else if (pm === 'yarn') {
68
- cmd += ' install --production';
130
+ await ctx.run(cmd);
131
+ };
132
+ const pm2SetupSkip = async (ctx) => {
133
+ if (ctx.config.pm2 === false) {
134
+ return 'PM2 disabled';
69
135
  }
70
- else if (pm === 'pnpm') {
71
- cmd += ' install --prod';
136
+ const pm2ConfigExists = await ctx.test('test -f pm2.config.*');
137
+ if (!pm2ConfigExists) {
138
+ return 'PM2 config not found';
72
139
  }
73
- else {
74
- throw new Exception(`Unsupported package manager "${pm}"`, 1774823752134);
140
+ return false;
141
+ };
142
+ const pm2SetupTask = async (ctx) => {
143
+ await ctx.run('pm2 start pm2.config.* --update-env');
144
+ await ctx.run('pm2 save');
145
+ };
146
+ const dockerSetupSkip = async (ctx) => {
147
+ if (ctx.config.dockerCompose === false) {
148
+ return 'Docker Compose disabled';
75
149
  }
76
- await ctx.run(cmd);
150
+ const composeExists = await ctx.test('test -f docker-compose.yml -o -f docker-compose.yaml -o -f compose.yml -o -f compose.yaml');
151
+ if (!composeExists) {
152
+ return 'Docker Compose config not found';
153
+ }
154
+ return false;
155
+ };
156
+ const dockerSetupTask = async (ctx) => {
157
+ await ctx.run('docker compose up -d --build --remove-orphans');
158
+ };
159
+ const clearTargetTask = async (ctx, ph) => {
160
+ const confirmed = await confirm({
161
+ message: chalk.red(`Remove entire deploy path ${ph.deployPath} on ${ctx.server.host}?`),
162
+ default: false,
163
+ });
164
+ if (!confirmed) {
165
+ console.log(chalk.yellow('Skipped clearing target'));
166
+ return;
167
+ }
168
+ await ctx.run(`rm -rf ${ph.deployPath}`);
77
169
  };
78
170
  const printDeploymentTask = async (ctx, ph) => {
79
171
  await ctx.run('date');
@@ -82,35 +174,44 @@ const printDeploymentTask = async (ctx, ph) => {
82
174
  console.log(chalk.cyan('Directory size'));
83
175
  await ctx.run('du -hd 1 .');
84
176
  };
85
- const pm2SetupTask = async (ctx) => {
86
- const pm2ConfigExists = await ctx.test('test -f pm2.config.*');
87
- if (!pm2ConfigExists) {
88
- console.log(chalk.yellow('PM2 config not found, skipping setup'));
89
- return;
90
- }
91
- await ctx.run('pm2 start pm2.config.* --update-env');
92
- await ctx.run('pm2 save');
93
- };
94
177
  export const defaultTasks = {
178
+ clearTarget: {
179
+ name: 'Clear target',
180
+ task: clearTargetTask,
181
+ },
95
182
  upload: {
96
183
  name: 'Upload files',
97
- fn: uploadTask,
184
+ skip: uploadSkip,
185
+ task: uploadTask,
186
+ },
187
+ download: {
188
+ name: 'Download files',
189
+ skip: downloadSkip,
190
+ task: downloadTask,
98
191
  },
99
192
  symlinks: {
100
193
  name: 'Create symlinks',
101
- fn: symlinksTask,
194
+ skip: symlinksSkip,
195
+ task: symlinksTask,
102
196
  },
103
197
  depInstall: {
104
198
  name: 'Install dependencies',
105
- fn: depInstallTask,
106
- },
107
- printDeployment: {
108
- name: 'Print deployment info',
109
- fn: printDeploymentTask,
199
+ skip: depInstallSkip,
200
+ task: depInstallTask,
110
201
  },
111
202
  pm2Setup: {
112
203
  name: 'PM2 setup',
113
- fn: pm2SetupTask,
204
+ skip: pm2SetupSkip,
205
+ task: pm2SetupTask,
206
+ },
207
+ dockerSetup: {
208
+ name: 'Docker Compose setup',
209
+ skip: dockerSetupSkip,
210
+ task: dockerSetupTask,
211
+ },
212
+ printDeployment: {
213
+ name: 'Print deployment info',
214
+ task: printDeploymentTask,
114
215
  },
115
216
  };
116
217
  export const defaultScenarios = {
@@ -119,9 +220,10 @@ export const defaultScenarios = {
119
220
  tasks: [
120
221
  'upload',
121
222
  'symlinks',
122
- 'depInstall',
123
- 'pm2Setup',
124
- 'printDeployment',
223
+ 'dep:install',
224
+ 'pm2:setup',
225
+ 'docker:setup',
226
+ 'print:deployment',
125
227
  ],
126
228
  },
127
229
  };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
- export type { AuthMethod, DeployerConfig, DeployerConfigInput, FilesConfig, Placeholders, ScenarioDef, ScenarioInput, ServerConfig, ServerConfigInput, SymlinkConfig, TaskContext, TaskDef, TaskFn, TaskInput, } from './def.js';
1
+ export type { AuthMethod, DeployerConfig, DeployerConfigInput, FilesConfig, Placeholders, ScenarioDef, ScenarioInput, ServerConfig, ServerConfigInput, SymlinkConfig, TaskContext, TaskDef, TaskFn, TaskInput, TaskSkipFn, } from './def.js';
2
2
  export { defineConfig } from './config.js';
3
3
  export { runScenario, runTask } from './runner.js';
4
4
  export { loadConfig, findConfigFile } from './configLoader.js';
5
+ export { buildRsyncCommand, downloadSkip, downloadTask } from './defaultTasks.js';
6
+ export type { RsyncOptions } from './defaultTasks.js';
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { defineConfig } from './config.js';
2
2
  export { runScenario, runTask } from './runner.js';
3
3
  export { loadConfig, findConfigFile } from './configLoader.js';
4
+ export { buildRsyncCommand, downloadSkip, downloadTask } from './defaultTasks.js';
package/dist/runner.js CHANGED
@@ -193,7 +193,16 @@ function buildServerListr(serverName, server, config, tasks) {
193
193
  },
194
194
  ...tasks.map(([_key, taskDef]) => ({
195
195
  title: chalk.bgCyan.black(` ${taskDef.name} `),
196
- task: async (ctx, task) => taskDef.fn(ctx.taskCtx, ctx.ph),
196
+ skip: taskDef.skip
197
+ ? (ctx) => {
198
+ ctx.taskCtx.taskConfig = taskDef.config;
199
+ return taskDef.skip(ctx.taskCtx, ctx.ph);
200
+ }
201
+ : undefined,
202
+ task: async (ctx) => {
203
+ ctx.taskCtx.taskConfig = taskDef.config;
204
+ return taskDef.task(ctx.taskCtx, ctx.ph);
205
+ },
197
206
  options: listrOptions,
198
207
  })),
199
208
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "d3ployer",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,6 +25,7 @@
25
25
  "prepare": "husky"
26
26
  },
27
27
  "dependencies": {
28
+ "@inquirer/confirm": "^6.0.10",
28
29
  "chalk": "^5.6.2",
29
30
  "commander": "^14.0.3",
30
31
  "listr2": "^10.2.1",