d3ployer 0.0.13 → 0.0.15

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
@@ -25,7 +25,7 @@ export default defineConfig({
25
25
  },
26
26
  },
27
27
  files: {
28
- basePath: './dist',
28
+ localPath: './dist',
29
29
  exclude: ['node_modules', '.git'],
30
30
  },
31
31
  symlinks: [
@@ -103,9 +103,32 @@ Configure rsync file upload.
103
103
 
104
104
  ```ts
105
105
  files: {
106
- basePath: './dist', // local directory to sync (default: '.')
107
- include: ['src/**'], // rsync include patterns
108
- exclude: ['node_modules'],// rsync exclude patterns
106
+ localPath: './dist', // local directory to sync (relative to rootDir or absolute, default: '.')
107
+ remotePath: 'app', // remote directory (relative to deployPath or absolute, default: deployPath)
108
+ include: ['src/**'], // rsync include patterns
109
+ exclude: ['node_modules'], // rsync exclude patterns
110
+ }
111
+ ```
112
+
113
+ Or as an array of entries to sync multiple directories:
114
+
115
+ ```ts
116
+ files: [
117
+ { localPath: './dist', remotePath: 'app', exclude: ['*.map'] },
118
+ { localPath: './config', remotePath: 'config' },
119
+ ]
120
+ ```
121
+
122
+ Each entry can also include per-entry rsync options:
123
+
124
+ ```ts
125
+ files: {
126
+ localPath: './dist',
127
+ exclude: ['node_modules'],
128
+ rsync: {
129
+ delete: false, // don't delete extra files on remote (default: true for upload, false for download)
130
+ dryRun: true, // preview changes without applying them
131
+ },
109
132
  }
110
133
  ```
111
134
 
@@ -188,9 +211,26 @@ tasks: {
188
211
  }
189
212
  ```
190
213
 
214
+ Use `defineTask` for type-safe task definitions with a typed config:
215
+
216
+ ```ts
217
+ import { defineConfig, defineTask } from 'd3ployer';
218
+
219
+ export default defineConfig({
220
+ tasks: {
221
+ myTask: defineTask<{ retries: number }>({
222
+ task: async (ctx) => {
223
+ const retries = ctx.taskConfig!.retries; // typed as number
224
+ },
225
+ config: { retries: 3 },
226
+ }),
227
+ },
228
+ });
229
+ ```
230
+
191
231
  Task keys are auto-converted from camelCase to colon:case (e.g. `depInstall` becomes `dep:install`).
192
232
 
193
- **TaskContext** provides:
233
+ **TaskContext\<C\>** provides:
194
234
  - `run(cmd, options?)` - execute a command on the remote server (auto cd's to `deployPath`)
195
235
  - `test(cmd)` - execute a command on the remote server, returns `boolean`
196
236
  - `runLocal(cmd, options?)` - execute a command locally
@@ -198,7 +238,7 @@ Task keys are auto-converted from camelCase to colon:case (e.g. `depInstall` bec
198
238
  - `server` - current server config (includes `name`)
199
239
  - `ssh` - SSH2Promise connection
200
240
  - `config` - full deployer config
201
- - `taskConfig` - per-task config from task definition
241
+ - `taskConfig` - per-task config from task definition (typed as `C` when using `defineTask<C>`)
202
242
 
203
243
  **RunOptions** (for `run` and `runLocal`):
204
244
  - `printOutput` - print stdout/stderr (default: `true`)
@@ -234,8 +274,8 @@ scenarios: {
234
274
 
235
275
  | Task | Description |
236
276
  | ------------------- | -------------------------------------------------------- |
237
- | `upload` | Rsync files to the remote server |
238
- | `download` | Rsync files from the remote server (uses task config) |
277
+ | `upload` | Rsync files to the remote server (supports multiple file entries) |
278
+ | `download` | Rsync files from the remote server (uses task config, supports multiple file entries) |
239
279
  | `symlinks` | Create configured symlinks on the remote server |
240
280
  | `install:packages` | Install dependencies via npm/yarn/pnpm |
241
281
  | `setup:pm2` | Start/restart PM2 processes (auto-detects pm2.config.*) |
package/dist/config.d.ts CHANGED
@@ -1,2 +1,3 @@
1
- import type { DeployerConfig, DeployerConfigInput } from './def.js';
1
+ import type { DeployerConfig, DeployerConfigInput, TaskConfigBase } from './def.js';
2
+ export declare function defineTask<C>(input: TaskConfigBase<C>): TaskConfigBase<C>;
2
3
  export declare function defineConfig(input: DeployerConfigInput): DeployerConfig;
package/dist/config.js CHANGED
@@ -35,6 +35,9 @@ function normalizeScenario(key, input) {
35
35
  tasks: input.tasks,
36
36
  };
37
37
  }
38
+ export function defineTask(input) {
39
+ return input;
40
+ }
38
41
  export function defineConfig(input) {
39
42
  const servers = {};
40
43
  for (const [name, serverInput] of Object.entries(input.servers)) {
package/dist/def.d.ts CHANGED
@@ -19,11 +19,18 @@ export interface ServerConfig {
19
19
  initCmd?: string;
20
20
  }
21
21
  export type ServerConfigInput = Partial<ServerConfig> & Pick<ServerConfig, 'host' | 'deployPath'>;
22
- export interface FilesConfig {
23
- basePath?: string;
22
+ export type RsyncOptions = {
23
+ delete?: boolean;
24
+ dryRun?: boolean;
25
+ };
26
+ export interface FilesConfigBase {
27
+ localPath?: string;
28
+ remotePath?: string;
24
29
  include?: string[];
25
30
  exclude?: string[];
31
+ rsync?: RsyncOptions;
26
32
  }
33
+ export type FilesConfig = FilesConfigBase | FilesConfigBase[];
27
34
  export interface SymlinkConfig {
28
35
  path: string;
29
36
  target: string;
@@ -77,7 +84,7 @@ export type RunOptions = {
77
84
  printOutput?: boolean;
78
85
  ignoreError?: boolean;
79
86
  };
80
- export interface TaskContext {
87
+ export interface TaskContext<C = any> {
81
88
  server: ServerConfig & {
82
89
  name: string;
83
90
  };
@@ -87,16 +94,18 @@ export interface TaskContext {
87
94
  testLocal: (cmd: string) => Promise<boolean>;
88
95
  run: (cmd: string, options?: RunOptions) => Promise<ExecResult>;
89
96
  test: (cmd: string) => Promise<boolean>;
90
- taskConfig?: any;
97
+ taskConfig?: C;
91
98
  }
92
- export type TaskFn = (ctx: TaskContext, ph: Placeholders, task: ListrTaskWrapper<any, any, any>) => Promise<void>;
99
+ export type TaskFn<C = any> = (ctx: TaskContext<C>, ph: Placeholders, task: ListrTaskWrapper<any, any, any>) => Promise<void>;
93
100
  export type TaskSkipFn = (ctx: TaskContext, ph: Placeholders) => Promise<boolean | string> | boolean | string;
94
- export type TaskInput = TaskFn | {
101
+ export type TaskConfigBase<C = any> = {
95
102
  name?: string;
96
- task: TaskFn;
103
+ task: TaskFn<C>;
97
104
  skip?: TaskSkipFn;
98
- config?: any;
105
+ config?: C;
99
106
  };
107
+ export type TaskConfig<T = any> = T extends TaskFn<infer C> ? TaskConfigBase<C> : TaskConfigBase;
108
+ export type TaskInput<C = any> = TaskFn<C> | TaskConfig<C>;
100
109
  export interface ScenarioDef {
101
110
  name: string;
102
111
  tasks: string[];
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
- export type { AuthMethod, DeployerConfig, DeployerConfigInput, FilesConfig, LogsConfig, Placeholders, ScenarioDef, ScenarioInput, ServerConfig, ServerConfigInput, SymlinkConfig, TaskContext, TaskDef, TaskFn, TaskInput, TaskSkipFn, } from './def.js';
2
- export { defineConfig } from './config.js';
1
+ export type { AuthMethod, DeployerConfig, DeployerConfigInput, FilesConfig, LogsConfig, Placeholders, RsyncOptions, ScenarioDef, ScenarioInput, ServerConfig, ServerConfigInput, SymlinkConfig, TaskContext, TaskDef, TaskFn, TaskInput, TaskSkipFn, } from './def.js';
2
+ export { defineConfig, defineTask } from './config.js';
3
3
  export { runScenario, runTask } from './runner.js';
4
4
  export { loadConfig, findConfigFile } from './configLoader.js';
5
5
  export { buildRsyncCommand, downloadSkip, downloadTask } from './tasks/index.js';
6
- export type { RsyncOptions } from './tasks/index.js';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { defineConfig } from './config.js';
1
+ export { defineConfig, defineTask } from './config.js';
2
2
  export { runScenario, runTask } from './runner.js';
3
3
  export { loadConfig, findConfigFile } from './configLoader.js';
4
4
  export { buildRsyncCommand, downloadSkip, downloadTask } from './tasks/index.js';
@@ -1,3 +1,3 @@
1
- import type { TaskFn, TaskSkipFn } from '../def.js';
1
+ import type { FilesConfig, TaskFn, TaskSkipFn } from '../def.js';
2
2
  export declare const downloadSkip: TaskSkipFn;
3
- export declare const downloadTask: TaskFn;
3
+ export declare const downloadTask: TaskFn<FilesConfig>;
@@ -9,20 +9,26 @@ export const downloadSkip = (ctx) => {
9
9
  : false;
10
10
  };
11
11
  export const downloadTask = async (ctx, ph) => {
12
- const files = ctx.taskConfig;
13
- if (!files) {
12
+ if (!ctx.taskConfig) {
14
13
  throw new Exception('No files configuration provided in task config', 1784523741234);
15
14
  }
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
- ...ctx.taskConfig,
25
- });
26
- console.log(chalk.grey(command));
27
- await ctx.runLocal(command);
15
+ const filesArray = ctx.taskConfig instanceof Array
16
+ ? ctx.taskConfig
17
+ : [ctx.taskConfig];
18
+ for (const filesEntry of filesArray) {
19
+ const localBase = filesEntry.localPath?.startsWith('/')
20
+ ? filesEntry.localPath
21
+ : path.resolve(ctx.config.rootDir, filesEntry.localPath ?? '.');
22
+ const remotePath = filesEntry.remotePath?.startsWith('/')
23
+ ? filesEntry.remotePath
24
+ : path.join(ctx.server.deployPath, filesEntry.remotePath ?? '.');
25
+ const source = `${ctx.server.username}@${ctx.server.host}:${remotePath}/`;
26
+ const dest = localBase.endsWith('/') ? localBase : localBase + '/';
27
+ const command = buildRsyncCommand(ctx.server, source, dest, filesEntry, {
28
+ delete: false,
29
+ ...filesEntry.rsync,
30
+ });
31
+ console.log(chalk.grey(command));
32
+ await ctx.runLocal(command);
33
+ }
28
34
  };
@@ -1,6 +1,2 @@
1
- import type { FilesConfig, ServerConfig } from '../../def.js';
2
- export type RsyncOptions = {
3
- delete?: boolean;
4
- dryRun?: boolean;
5
- };
6
- export declare function buildRsyncCommand(server: ServerConfig, source: string, dest: string, files: FilesConfig, options?: RsyncOptions): string;
1
+ import type { FilesConfigBase, RsyncOptions, ServerConfig } from '../../def.js';
2
+ export declare function buildRsyncCommand(server: ServerConfig, source: string, dest: string, files: FilesConfigBase, options?: RsyncOptions): string;
@@ -1,6 +1,5 @@
1
1
  import type { ScenarioDef, TaskDef } from '../def.js';
2
2
  export { buildRsyncCommand } from './helpers/rsync.js';
3
- export type { RsyncOptions } from './helpers/rsync.js';
4
3
  export { downloadSkip, downloadTask } from './download.js';
5
4
  export declare const defaultTasks: Record<string, TaskDef>;
6
5
  export declare const defaultScenarios: Record<string, ScenarioDef>;
@@ -1,3 +1,3 @@
1
- import type { TaskFn, TaskSkipFn } from '../def.js';
1
+ import type { FilesConfig, TaskFn, TaskSkipFn } from '../def.js';
2
2
  export declare const uploadSkip: TaskSkipFn;
3
- export declare const uploadTask: TaskFn;
3
+ export declare const uploadTask: TaskFn<FilesConfig>;
@@ -8,18 +8,25 @@ export const uploadSkip = (ctx) => {
8
8
  : false;
9
9
  };
10
10
  export const uploadTask = async (ctx, ph) => {
11
- const files = ctx.config.files;
12
- const localBase = files.basePath?.startsWith('/')
13
- ? files.basePath
14
- : path.resolve(ctx.config.rootDir, files.basePath ?? '.');
15
- const remotePath = ph.deployPath;
16
- const dest = `${ctx.server.username}@${ctx.server.host}:${remotePath}`;
17
- const source = localBase.endsWith('/') ? localBase : localBase + '/';
18
- await ctx.run(`mkdir -p ${remotePath}`);
19
- const command = buildRsyncCommand(ctx.server, source, dest, files, {
20
- delete: true,
21
- ...ctx.taskConfig,
22
- });
23
- console.log(chalk.grey(command));
24
- await ctx.runLocal(command);
11
+ const configSource = ctx.taskConfig ?? ctx.config.files;
12
+ const filesArray = configSource instanceof Array
13
+ ? configSource
14
+ : [configSource];
15
+ for (const filesEntry of filesArray) {
16
+ const localBase = filesEntry.localPath?.startsWith('/')
17
+ ? filesEntry.localPath
18
+ : path.resolve(ctx.config.rootDir, filesEntry.localPath ?? '.');
19
+ const remotePath = filesEntry.remotePath?.startsWith('/')
20
+ ? filesEntry.remotePath
21
+ : path.join(ctx.server.deployPath, filesEntry.remotePath ?? '.');
22
+ const dest = `${ctx.server.username}@${ctx.server.host}:${remotePath}`;
23
+ const source = localBase.endsWith('/') ? localBase : localBase + '/';
24
+ await ctx.run(`mkdir -p ${remotePath}`);
25
+ const command = buildRsyncCommand(ctx.server, source, dest, filesEntry, {
26
+ delete: true,
27
+ ...filesEntry.rsync,
28
+ });
29
+ console.log(chalk.grey(command));
30
+ await ctx.runLocal(command);
31
+ }
25
32
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "d3ployer",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "bin": {