d3ployer 0.0.5 → 0.0.7
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 +88 -25
- package/dist/config.d.ts +1 -0
- package/dist/config.js +38 -8
- package/dist/def.d.ts +12 -4
- package/dist/defaultTasks.d.ts +7 -1
- package/dist/defaultTasks.js +134 -36
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/runner.js +10 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -33,11 +33,11 @@ export default defineConfig({
|
|
|
33
33
|
],
|
|
34
34
|
tasks: {
|
|
35
35
|
restart: async (ctx) => {
|
|
36
|
-
await ctx.
|
|
36
|
+
await ctx.run('systemctl restart myapp');
|
|
37
37
|
},
|
|
38
38
|
},
|
|
39
39
|
scenarios: {
|
|
40
|
-
deploy: ['upload', 'symlinks', '
|
|
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` | - |
|
|
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.
|
|
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
|
-
- `
|
|
123
|
-
- `
|
|
124
|
-
- `
|
|
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', '
|
|
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
|
|
146
|
-
|
|
|
147
|
-
| `upload`
|
|
148
|
-
| `
|
|
149
|
-
| `
|
|
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`
|
|
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
package/dist/config.js
CHANGED
|
@@ -11,31 +11,61 @@ function resolveServer(input) {
|
|
|
11
11
|
}
|
|
12
12
|
function normalizeTask(key, input) {
|
|
13
13
|
if (typeof input === 'function') {
|
|
14
|
-
return {
|
|
14
|
+
return {
|
|
15
|
+
name: key,
|
|
16
|
+
task: input,
|
|
17
|
+
};
|
|
15
18
|
}
|
|
16
|
-
return {
|
|
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 {
|
|
28
|
+
return {
|
|
29
|
+
name: key,
|
|
30
|
+
tasks: input,
|
|
31
|
+
};
|
|
21
32
|
}
|
|
22
|
-
return {
|
|
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 = {
|
|
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 = {
|
|
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
|
-
|
|
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 {
|
package/dist/def.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export interface ServerConfig {
|
|
|
14
14
|
password?: string;
|
|
15
15
|
agent?: string;
|
|
16
16
|
deployPath: string;
|
|
17
|
-
packageManager?: PackageManagerConfig;
|
|
17
|
+
packageManager?: PackageManagerConfig | false;
|
|
18
18
|
initCmd?: string;
|
|
19
19
|
}
|
|
20
20
|
export type ServerConfigInput = Partial<ServerConfig> & Pick<ServerConfig, 'host' | 'deployPath'>;
|
|
@@ -52,15 +52,21 @@ export interface TaskContext {
|
|
|
52
52
|
testLocal: (cmd: string) => Promise<boolean>;
|
|
53
53
|
run: (cmd: string, options?: RunOptions) => Promise<ExecResult>;
|
|
54
54
|
test: (cmd: string) => Promise<boolean>;
|
|
55
|
+
taskConfig?: any;
|
|
55
56
|
}
|
|
56
57
|
export type TaskFn = (ctx: TaskContext, ph: Placeholders) => Promise<void>;
|
|
58
|
+
export type TaskSkipFn = (ctx: TaskContext, ph: Placeholders) => Promise<boolean | string> | boolean | string;
|
|
57
59
|
export interface TaskDef {
|
|
58
60
|
name: string;
|
|
59
|
-
|
|
61
|
+
task: TaskFn;
|
|
62
|
+
skip?: TaskSkipFn;
|
|
63
|
+
config?: any;
|
|
60
64
|
}
|
|
61
65
|
export type TaskInput = TaskFn | {
|
|
62
|
-
name
|
|
66
|
+
name?: string;
|
|
63
67
|
task: TaskFn;
|
|
68
|
+
skip?: TaskSkipFn;
|
|
69
|
+
config?: any;
|
|
64
70
|
};
|
|
65
71
|
export interface ScenarioDef {
|
|
66
72
|
name: string;
|
|
@@ -73,9 +79,11 @@ export type ScenarioInput = string[] | {
|
|
|
73
79
|
export interface DeployerConfig {
|
|
74
80
|
rootDir: string;
|
|
75
81
|
servers: Record<string, ServerConfig>;
|
|
76
|
-
packageManager?: PackageManagerConfig;
|
|
77
82
|
files?: FilesConfig;
|
|
78
83
|
symlinks?: SymlinkConfig[];
|
|
84
|
+
packageManager?: PackageManagerConfig | false;
|
|
85
|
+
pm2?: boolean;
|
|
86
|
+
dockerCompose?: boolean;
|
|
79
87
|
tasks?: Record<string, TaskDef>;
|
|
80
88
|
scenarios?: Record<string, ScenarioDef>;
|
|
81
89
|
}
|
package/dist/defaultTasks.d.ts
CHANGED
|
@@ -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>;
|
package/dist/defaultTasks.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import confirm from '@inquirer/confirm';
|
|
1
2
|
import chalk from 'chalk';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { Exception } from './utils/index.js';
|
|
4
|
-
function buildRsyncCommand(server, source, dest, files) {
|
|
5
|
-
const
|
|
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
|
|
17
|
-
if (files.include) {
|
|
18
|
-
for (const pattern of files.include) {
|
|
19
|
-
args.push(`--include=${pattern}`);
|
|
20
|
-
}
|
|
21
|
-
args.push('--exclude=*');
|
|
22
|
-
}
|
|
23
22
|
if (files.exclude) {
|
|
24
23
|
for (const pattern of files.exclude) {
|
|
25
|
-
args.push(`--exclude
|
|
24
|
+
args.push(`--exclude="${pattern}"`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (files.include) {
|
|
28
|
+
for (const pattern of files.include) {
|
|
29
|
+
args.push(`--include="${pattern}"`);
|
|
26
30
|
}
|
|
31
|
+
args.push('--exclude="*"');
|
|
27
32
|
}
|
|
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,43 @@ 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));
|
|
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));
|
|
44
78
|
await ctx.runLocal(command);
|
|
45
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
|
+
};
|
|
46
86
|
const symlinksTask = async (ctx, ph) => {
|
|
47
87
|
const symlinks = ctx.config.symlinks;
|
|
48
|
-
if (!symlinks || symlinks.length === 0) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
88
|
for (const link of symlinks) {
|
|
52
89
|
const target = link.target.startsWith('/')
|
|
53
90
|
? link.target
|
|
@@ -58,6 +95,19 @@ const symlinksTask = async (ctx, ph) => {
|
|
|
58
95
|
await ctx.run(`ln -sfn ${target} ${path}`);
|
|
59
96
|
}
|
|
60
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
|
+
};
|
|
61
111
|
const depInstallTask = async (ctx) => {
|
|
62
112
|
const config = {
|
|
63
113
|
manager: 'npm',
|
|
@@ -82,6 +132,44 @@ const depInstallTask = async (ctx) => {
|
|
|
82
132
|
}
|
|
83
133
|
await ctx.run(cmd);
|
|
84
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
|
+
const dockerSetupSkip = async (ctx) => {
|
|
150
|
+
if (ctx.config.dockerCompose === false) {
|
|
151
|
+
return 'Docker Compose disabled';
|
|
152
|
+
}
|
|
153
|
+
const composeExists = await ctx.test('test -f docker-compose.yml -o -f docker-compose.yaml -o -f compose.yml -o -f compose.yaml');
|
|
154
|
+
if (!composeExists) {
|
|
155
|
+
return 'Docker Compose config not found';
|
|
156
|
+
}
|
|
157
|
+
return false;
|
|
158
|
+
};
|
|
159
|
+
const dockerSetupTask = async (ctx) => {
|
|
160
|
+
await ctx.run('docker compose up -d --build --remove-orphans');
|
|
161
|
+
};
|
|
162
|
+
const clearTargetTask = async (ctx, ph) => {
|
|
163
|
+
const confirmed = await confirm({
|
|
164
|
+
message: chalk.red(`Remove entire deploy path ${ph.deployPath} on ${ctx.server.host}?`),
|
|
165
|
+
default: false,
|
|
166
|
+
});
|
|
167
|
+
if (!confirmed) {
|
|
168
|
+
console.log(chalk.yellow('Skipped clearing target'));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
await ctx.run(`rm -rf ${ph.deployPath}`);
|
|
172
|
+
};
|
|
85
173
|
const printDeploymentTask = async (ctx, ph) => {
|
|
86
174
|
await ctx.run('date');
|
|
87
175
|
console.log(chalk.cyan('Deployment directory'), ph.deployPath);
|
|
@@ -89,35 +177,44 @@ const printDeploymentTask = async (ctx, ph) => {
|
|
|
89
177
|
console.log(chalk.cyan('Directory size'));
|
|
90
178
|
await ctx.run('du -hd 1 .');
|
|
91
179
|
};
|
|
92
|
-
const pm2SetupTask = async (ctx) => {
|
|
93
|
-
const pm2ConfigExists = await ctx.test('test -f pm2.config.*');
|
|
94
|
-
if (!pm2ConfigExists) {
|
|
95
|
-
console.log(chalk.yellow('PM2 config not found, skipping setup'));
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
await ctx.run('pm2 start pm2.config.* --update-env');
|
|
99
|
-
await ctx.run('pm2 save');
|
|
100
|
-
};
|
|
101
180
|
export const defaultTasks = {
|
|
181
|
+
clearTarget: {
|
|
182
|
+
name: 'Clear target',
|
|
183
|
+
task: clearTargetTask,
|
|
184
|
+
},
|
|
102
185
|
upload: {
|
|
103
186
|
name: 'Upload files',
|
|
104
|
-
|
|
187
|
+
skip: uploadSkip,
|
|
188
|
+
task: uploadTask,
|
|
189
|
+
},
|
|
190
|
+
download: {
|
|
191
|
+
name: 'Download files',
|
|
192
|
+
skip: downloadSkip,
|
|
193
|
+
task: downloadTask,
|
|
105
194
|
},
|
|
106
195
|
symlinks: {
|
|
107
196
|
name: 'Create symlinks',
|
|
108
|
-
|
|
197
|
+
skip: symlinksSkip,
|
|
198
|
+
task: symlinksTask,
|
|
109
199
|
},
|
|
110
200
|
depInstall: {
|
|
111
201
|
name: 'Install dependencies',
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
printDeployment: {
|
|
115
|
-
name: 'Print deployment info',
|
|
116
|
-
fn: printDeploymentTask,
|
|
202
|
+
skip: depInstallSkip,
|
|
203
|
+
task: depInstallTask,
|
|
117
204
|
},
|
|
118
205
|
pm2Setup: {
|
|
119
206
|
name: 'PM2 setup',
|
|
120
|
-
|
|
207
|
+
skip: pm2SetupSkip,
|
|
208
|
+
task: pm2SetupTask,
|
|
209
|
+
},
|
|
210
|
+
dockerSetup: {
|
|
211
|
+
name: 'Docker Compose setup',
|
|
212
|
+
skip: dockerSetupSkip,
|
|
213
|
+
task: dockerSetupTask,
|
|
214
|
+
},
|
|
215
|
+
printDeployment: {
|
|
216
|
+
name: 'Print deployment info',
|
|
217
|
+
task: printDeploymentTask,
|
|
121
218
|
},
|
|
122
219
|
};
|
|
123
220
|
export const defaultScenarios = {
|
|
@@ -126,9 +223,10 @@ export const defaultScenarios = {
|
|
|
126
223
|
tasks: [
|
|
127
224
|
'upload',
|
|
128
225
|
'symlinks',
|
|
129
|
-
'
|
|
130
|
-
'
|
|
131
|
-
'
|
|
226
|
+
'dep:install',
|
|
227
|
+
'pm2:setup',
|
|
228
|
+
'docker:setup',
|
|
229
|
+
'print:deployment',
|
|
132
230
|
],
|
|
133
231
|
},
|
|
134
232
|
};
|
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
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
|
-
|
|
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.
|
|
3
|
+
"version": "0.0.7",
|
|
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",
|