d3ployer 0.0.13 → 0.0.14

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,12 +103,22 @@ 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
109
110
  }
110
111
  ```
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
+
112
122
  ### `symlinks`
113
123
 
114
124
  Create symlinks on the remote server.
@@ -188,9 +198,26 @@ tasks: {
188
198
  }
189
199
  ```
190
200
 
201
+ Use `defineTask` for type-safe task definitions with a typed config:
202
+
203
+ ```ts
204
+ import { defineConfig, defineTask } from 'd3ployer';
205
+
206
+ export default defineConfig({
207
+ tasks: {
208
+ myTask: defineTask<{ retries: number }>({
209
+ task: async (ctx) => {
210
+ const retries = ctx.taskConfig!.retries; // typed as number
211
+ },
212
+ config: { retries: 3 },
213
+ }),
214
+ },
215
+ });
216
+ ```
217
+
191
218
  Task keys are auto-converted from camelCase to colon:case (e.g. `depInstall` becomes `dep:install`).
192
219
 
193
- **TaskContext** provides:
220
+ **TaskContext\<C\>** provides:
194
221
  - `run(cmd, options?)` - execute a command on the remote server (auto cd's to `deployPath`)
195
222
  - `test(cmd)` - execute a command on the remote server, returns `boolean`
196
223
  - `runLocal(cmd, options?)` - execute a command locally
@@ -198,7 +225,7 @@ Task keys are auto-converted from camelCase to colon:case (e.g. `depInstall` bec
198
225
  - `server` - current server config (includes `name`)
199
226
  - `ssh` - SSH2Promise connection
200
227
  - `config` - full deployer config
201
- - `taskConfig` - per-task config from task definition
228
+ - `taskConfig` - per-task config from task definition (typed as `C` when using `defineTask<C>`)
202
229
 
203
230
  **RunOptions** (for `run` and `runLocal`):
204
231
  - `printOutput` - print stdout/stderr (default: `true`)
@@ -234,8 +261,8 @@ scenarios: {
234
261
 
235
262
  | Task | Description |
236
263
  | ------------------- | -------------------------------------------------------- |
237
- | `upload` | Rsync files to the remote server |
238
- | `download` | Rsync files from the remote server (uses task config) |
264
+ | `upload` | Rsync files to the remote server (supports multiple file entries) |
265
+ | `download` | Rsync files from the remote server (uses task config, supports multiple file entries) |
239
266
  | `symlinks` | Create configured symlinks on the remote server |
240
267
  | `install:packages` | Install dependencies via npm/yarn/pnpm |
241
268
  | `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,13 @@ 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 interface FilesConfigBase {
23
+ localPath?: string;
24
+ remotePath?: string;
24
25
  include?: string[];
25
26
  exclude?: string[];
26
27
  }
28
+ export type FilesConfig = FilesConfigBase | FilesConfigBase[];
27
29
  export interface SymlinkConfig {
28
30
  path: string;
29
31
  target: string;
@@ -77,7 +79,7 @@ export type RunOptions = {
77
79
  printOutput?: boolean;
78
80
  ignoreError?: boolean;
79
81
  };
80
- export interface TaskContext {
82
+ export interface TaskContext<C = any> {
81
83
  server: ServerConfig & {
82
84
  name: string;
83
85
  };
@@ -87,16 +89,18 @@ export interface TaskContext {
87
89
  testLocal: (cmd: string) => Promise<boolean>;
88
90
  run: (cmd: string, options?: RunOptions) => Promise<ExecResult>;
89
91
  test: (cmd: string) => Promise<boolean>;
90
- taskConfig?: any;
92
+ taskConfig?: C;
91
93
  }
92
- export type TaskFn = (ctx: TaskContext, ph: Placeholders, task: ListrTaskWrapper<any, any, any>) => Promise<void>;
94
+ export type TaskFn<C = any> = (ctx: TaskContext<C>, ph: Placeholders, task: ListrTaskWrapper<any, any, any>) => Promise<void>;
93
95
  export type TaskSkipFn = (ctx: TaskContext, ph: Placeholders) => Promise<boolean | string> | boolean | string;
94
- export type TaskInput = TaskFn | {
96
+ export type TaskConfigBase<C = any> = {
95
97
  name?: string;
96
- task: TaskFn;
98
+ task: TaskFn<C>;
97
99
  skip?: TaskSkipFn;
98
- config?: any;
100
+ config?: C;
99
101
  };
102
+ export type TaskConfig<T = any> = T extends TaskFn<infer C> ? TaskConfigBase<C> : TaskConfigBase;
103
+ export type TaskInput<C = any> = TaskFn<C> | TaskConfig<C>;
100
104
  export interface ScenarioDef {
101
105
  name: string;
102
106
  tasks: string[];
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
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';
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';
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
+ ...ctx.taskConfig,
30
+ });
31
+ console.log(chalk.grey(command));
32
+ await ctx.runLocal(command);
33
+ }
28
34
  };
@@ -1,6 +1,6 @@
1
- import type { FilesConfig, ServerConfig } from '../../def.js';
1
+ import type { FilesConfigBase, ServerConfig } from '../../def.js';
2
2
  export type RsyncOptions = {
3
3
  delete?: boolean;
4
4
  dryRun?: boolean;
5
5
  };
6
- export declare function buildRsyncCommand(server: ServerConfig, source: string, dest: string, files: FilesConfig, options?: RsyncOptions): string;
6
+ export declare function buildRsyncCommand(server: ServerConfig, source: string, dest: string, files: FilesConfigBase, options?: RsyncOptions): string;
@@ -8,18 +8,24 @@ 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 filesArray = ctx.config.files instanceof Array
12
+ ? ctx.config.files
13
+ : [ctx.config.files];
14
+ for (const filesEntry of filesArray) {
15
+ const localBase = filesEntry.localPath?.startsWith('/')
16
+ ? filesEntry.localPath
17
+ : path.resolve(ctx.config.rootDir, filesEntry.localPath ?? '.');
18
+ const remotePath = filesEntry.remotePath?.startsWith('/')
19
+ ? filesEntry.remotePath
20
+ : path.join(ctx.server.deployPath, filesEntry.remotePath ?? '.');
21
+ const dest = `${ctx.server.username}@${ctx.server.host}:${remotePath}`;
22
+ const source = localBase.endsWith('/') ? localBase : localBase + '/';
23
+ await ctx.run(`mkdir -p ${remotePath}`);
24
+ const command = buildRsyncCommand(ctx.server, source, dest, filesEntry, {
25
+ delete: true,
26
+ ...ctx.taskConfig,
27
+ });
28
+ console.log(chalk.grey(command));
29
+ await ctx.runLocal(command);
30
+ }
25
31
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "d3ployer",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "bin": {