d3ployer 0.0.11 → 0.0.13
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 +62 -33
- package/dist/cli.js +6 -4
- package/dist/configLoader.d.ts +1 -1
- package/dist/configLoader.js +36 -1
- package/dist/def.d.ts +6 -6
- package/dist/index.d.ts +1 -1
- package/dist/runner.d.ts +4 -5
- package/dist/tasks/download.js +2 -1
- package/dist/tasks/helpers/docker.d.ts +2 -0
- package/dist/tasks/helpers/docker.js +13 -0
- package/dist/tasks/helpers/rsync.d.ts +6 -0
- package/dist/tasks/helpers/rsync.js +38 -0
- package/dist/tasks/index.d.ts +2 -2
- package/dist/tasks/index.js +1 -1
- package/dist/tasks/printLogs.js +1 -1
- package/dist/tasks/setupDocker.d.ts +1 -2
- package/dist/tasks/setupDocker.js +1 -13
- package/dist/tasks/upload.d.ts +1 -5
- package/dist/tasks/upload.js +2 -31
- package/package.json +2 -1
- package/dist/defaultTasks.d.ts +0 -9
- package/dist/defaultTasks.js +0 -303
- package/dist/tasks/logsStream.d.ts +0 -5
- package/dist/tasks/logsStream.js +0 -69
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ export default defineConfig({
|
|
|
37
37
|
},
|
|
38
38
|
},
|
|
39
39
|
scenarios: {
|
|
40
|
-
deploy: ['upload', 'symlinks', '
|
|
40
|
+
deploy: ['upload', 'symlinks', 'install:packages', 'restart'],
|
|
41
41
|
},
|
|
42
42
|
});
|
|
43
43
|
```
|
|
@@ -45,24 +45,39 @@ export default defineConfig({
|
|
|
45
45
|
Then deploy:
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
dpl deploy
|
|
49
|
-
dpl deploy prod
|
|
50
|
-
dpl upload
|
|
51
|
-
dpl
|
|
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
|
|
55
56
|
|
|
56
57
|
```
|
|
57
|
-
dpl <name> [servers...]
|
|
58
|
-
dpl list
|
|
58
|
+
dpl <name> [servers...] Run a scenario or task
|
|
59
|
+
dpl list List scenarios, tasks, and servers
|
|
59
60
|
|
|
60
61
|
Options:
|
|
61
|
-
-
|
|
62
|
+
-p, --project <path> Path to deployer.config.ts
|
|
63
|
+
--skip <tasks> Comma-separated list of tasks to skip
|
|
64
|
+
--config <task.key=value...> Override task config values at runtime
|
|
62
65
|
```
|
|
63
66
|
|
|
64
67
|
If `<name>` matches a scenario, it runs all tasks in that scenario sequentially. Otherwise it runs the matching task directly.
|
|
65
68
|
|
|
69
|
+
### Runtime config overrides
|
|
70
|
+
|
|
71
|
+
Use `--config` to override individual task config values without editing `deployer.config.ts`. Values are automatically coerced to `boolean` (`true`/`false`) or `number` where applicable.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
dpl deploy --config download.dryRun=true
|
|
75
|
+
dpl upload --config upload.delete=false
|
|
76
|
+
dpl deploy --config myTask.someKey=hello --config myTask.retries=3
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The format is `taskName.path.to.key=value`, where `taskName` is the task's key in the config.
|
|
80
|
+
|
|
66
81
|
## Config
|
|
67
82
|
|
|
68
83
|
### `servers`
|
|
@@ -119,20 +134,33 @@ packageManager: {
|
|
|
119
134
|
|
|
120
135
|
### `pm2`
|
|
121
136
|
|
|
122
|
-
Set to `false` to disable
|
|
123
|
-
|
|
124
|
-
### `dockerCompose`
|
|
137
|
+
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`.
|
|
125
138
|
|
|
126
|
-
|
|
139
|
+
```ts
|
|
140
|
+
pm2: {
|
|
141
|
+
logs: {
|
|
142
|
+
time: 5, // seconds to stream (default: 3)
|
|
143
|
+
lines: 50, // log lines to show (default: 25)
|
|
144
|
+
},
|
|
145
|
+
// logs: false — disable log streaming only
|
|
146
|
+
}
|
|
147
|
+
// pm2: false — disable PM2 entirely
|
|
148
|
+
```
|
|
127
149
|
|
|
128
|
-
### `
|
|
150
|
+
### `dockerCompose`
|
|
129
151
|
|
|
130
|
-
Configure
|
|
152
|
+
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
153
|
|
|
132
154
|
```ts
|
|
133
|
-
|
|
134
|
-
|
|
155
|
+
dockerCompose: {
|
|
156
|
+
configFiles: ['docker-compose.prod.yml'], // optional, defaults to auto-detect
|
|
157
|
+
logs: {
|
|
158
|
+
time: 5, // seconds to stream (default: 3)
|
|
159
|
+
lines: 50, // log lines to show (default: 25)
|
|
160
|
+
},
|
|
161
|
+
// logs: false — disable log streaming only
|
|
135
162
|
}
|
|
163
|
+
// dockerCompose: false — disable Docker Compose entirely
|
|
136
164
|
```
|
|
137
165
|
|
|
138
166
|
### `tasks`
|
|
@@ -141,7 +169,7 @@ Custom task functions receive a `TaskContext` and `Placeholders`:
|
|
|
141
169
|
|
|
142
170
|
```ts
|
|
143
171
|
tasks: {
|
|
144
|
-
migrate: async (ctx, ph) => {
|
|
172
|
+
migrate: async (ctx, ph, task) => {
|
|
145
173
|
await ctx.run('npm run migrate');
|
|
146
174
|
},
|
|
147
175
|
}
|
|
@@ -154,7 +182,7 @@ tasks: {
|
|
|
154
182
|
myTask: {
|
|
155
183
|
name: 'My Task',
|
|
156
184
|
skip: async (ctx) => !someCondition ? 'Reason to skip' : false,
|
|
157
|
-
task: async (ctx, ph) => { /* ... */ },
|
|
185
|
+
task: async (ctx, ph, task) => { /* ... */ },
|
|
158
186
|
config: { /* passed as ctx.taskConfig */ },
|
|
159
187
|
},
|
|
160
188
|
}
|
|
@@ -187,7 +215,7 @@ Named sequences of tasks:
|
|
|
187
215
|
|
|
188
216
|
```ts
|
|
189
217
|
scenarios: {
|
|
190
|
-
deploy: ['upload', 'symlinks', '
|
|
218
|
+
deploy: ['upload', 'symlinks', 'install:packages', 'restart'],
|
|
191
219
|
}
|
|
192
220
|
```
|
|
193
221
|
|
|
@@ -197,30 +225,31 @@ Or as objects with a custom name:
|
|
|
197
225
|
scenarios: {
|
|
198
226
|
deploy: {
|
|
199
227
|
name: 'Deploy',
|
|
200
|
-
tasks: ['upload', 'symlinks', '
|
|
228
|
+
tasks: ['upload', 'symlinks', 'install:packages', 'restart'],
|
|
201
229
|
},
|
|
202
230
|
}
|
|
203
231
|
```
|
|
204
232
|
|
|
205
233
|
## Built-in tasks
|
|
206
234
|
|
|
207
|
-
| Task
|
|
208
|
-
|
|
|
209
|
-
| `upload`
|
|
210
|
-
| `download`
|
|
211
|
-
| `symlinks`
|
|
212
|
-
| `
|
|
213
|
-
| `pm2
|
|
214
|
-
| `docker
|
|
215
|
-
| `clear:target`
|
|
216
|
-
| `print:deployment`
|
|
217
|
-
| `
|
|
235
|
+
| Task | Description |
|
|
236
|
+
| ------------------- | -------------------------------------------------------- |
|
|
237
|
+
| `upload` | Rsync files to the remote server |
|
|
238
|
+
| `download` | Rsync files from the remote server (uses task config) |
|
|
239
|
+
| `symlinks` | Create configured symlinks on the remote server |
|
|
240
|
+
| `install:packages` | Install dependencies via npm/yarn/pnpm |
|
|
241
|
+
| `setup:pm2` | Start/restart PM2 processes (auto-detects pm2.config.*) |
|
|
242
|
+
| `setup:docker` | Run docker compose up (auto-detects compose files) |
|
|
243
|
+
| `clear:target` | Remove the entire deploy path (with confirmation prompt) |
|
|
244
|
+
| `print:deployment` | Print deployment info (date, files, disk usage) |
|
|
245
|
+
| `print:logs:pm2` | Stream PM2 logs for a configured duration |
|
|
246
|
+
| `print:logs:docker` | Stream Docker Compose logs for a configured duration |
|
|
218
247
|
|
|
219
248
|
### Default `deploy` scenario
|
|
220
249
|
|
|
221
|
-
The built-in `deploy` scenario runs: `upload` → `symlinks` → `
|
|
250
|
+
The built-in `deploy` scenario runs: `upload` → `symlinks` → `install:packages` → `setup:pm2` → `setup:docker` → `print:deployment` → `print:logs:pm2` → `print:logs:docker`
|
|
222
251
|
|
|
223
|
-
Tasks with skip conditions will be automatically skipped when not applicable (e.g. `pm2
|
|
252
|
+
Tasks with skip conditions will be automatically skipped when not applicable (e.g. `setup:pm2` skips if no PM2 config file exists).
|
|
224
253
|
|
|
225
254
|
## Requirements
|
|
226
255
|
|
package/dist/cli.js
CHANGED
|
@@ -6,15 +6,16 @@ import { runScenario, runTask } from './runner.js';
|
|
|
6
6
|
const program = new Command()
|
|
7
7
|
.name('deployer')
|
|
8
8
|
.description('TypeScript deployment tool')
|
|
9
|
-
.option('-
|
|
10
|
-
.option('--skip <tasks>', 'comma-separated list of tasks to skip')
|
|
9
|
+
.option('-p, --project <path>', 'path to deployer.config.ts')
|
|
10
|
+
.option('--skip <tasks>', 'comma-separated list of tasks to skip')
|
|
11
|
+
.option('-c, --config <taskConfig...>', 'option to override task config (format: path.to.entity=value)');
|
|
11
12
|
program
|
|
12
13
|
.argument('<name>', 'scenario or task name')
|
|
13
14
|
.argument('[servers...]', 'target server(s)')
|
|
14
15
|
.action(async (name, servers) => {
|
|
15
16
|
try {
|
|
16
17
|
const opts = program.opts();
|
|
17
|
-
const config = await loadConfig(opts.config);
|
|
18
|
+
const config = await loadConfig(opts.project, opts.config);
|
|
18
19
|
const serverList = servers.length > 0 ? servers : undefined;
|
|
19
20
|
const skipTasks = opts.skip
|
|
20
21
|
? opts.skip.split(',').map((s) => s.trim()).filter(Boolean)
|
|
@@ -36,7 +37,8 @@ program
|
|
|
36
37
|
.description('list available scenarios, tasks and servers')
|
|
37
38
|
.action(async () => {
|
|
38
39
|
try {
|
|
39
|
-
const
|
|
40
|
+
const opts = program.opts();
|
|
41
|
+
const config = await loadConfig(opts.project, opts.config);
|
|
40
42
|
console.log(chalk.bold('\nScenarios:'));
|
|
41
43
|
const scenarios = config.scenarios ?? {};
|
|
42
44
|
const scenarioKeys = Object.keys(scenarios);
|
package/dist/configLoader.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { DeployerConfig } from './def.js';
|
|
2
2
|
export declare function findConfigFile(startDir?: string): string;
|
|
3
|
-
export declare function loadConfig(configPath?: string): Promise<DeployerConfig>;
|
|
3
|
+
export declare function loadConfig(configPath?: string, configOverrides?: string[]): Promise<DeployerConfig>;
|
package/dist/configLoader.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { set } from 'lodash-es';
|
|
1
2
|
import fs from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { Exception } from './utils/Exception.js';
|
|
@@ -15,7 +16,7 @@ export function findConfigFile(startDir = process.cwd()) {
|
|
|
15
16
|
} while (parent !== dir);
|
|
16
17
|
throw new Exception(`Could not find ${CONFIG_FILENAME} in ${startDir} or any parent directory`, 1774741892462);
|
|
17
18
|
}
|
|
18
|
-
export async function loadConfig(configPath) {
|
|
19
|
+
export async function loadConfig(configPath, configOverrides) {
|
|
19
20
|
const resolvedPath = configPath ?? findConfigFile();
|
|
20
21
|
const absolutePath = path.resolve(resolvedPath);
|
|
21
22
|
if (!fs.existsSync(absolutePath)) {
|
|
@@ -27,5 +28,39 @@ export async function loadConfig(configPath) {
|
|
|
27
28
|
if (!config.servers || Object.keys(config.servers).length === 0) {
|
|
28
29
|
throw new Exception('Config must define at least one server', 1774741913430);
|
|
29
30
|
}
|
|
31
|
+
// override task config if provided via CLI
|
|
32
|
+
if (configOverrides) {
|
|
33
|
+
for (const configOverride of configOverrides) {
|
|
34
|
+
const eqIdx = configOverride.indexOf('=');
|
|
35
|
+
if (eqIdx === -1) {
|
|
36
|
+
console.warn(`Invalid task config override (missing '='): ${configOverride}`);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const overrideKey = configOverride.slice(0, eqIdx);
|
|
40
|
+
const rawValue = configOverride.slice(eqIdx + 1);
|
|
41
|
+
const keyParts = overrideKey.split('.');
|
|
42
|
+
if (keyParts.length < 2) {
|
|
43
|
+
console.warn(`Invalid task config override key: ${overrideKey}`);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const [taskName, ...configPathParts] = keyParts;
|
|
47
|
+
const taskDef = config.tasks?.[taskName];
|
|
48
|
+
if (!taskDef) {
|
|
49
|
+
console.warn(`Task not found for config override: ${taskName}`);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (!taskDef.config) {
|
|
53
|
+
taskDef.config = {};
|
|
54
|
+
}
|
|
55
|
+
const value = rawValue === 'true'
|
|
56
|
+
? true
|
|
57
|
+
: rawValue === 'false'
|
|
58
|
+
? false
|
|
59
|
+
: rawValue !== '' && !isNaN(Number(rawValue))
|
|
60
|
+
? Number(rawValue)
|
|
61
|
+
: rawValue;
|
|
62
|
+
set(taskDef.config, configPathParts, value);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
30
65
|
return config;
|
|
31
66
|
}
|
package/dist/def.d.ts
CHANGED
|
@@ -40,6 +40,12 @@ export type DockerComposeConfig = {
|
|
|
40
40
|
configFiles: string[];
|
|
41
41
|
logs: ConfigOrDisable<LogsConfig>;
|
|
42
42
|
};
|
|
43
|
+
export interface TaskDef {
|
|
44
|
+
name: string;
|
|
45
|
+
task: TaskFn;
|
|
46
|
+
skip?: TaskSkipFn;
|
|
47
|
+
config?: any;
|
|
48
|
+
}
|
|
43
49
|
export interface DeployerConfig {
|
|
44
50
|
rootDir: string;
|
|
45
51
|
servers: Record<string, ServerConfig>;
|
|
@@ -85,12 +91,6 @@ export interface TaskContext {
|
|
|
85
91
|
}
|
|
86
92
|
export type TaskFn = (ctx: TaskContext, ph: Placeholders, task: ListrTaskWrapper<any, any, any>) => Promise<void>;
|
|
87
93
|
export type TaskSkipFn = (ctx: TaskContext, ph: Placeholders) => Promise<boolean | string> | boolean | string;
|
|
88
|
-
export interface TaskDef {
|
|
89
|
-
name: string;
|
|
90
|
-
task: TaskFn;
|
|
91
|
-
skip?: TaskSkipFn;
|
|
92
|
-
config?: any;
|
|
93
|
-
}
|
|
94
94
|
export type TaskInput = TaskFn | {
|
|
95
95
|
name?: string;
|
|
96
96
|
task: TaskFn;
|
package/dist/index.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export { defineConfig } 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/
|
|
6
|
+
export type { RsyncOptions } from './tasks/index.js';
|
package/dist/runner.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { DeployerConfig, ServerConfig, TaskDef } from './def.js';
|
|
2
2
|
export declare function resolveServers(config: DeployerConfig, serverNames?: string[]): Record<string, ServerConfig>;
|
|
3
3
|
export declare function resolveTaskDefs(taskNames: string[], allTasks: Record<string, TaskDef>): Record<string, TaskDef>;
|
|
4
|
-
export
|
|
4
|
+
export type RunTaskOrScenarioOptions = {
|
|
5
5
|
skip?: string[];
|
|
6
|
-
}
|
|
7
|
-
export declare function
|
|
8
|
-
|
|
9
|
-
}): Promise<void>;
|
|
6
|
+
};
|
|
7
|
+
export declare function runScenario(config: DeployerConfig, scenarioName: string, serverNames?: string[], options?: RunTaskOrScenarioOptions): Promise<void>;
|
|
8
|
+
export declare function runTask(config: DeployerConfig, taskName: string, serverNames?: string[], options?: RunTaskOrScenarioOptions): Promise<void>;
|
package/dist/tasks/download.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { Exception } from '../utils/index.js';
|
|
4
|
-
import { buildRsyncCommand } from './
|
|
4
|
+
import { buildRsyncCommand } from './helpers/rsync.js';
|
|
5
5
|
export const downloadSkip = (ctx) => {
|
|
6
6
|
const files = ctx.taskConfig;
|
|
7
7
|
return !files
|
|
@@ -21,6 +21,7 @@ export const downloadTask = async (ctx, ph) => {
|
|
|
21
21
|
const dest = localBase.endsWith('/') ? localBase : localBase + '/';
|
|
22
22
|
const command = buildRsyncCommand(ctx.server, source, dest, files, {
|
|
23
23
|
delete: false,
|
|
24
|
+
...ctx.taskConfig,
|
|
24
25
|
});
|
|
25
26
|
console.log(chalk.grey(command));
|
|
26
27
|
await ctx.runLocal(command);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function buildDockerComposeTestCmd(dockerComposeConfig) {
|
|
2
|
+
if (dockerComposeConfig === false) {
|
|
3
|
+
return 'false';
|
|
4
|
+
}
|
|
5
|
+
const configFiles = dockerComposeConfig.configFiles ?? [
|
|
6
|
+
'docker-compose.yml',
|
|
7
|
+
'docker-compose.yaml',
|
|
8
|
+
'compose.yml',
|
|
9
|
+
'compose.yaml',
|
|
10
|
+
];
|
|
11
|
+
const testCmdPart = configFiles.map(f => `-f ${f}`);
|
|
12
|
+
return `test ${testCmdPart.join(' -o ')}`;
|
|
13
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
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;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export function buildRsyncCommand(server, source, dest, files, options = {}) {
|
|
2
|
+
options = {
|
|
3
|
+
delete: true,
|
|
4
|
+
dryRun: false,
|
|
5
|
+
...options,
|
|
6
|
+
};
|
|
7
|
+
const args = ['rsync', '-avz', '--progress=info2'];
|
|
8
|
+
if (options.delete) {
|
|
9
|
+
args.push('--delete');
|
|
10
|
+
}
|
|
11
|
+
if (options.dryRun) {
|
|
12
|
+
args.push('--dry-run');
|
|
13
|
+
}
|
|
14
|
+
// ssh shell
|
|
15
|
+
const sshParts = ['ssh'];
|
|
16
|
+
if (server.port && server.port !== 22) {
|
|
17
|
+
sshParts.push(`-p ${server.port}`);
|
|
18
|
+
}
|
|
19
|
+
if (server.authMethod === 'key' && server.privateKey) {
|
|
20
|
+
sshParts.push(`-i ${server.privateKey}`);
|
|
21
|
+
}
|
|
22
|
+
sshParts.push('-o StrictHostKeyChecking=no');
|
|
23
|
+
args.push('-e', `"${sshParts.join(' ')}"`);
|
|
24
|
+
// include/exclude
|
|
25
|
+
if (files.exclude) {
|
|
26
|
+
for (const pattern of files.exclude) {
|
|
27
|
+
args.push(`--exclude="${pattern}"`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (files.include) {
|
|
31
|
+
for (const pattern of files.include) {
|
|
32
|
+
args.push(`--include="${pattern}"`);
|
|
33
|
+
}
|
|
34
|
+
args.push('--exclude="*"');
|
|
35
|
+
}
|
|
36
|
+
args.push(source, dest);
|
|
37
|
+
return args.join(' ');
|
|
38
|
+
}
|
package/dist/tasks/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ScenarioDef, TaskDef } from '../def.js';
|
|
2
|
-
export { buildRsyncCommand } from './
|
|
3
|
-
export type { RsyncOptions } from './
|
|
2
|
+
export { buildRsyncCommand } from './helpers/rsync.js';
|
|
3
|
+
export type { RsyncOptions } from './helpers/rsync.js';
|
|
4
4
|
export { downloadSkip, downloadTask } from './download.js';
|
|
5
5
|
export declare const defaultTasks: Record<string, TaskDef>;
|
|
6
6
|
export declare const defaultScenarios: Record<string, ScenarioDef>;
|
package/dist/tasks/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { setupDockerSkip, setupDockerTask } from './setupDocker.js';
|
|
|
7
7
|
import { setupPm2Skip, setupPm2Task } from './setupPm2.js';
|
|
8
8
|
import { symlinksSkip, symlinksTask } from './symlinks.js';
|
|
9
9
|
import { uploadSkip, uploadTask } from './upload.js';
|
|
10
|
-
export { buildRsyncCommand } from './
|
|
10
|
+
export { buildRsyncCommand } from './helpers/rsync.js';
|
|
11
11
|
export { downloadSkip, downloadTask } from './download.js';
|
|
12
12
|
export const defaultTasks = {
|
|
13
13
|
'clear:target': {
|
package/dist/tasks/printLogs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { buildDockerComposeTestCmd } from './
|
|
2
|
+
import { buildDockerComposeTestCmd } from './helpers/docker.js';
|
|
3
3
|
export const printLogsPm2Skip = async (ctx) => {
|
|
4
4
|
if (ctx.config.pm2 === false
|
|
5
5
|
|| ctx.config.pm2.logs === false) {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare function buildDockerComposeTestCmd(dockerComposeConfig: DockerComposeConfig | false): string;
|
|
1
|
+
import type { TaskFn, TaskSkipFn } from '../def.js';
|
|
3
2
|
export declare const setupDockerSkip: TaskSkipFn;
|
|
4
3
|
export declare const setupDockerTask: TaskFn;
|
|
@@ -1,16 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
if (dockerComposeConfig === false) {
|
|
3
|
-
return 'false';
|
|
4
|
-
}
|
|
5
|
-
const configFiles = dockerComposeConfig.configFiles ?? [
|
|
6
|
-
'docker-compose.yml',
|
|
7
|
-
'docker-compose.yaml',
|
|
8
|
-
'compose.yml',
|
|
9
|
-
'compose.yaml',
|
|
10
|
-
];
|
|
11
|
-
const testCmdPart = configFiles.map(f => `-f ${f}`);
|
|
12
|
-
return `test ${testCmdPart.join(' -o ')}`;
|
|
13
|
-
}
|
|
1
|
+
import { buildDockerComposeTestCmd } from './helpers/docker.js';
|
|
14
2
|
export const setupDockerSkip = async (ctx) => {
|
|
15
3
|
if (ctx.config.dockerCompose === false) {
|
|
16
4
|
return 'Docker Compose disabled';
|
package/dist/tasks/upload.d.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export type RsyncOptions = {
|
|
3
|
-
delete?: boolean;
|
|
4
|
-
};
|
|
5
|
-
export declare function buildRsyncCommand(server: ServerConfig, source: string, dest: string, files: FilesConfig, options?: RsyncOptions): string;
|
|
1
|
+
import type { TaskFn, TaskSkipFn } from '../def.js';
|
|
6
2
|
export declare const uploadSkip: TaskSkipFn;
|
|
7
3
|
export declare const uploadTask: TaskFn;
|
package/dist/tasks/upload.js
CHANGED
|
@@ -1,36 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
const { delete: useDelete = true, } = options;
|
|
5
|
-
const args = ['rsync', '-avz', '--progress=info2'];
|
|
6
|
-
if (useDelete) {
|
|
7
|
-
args.push('--delete');
|
|
8
|
-
}
|
|
9
|
-
// ssh shell
|
|
10
|
-
const sshParts = ['ssh'];
|
|
11
|
-
if (server.port && server.port !== 22) {
|
|
12
|
-
sshParts.push(`-p ${server.port}`);
|
|
13
|
-
}
|
|
14
|
-
if (server.authMethod === 'key' && server.privateKey) {
|
|
15
|
-
sshParts.push(`-i ${server.privateKey}`);
|
|
16
|
-
}
|
|
17
|
-
sshParts.push('-o StrictHostKeyChecking=no');
|
|
18
|
-
args.push('-e', `"${sshParts.join(' ')}"`);
|
|
19
|
-
// include/exclude
|
|
20
|
-
if (files.exclude) {
|
|
21
|
-
for (const pattern of files.exclude) {
|
|
22
|
-
args.push(`--exclude="${pattern}"`);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
if (files.include) {
|
|
26
|
-
for (const pattern of files.include) {
|
|
27
|
-
args.push(`--include="${pattern}"`);
|
|
28
|
-
}
|
|
29
|
-
args.push('--exclude="*"');
|
|
30
|
-
}
|
|
31
|
-
args.push(source, dest);
|
|
32
|
-
return args.join(' ');
|
|
33
|
-
}
|
|
3
|
+
import { buildRsyncCommand } from './helpers/rsync.js';
|
|
34
4
|
export const uploadSkip = (ctx) => {
|
|
35
5
|
const files = ctx.config.files;
|
|
36
6
|
return !files
|
|
@@ -48,6 +18,7 @@ export const uploadTask = async (ctx, ph) => {
|
|
|
48
18
|
await ctx.run(`mkdir -p ${remotePath}`);
|
|
49
19
|
const command = buildRsyncCommand(ctx.server, source, dest, files, {
|
|
50
20
|
delete: true,
|
|
21
|
+
...ctx.taskConfig,
|
|
51
22
|
});
|
|
52
23
|
console.log(chalk.grey(command));
|
|
53
24
|
await ctx.runLocal(command);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "d3ployer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"@tsconfig/node24": "^24.0.4",
|
|
40
40
|
"@types/chai": "^5.2.3",
|
|
41
41
|
"@types/chai-as-promised": "^8.0.2",
|
|
42
|
+
"@types/lodash-es": "^4.17.12",
|
|
42
43
|
"@types/mocha": "^10.0.10",
|
|
43
44
|
"@types/node": "^24.10.13",
|
|
44
45
|
"c8": "^10.1.3",
|
package/dist/defaultTasks.d.ts
DELETED
|
@@ -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>;
|
package/dist/defaultTasks.js
DELETED
|
@@ -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
|
-
};
|
package/dist/tasks/logsStream.js
DELETED
|
@@ -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
|
-
};
|