@eggjs/scripts 4.0.0 → 5.0.0-beta.27

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.
Files changed (49) hide show
  1. package/README.md +2 -6
  2. package/bin/dev.js +0 -0
  3. package/dist/baseCommand.d.ts +28 -0
  4. package/dist/baseCommand.js +47 -0
  5. package/dist/commands/start.d.ts +39 -0
  6. package/dist/commands/start.js +277 -0
  7. package/dist/commands/stop.d.ts +21 -0
  8. package/dist/commands/stop.js +68 -0
  9. package/dist/helper.d.ts +11 -0
  10. package/dist/helper.js +35 -0
  11. package/dist/index.d.ts +3 -0
  12. package/dist/index.js +4 -0
  13. package/{src/types.ts → dist/types.d.ts} +4 -1
  14. package/dist/types.js +1 -0
  15. package/package.json +66 -86
  16. package/scripts/start-cluster.cjs +11 -4
  17. package/scripts/start-cluster.mjs +6 -2
  18. package/dist/commonjs/baseCommand.d.ts +0 -25
  19. package/dist/commonjs/baseCommand.js +0 -62
  20. package/dist/commonjs/commands/start.d.ts +0 -34
  21. package/dist/commonjs/commands/start.js +0 -350
  22. package/dist/commonjs/commands/stop.d.ts +0 -16
  23. package/dist/commonjs/commands/stop.js +0 -95
  24. package/dist/commonjs/helper.d.ts +0 -10
  25. package/dist/commonjs/helper.js +0 -61
  26. package/dist/commonjs/index.d.ts +0 -3
  27. package/dist/commonjs/index.js +0 -26
  28. package/dist/commonjs/package.json +0 -3
  29. package/dist/commonjs/types.d.ts +0 -9
  30. package/dist/commonjs/types.js +0 -3
  31. package/dist/esm/baseCommand.d.ts +0 -25
  32. package/dist/esm/baseCommand.js +0 -55
  33. package/dist/esm/commands/start.d.ts +0 -34
  34. package/dist/esm/commands/start.js +0 -344
  35. package/dist/esm/commands/stop.d.ts +0 -16
  36. package/dist/esm/commands/stop.js +0 -92
  37. package/dist/esm/helper.d.ts +0 -10
  38. package/dist/esm/helper.js +0 -51
  39. package/dist/esm/index.d.ts +0 -3
  40. package/dist/esm/index.js +0 -5
  41. package/dist/esm/package.json +0 -3
  42. package/dist/esm/types.d.ts +0 -9
  43. package/dist/esm/types.js +0 -2
  44. package/dist/package.json +0 -4
  45. package/src/baseCommand.ts +0 -68
  46. package/src/commands/start.ts +0 -384
  47. package/src/commands/stop.ts +0 -105
  48. package/src/helper.ts +0 -62
  49. package/src/index.ts +0 -8
@@ -1,3 +0,0 @@
1
- import Start from './commands/start.js';
2
- export * from './baseCommand.js';
3
- export { Start, Start as StartCommand, };
package/dist/esm/index.js DELETED
@@ -1,5 +0,0 @@
1
- import Start from './commands/start.js';
2
- // exports.StopCommand = require('./lib/cmd/stop');
3
- export * from './baseCommand.js';
4
- export { Start, Start as StartCommand, };
5
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE1BQU0scUJBQXFCLENBQUM7QUFFeEMsbURBQW1EO0FBRW5ELGNBQWMsa0JBQWtCLENBQUM7QUFDakMsT0FBTyxFQUNMLEtBQUssRUFBRSxLQUFLLElBQUksWUFBWSxHQUM3QixDQUFDIn0=
@@ -1,3 +0,0 @@
1
- {
2
- "type": "module"
3
- }
@@ -1,9 +0,0 @@
1
- export interface PackageEgg {
2
- framework?: boolean;
3
- typescript?: boolean;
4
- tscompiler?: string;
5
- declarations?: boolean;
6
- revert?: string | string[];
7
- require?: string | string[];
8
- import?: string | string[];
9
- }
package/dist/esm/types.js DELETED
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
package/dist/package.json DELETED
@@ -1,4 +0,0 @@
1
- {
2
- "name": "@eggjs/scripts",
3
- "version": "4.0.0"
4
- }
@@ -1,68 +0,0 @@
1
- import { debuglog } from 'node:util';
2
- import { Command, Flags, Interfaces } from '@oclif/core';
3
- import { PackageEgg } from './types.js';
4
- import { readJSON } from 'utility';
5
- import path from 'node:path';
6
-
7
- const debug = debuglog('@eggjs/scripts/baseCommand');
8
-
9
- type Flags<T extends typeof Command> = Interfaces.InferredFlags<typeof BaseCommand['baseFlags'] & T['flags']>;
10
- type Args<T extends typeof Command> = Interfaces.InferredArgs<T['args']>;
11
-
12
- export abstract class BaseCommand<T extends typeof Command> extends Command {
13
- // add the --json flag
14
- static enableJsonFlag = false;
15
-
16
- // define flags that can be inherited by any command that extends BaseCommand
17
- static baseFlags = {
18
- // 'log-level': Flags.option({
19
- // default: 'info',
20
- // helpGroup: 'GLOBAL',
21
- // options: ['debug', 'warn', 'error', 'info', 'trace'] as const,
22
- // summary: 'Specify level for logging.',
23
- // })(),
24
- };
25
-
26
- protected flags!: Flags<T>;
27
- protected args!: Args<T>;
28
-
29
- protected env = { ...process.env };
30
- protected pkg: Record<string, any>;
31
- protected isESM: boolean;
32
- protected pkgEgg: PackageEgg;
33
- protected globalExecArgv: string[] = [];
34
-
35
- public async init(): Promise<void> {
36
- await super.init();
37
- debug('[init] raw args: %o, NODE_ENV: %o', this.argv, this.env.NODE_ENV);
38
- const { args, flags } = await this.parse({
39
- flags: this.ctor.flags,
40
- baseFlags: (super.ctor as typeof BaseCommand).baseFlags,
41
- enableJsonFlag: this.ctor.enableJsonFlag,
42
- args: this.ctor.args,
43
- strict: this.ctor.strict,
44
- });
45
- this.flags = flags as Flags<T>;
46
- this.args = args as Args<T>;
47
- }
48
-
49
- protected async initBaseInfo(baseDir: string) {
50
- const pkg = await readJSON(path.join(baseDir, 'package.json'));
51
- this.pkg = pkg;
52
- this.pkgEgg = pkg.egg ?? {};
53
- this.isESM = pkg.type === 'module';
54
- debug('[initBaseInfo] baseDir: %o, pkgEgg: %o, isESM: %o', baseDir, this.pkgEgg, this.isESM);
55
- }
56
-
57
- protected async catch(err: Error & {exitCode?: number}): Promise<any> {
58
- // add any custom logic to handle errors from the command
59
- // or simply return the parent class error handling
60
- return super.catch(err);
61
- }
62
-
63
- protected async finally(_: Error | undefined): Promise<any> {
64
- // called after run and catch regardless of whether or not the command errored
65
- return super.finally(_);
66
- }
67
- }
68
-
@@ -1,384 +0,0 @@
1
- import { debuglog, promisify } from 'node:util';
2
- import path from 'node:path';
3
- import { scheduler } from 'node:timers/promises';
4
- import { spawn, SpawnOptions, ChildProcess, execFile as _execFile } from 'node:child_process';
5
- import { mkdir, rename, stat, open } from 'node:fs/promises';
6
- import { homedir } from 'node-homedir';
7
- import { Args, Flags } from '@oclif/core';
8
- import { getFrameworkPath, importResolve } from '@eggjs/utils';
9
- import { readJSON, exists, getDateStringParts } from 'utility';
10
- import { BaseCommand } from '../baseCommand.js';
11
- import { getSourceDirname } from '../helper.js';
12
-
13
- const debug = debuglog('@eggjs/scripts/commands/start');
14
-
15
- const execFile = promisify(_execFile);
16
-
17
- export interface FrameworkOptions {
18
- baseDir: string;
19
- framework?: string;
20
- }
21
-
22
- export default class Start<T extends typeof Start> extends BaseCommand<T> {
23
- static override description = 'Start server at prod mode';
24
-
25
- static override examples = [
26
- '<%= config.bin %> <%= command.id %>',
27
- ];
28
-
29
- static override args = {
30
- baseDir: Args.string({
31
- description: 'directory of application',
32
- required: false,
33
- }),
34
- };
35
-
36
- static override flags = {
37
- title: Flags.string({
38
- description: 'process title description, use for kill grep, default to `egg-server-${APP_NAME}`',
39
- }),
40
- framework: Flags.string({
41
- description: 'specify framework that can be absolute path or npm package',
42
- }),
43
- port: Flags.integer({
44
- description: 'listening port, default to `process.env.PORT`',
45
- char: 'p',
46
- }),
47
- workers: Flags.integer({
48
- char: 'c',
49
- aliases: [ 'cluster' ],
50
- description: 'numbers of app workers, default to `process.env.EGG_WORKERS` or `os.cpus().length`',
51
- }),
52
- env: Flags.string({
53
- description: 'server env, default to `process.env.EGG_SERVER_ENV`',
54
- default: process.env.EGG_SERVER_ENV,
55
- }),
56
- daemon: Flags.boolean({
57
- description: 'whether run at background daemon mode',
58
- }),
59
- stdout: Flags.string({
60
- description: 'customize stdout file',
61
- }),
62
- stderr: Flags.string({
63
- description: 'customize stderr file',
64
- }),
65
- timeout: Flags.integer({
66
- description: 'the maximum timeout(ms) when app starts',
67
- default: 300 * 1000,
68
- }),
69
- 'ignore-stderr': Flags.boolean({
70
- description: 'whether ignore stderr when app starts',
71
- }),
72
- node: Flags.string({
73
- description: 'customize node command path',
74
- default: 'node',
75
- }),
76
- require: Flags.string({
77
- summary: 'require the given module',
78
- char: 'r',
79
- multiple: true,
80
- }),
81
- sourcemap: Flags.boolean({
82
- summary: 'whether enable sourcemap support, will load `source-map-support` etc',
83
- aliases: [ 'ts', 'typescript' ],
84
- }),
85
- };
86
-
87
- isReady = false;
88
- #child: ChildProcess;
89
-
90
- protected async getFrameworkPath(options: FrameworkOptions) {
91
- return getFrameworkPath(options);
92
- }
93
-
94
- protected async getFrameworkName(frameworkPath: string) {
95
- const pkgPath = path.join(frameworkPath, 'package.json');
96
- let name = 'egg';
97
- try {
98
- const pkg = await readJSON(pkgPath);
99
- if (pkg.name) {
100
- name = pkg.name;
101
- }
102
- } catch {
103
- // ignore
104
- }
105
- return name;
106
- }
107
-
108
- protected async getServerBin() {
109
- const serverBinName = this.isESM ? 'start-cluster.mjs' : 'start-cluster.cjs';
110
- // for src paths, `./src/commands/start.js`
111
- let serverBin = path.join(getSourceDirname(), '../scripts', serverBinName);
112
- if (!(await exists(serverBin))) {
113
- // for dist paths, `./dist/esm/commands/start.js`
114
- serverBin = path.join(getSourceDirname(), '../../scripts', serverBinName);
115
- }
116
- return serverBin;
117
- }
118
-
119
- public async run(): Promise<void> {
120
- const { args, flags } = this;
121
- // context.execArgvObj = context.execArgvObj || {};
122
- // const { argv, env, cwd, execArgvObj } = context;
123
- const HOME = homedir();
124
- const logDir = path.join(HOME, 'logs');
125
-
126
- // eggctl start
127
- // eggctl start ./server
128
- // eggctl start /opt/app
129
- const cwd = process.cwd();
130
- let baseDir = args.baseDir || cwd;
131
- if (!path.isAbsolute(baseDir)) {
132
- baseDir = path.join(cwd, baseDir);
133
- }
134
- await this.initBaseInfo(baseDir);
135
-
136
- flags.framework = await this.getFrameworkPath({
137
- framework: flags.framework,
138
- baseDir,
139
- });
140
-
141
- const frameworkName = await this.getFrameworkName(flags.framework);
142
-
143
- flags.title = flags.title || `egg-server-${this.pkg.name}`;
144
-
145
- flags.stdout = flags.stdout || path.join(logDir, 'master-stdout.log');
146
- flags.stderr = flags.stderr || path.join(logDir, 'master-stderr.log');
147
-
148
- if (flags.workers === undefined && process.env.EGG_WORKERS) {
149
- flags.workers = Number(process.env.EGG_WORKERS);
150
- }
151
-
152
- // normalize env
153
- this.env.HOME = HOME;
154
- this.env.NODE_ENV = 'production';
155
-
156
- // it makes env big but more robust
157
- this.env.PATH = this.env.Path = [
158
- // for nodeinstall
159
- path.join(baseDir, 'node_modules/.bin'),
160
- // support `.node/bin`, due to npm5 will remove `node_modules/.bin`
161
- path.join(baseDir, '.node/bin'),
162
- // adjust env for win
163
- this.env.PATH || this.env.Path,
164
- ].filter(x => !!x).join(path.delimiter);
165
-
166
- // for alinode
167
- this.env.ENABLE_NODE_LOG = 'YES';
168
- this.env.NODE_LOG_DIR = this.env.NODE_LOG_DIR || path.join(logDir, 'alinode');
169
- await mkdir(this.env.NODE_LOG_DIR, { recursive: true });
170
-
171
- // cli argv -> process.env.EGG_SERVER_ENV -> `undefined` then egg will use `prod`
172
- if (flags.env) {
173
- // if undefined, should not pass key due to `spawn`, https://github.com/nodejs/node/blob/master/lib/child_process.js#L470
174
- this.env.EGG_SERVER_ENV = flags.env;
175
- }
176
-
177
- // additional execArgv
178
- const execArgv: string[] = [
179
- '--no-deprecation',
180
- '--trace-warnings',
181
- ];
182
- if (this.pkgEgg.revert) {
183
- const reverts = Array.isArray(this.pkgEgg.revert) ? this.pkgEgg.revert : [ this.pkgEgg.revert ];
184
- for (const revert of reverts) {
185
- execArgv.push(`--security-revert=${revert}`);
186
- }
187
- }
188
-
189
- // pkg.eggScriptsConfig.require
190
- const scriptsConfig: Record<string, any> = this.pkg.eggScriptsConfig;
191
- if (scriptsConfig?.require) {
192
- scriptsConfig.require = Array.isArray(scriptsConfig.require) ? scriptsConfig.require : [ scriptsConfig.require ];
193
- flags.require = [ ...scriptsConfig.require, ...(flags.require ?? []) ];
194
- }
195
-
196
- // read argv from eggScriptsConfig in package.json
197
- if (scriptsConfig) {
198
- for (const key in scriptsConfig) {
199
- const v = scriptsConfig[key];
200
- if (key.startsWith('node-options--')) {
201
- const newKey = key.replace('node-options--', '');
202
- if (v === true) {
203
- // "node-options--allow-wasi": true
204
- // => --allow-wasi
205
- execArgv.push(`--${newKey}`);
206
- } else {
207
- // "node-options--max-http-header-size": "20000"
208
- // => --max-http-header-size=20000
209
- execArgv.push(`--${newKey}=${v}`);
210
- }
211
- continue;
212
- }
213
- const existsValue = Reflect.get(flags, key);
214
- if (existsValue === undefined) {
215
- // only set if key is not pass from command line
216
- Reflect.set(flags, key, v);
217
- }
218
- }
219
- }
220
-
221
- // read `egg.typescript` from package.json
222
- if (this.pkgEgg.typescript && flags.sourcemap === undefined) {
223
- flags.sourcemap = true;
224
- }
225
- if (flags.sourcemap) {
226
- const sourceMapSupport = importResolve('source-map-support/register.js', {
227
- paths: [ getSourceDirname() ],
228
- });
229
- if (this.isESM) {
230
- execArgv.push('--import', sourceMapSupport);
231
- } else {
232
- execArgv.push('--require', sourceMapSupport);
233
- }
234
- }
235
-
236
- if (flags.port === undefined && process.env.PORT) {
237
- flags.port = parseInt(process.env.PORT);
238
- }
239
-
240
- debug('flags: %o, framework: %o, baseDir: %o, execArgv: %o',
241
- flags, frameworkName, baseDir, execArgv);
242
-
243
- const command = flags.node;
244
- const options: SpawnOptions = {
245
- env: this.env,
246
- stdio: 'inherit',
247
- detached: false,
248
- cwd: baseDir,
249
- };
250
-
251
- this.log('Starting %s application at %s', frameworkName, baseDir);
252
-
253
- // remove unused properties from stringify, alias had been remove by `removeAlias`
254
- const ignoreKeys = [ 'env', 'daemon', 'stdout', 'stderr', 'timeout', 'ignore-stderr', 'node' ];
255
- const clusterOptions = stringify({
256
- ...flags,
257
- baseDir,
258
- }, ignoreKeys);
259
- // Note: `spawn` is not like `fork`, had to pass `execArgv` yourself
260
- const serverBin = await this.getServerBin();
261
- const eggArgs = [ ...execArgv, serverBin, clusterOptions, `--title=${flags.title}` ];
262
- const spawnScript = `${command} ${eggArgs.map(a => `'${a}'`).join(' ')}`;
263
- this.log('Spawn %o', spawnScript);
264
-
265
- // whether run in the background.
266
- if (flags.daemon) {
267
- this.log(`Save log file to ${logDir}`);
268
- const [ stdout, stderr ] = await Promise.all([
269
- getRotateLog(flags.stdout),
270
- getRotateLog(flags.stderr),
271
- ]);
272
- options.stdio = [ 'ignore', stdout, stderr, 'ipc' ];
273
- options.detached = true;
274
- const child = this.#child = spawn(command, eggArgs, options);
275
- this.isReady = false;
276
- child.on('message', (msg: any) => {
277
- // https://github.com/eggjs/cluster/blob/master/src/master.ts#L119
278
- if (msg && msg.action === 'egg-ready') {
279
- this.isReady = true;
280
- this.log('%s started on %s', frameworkName, msg.data.address);
281
- child.unref();
282
- child.disconnect();
283
- }
284
- });
285
-
286
- // check start status
287
- await this.checkStatus();
288
- } else {
289
- options.stdio = [ 'inherit', 'inherit', 'inherit', 'ipc' ];
290
- const child = this.#child = spawn(command, eggArgs, options);
291
- child.once('exit', code => {
292
- if (!code) return;
293
- // command should exit after child process exit
294
- this.exit(code);
295
- });
296
-
297
- // attach master signal to child
298
- let signal;
299
- const signals = [ 'SIGINT', 'SIGQUIT', 'SIGTERM' ] as NodeJS.Signals[];
300
- signals.forEach(event => {
301
- process.once(event, () => {
302
- debug('Kill child %s with %s', child.pid, signal);
303
- child.kill(event);
304
- });
305
- });
306
- }
307
- }
308
-
309
- protected async checkStatus() {
310
- let count = 0;
311
- let hasError = false;
312
- let isSuccess = true;
313
- const timeout = this.flags.timeout / 1000;
314
- const stderrFile = this.flags.stderr!;
315
- while (!this.isReady) {
316
- try {
317
- const stats = await stat(stderrFile);
318
- if (stats && stats.size > 0) {
319
- hasError = true;
320
- break;
321
- }
322
- } catch (_) {
323
- // nothing
324
- }
325
-
326
- if (count >= timeout) {
327
- this.logToStderr('Start failed, %ds timeout', timeout);
328
- isSuccess = false;
329
- break;
330
- }
331
-
332
- await scheduler.wait(1000);
333
- this.log('Wait Start: %d...', ++count);
334
- }
335
-
336
- if (hasError) {
337
- try {
338
- const args = [ '-n', '100', stderrFile ];
339
- this.logToStderr('tail %s', args.join(' '));
340
- const { stdout: headStdout } = await execFile('head', args);
341
- const { stdout: tailStdout } = await execFile('tail', args);
342
- this.logToStderr('Got error when startup: ');
343
- this.logToStderr(headStdout);
344
- this.logToStderr('...');
345
- this.logToStderr(tailStdout);
346
- } catch (err) {
347
- this.logToStderr('ignore tail error: %s', err);
348
- }
349
- isSuccess = this.flags['ignore-stderr'];
350
- this.logToStderr('Start got error, see %o', stderrFile);
351
- this.logToStderr('Or use `--ignore-stderr` to ignore stderr at startup.');
352
- }
353
-
354
- if (!isSuccess) {
355
- this.#child.kill('SIGTERM');
356
- await scheduler.wait(1000);
357
- this.exit(1);
358
- }
359
- }
360
- }
361
-
362
- function stringify(obj: Record<string, any>, ignore: string[]) {
363
- const result: Record<string, any> = {};
364
- Object.keys(obj).forEach(key => {
365
- if (!ignore.includes(key)) {
366
- result[key] = obj[key];
367
- }
368
- });
369
- return JSON.stringify(result);
370
- }
371
-
372
- async function getRotateLog(logFile: string) {
373
- await mkdir(path.dirname(logFile), { recursive: true });
374
-
375
- if (await exists(logFile)) {
376
- // format style: .20150602.193100
377
- const [ YYYY, MM, DD, HH, mm, ss ] = getDateStringParts();
378
- const timestamp = `.${YYYY}${MM}${DD}.${HH}${mm}${ss}`;
379
- // Note: rename last log to next start time, not when last log file created
380
- await rename(logFile, logFile + timestamp);
381
- }
382
-
383
- return (await open(logFile, 'a')).fd;
384
- }
@@ -1,105 +0,0 @@
1
- import { debuglog, format } from 'node:util';
2
- import { scheduler } from 'node:timers/promises';
3
- import { Args, Flags } from '@oclif/core';
4
- import { BaseCommand } from '../baseCommand.js';
5
- import { isWindows, findNodeProcess, NodeProcess, kill } from '../helper.js';
6
-
7
- const debug = debuglog('@eggjs/scripts/commands/stop');
8
-
9
- const osRelated = {
10
- titleTemplate: isWindows ? '\\"title\\":\\"%s\\"' : '"title":"%s"',
11
- // node_modules/@eggjs/cluster/dist/commonjs/app_worker.js
12
- // node_modules/@eggjs/cluster/dist/esm/app_worker.js
13
- appWorkerPath: /@eggjs[\/\\]cluster[\/\\]dist[\/\\](commonjs|esm)[\/\\]app_worker\.js/i,
14
- // node_modules/@eggjs/cluster/dist/commonjs/agent_worker.js
15
- // node_modules/@eggjs/cluster/dist/esm/agent_worker.js
16
- agentWorkerPath: /@eggjs[\/\\]cluster[\/\\]dist[\/\\](commonjs|esm)[\/\\]agent_worker\.js/i,
17
- };
18
-
19
- export default class Stop<T extends typeof Stop> extends BaseCommand<T> {
20
- static override description = 'Stop server';
21
-
22
- static override examples = [
23
- '<%= config.bin %> <%= command.id %>',
24
- ];
25
-
26
- static override args = {
27
- baseDir: Args.string({
28
- description: 'directory of application',
29
- required: false,
30
- }),
31
- };
32
-
33
- static override flags = {
34
- title: Flags.string({
35
- description: 'process title description, use for kill grep',
36
- }),
37
- timeout: Flags.integer({
38
- description: 'the maximum timeout(ms) when app stop',
39
- default: 5000,
40
- }),
41
- };
42
-
43
- public async run(): Promise<void> {
44
- const { flags } = this;
45
-
46
- this.log(`stopping egg application${flags.title ? ` with --title=${flags.title}` : ''}`);
47
-
48
- // node ~/eggjs/scripts/scripts/start-cluster.cjs {"title":"egg-server","workers":4,"port":7001,"baseDir":"~/eggjs/test/showcase","framework":"~/eggjs/test/showcase/node_modules/egg"}
49
- let processList = await this.findNodeProcesses(item => {
50
- const cmd = item.cmd;
51
- const matched = flags.title ?
52
- cmd.includes('start-cluster') && cmd.includes(format(osRelated.titleTemplate, flags.title)) :
53
- cmd.includes('start-cluster');
54
- if (matched) {
55
- debug('find master process: %o', item);
56
- }
57
- return matched;
58
- });
59
- let pids = processList.map(x => x.pid);
60
-
61
- if (pids.length) {
62
- this.log('got master pid %j, list:', pids);
63
- this.log('');
64
- for (const item of processList) {
65
- this.log('- %s: %o', item.pid, item.cmd);
66
- }
67
- this.log('');
68
- this.killProcesses(pids);
69
- // wait for 5s to confirm whether any worker process did not kill by master
70
- await scheduler.wait(flags.timeout);
71
- } else {
72
- this.logToStderr('can\'t detect any running egg process');
73
- }
74
-
75
- // node --debug-port=5856 /Users/tz/Workspaces/eggjs/test/showcase/node_modules/_egg-cluster@1.8.0@egg-cluster/lib/agent_worker.js {"framework":"/Users/tz/Workspaces/eggjs/test/showcase/node_modules/egg","baseDir":"/Users/tz/Workspaces/eggjs/test/showcase","port":7001,"workers":2,"plugins":null,"https":false,"key":"","cert":"","title":"egg-server","clusterPort":52406}
76
- // node /Users/tz/Workspaces/eggjs/test/showcase/node_modules/_egg-cluster@1.8.0@egg-cluster/lib/app_worker.js {"framework":"/Users/tz/Workspaces/eggjs/test/showcase/node_modules/egg","baseDir":"/Users/tz/Workspaces/eggjs/test/showcase","port":7001,"workers":2,"plugins":null,"https":false,"key":"","cert":"","title":"egg-server","clusterPort":52406}
77
- // ~/bin/node --no-deprecation --trace-warnings ~/eggjs/examples/helloworld/node_modules/@eggjs/cluster/dist/commonjs/agent_worker.js {"baseDir":"~/eggjs/examples/helloworld","startMode":"process","framework":"~/eggjs/examples/helloworld/node_modules/egg","title":"egg-server-helloworld","workers":10,"clusterPort":58977}
78
- processList = await this.findNodeProcesses(item => {
79
- const cmd = item.cmd;
80
- const matched = flags.title ?
81
- (osRelated.appWorkerPath.test(cmd) || osRelated.agentWorkerPath.test(cmd)) && cmd.includes(format(osRelated.titleTemplate, flags.title)) :
82
- (osRelated.appWorkerPath.test(cmd) || osRelated.agentWorkerPath.test(cmd));
83
- if (matched) {
84
- debug('find app/agent worker process: %o', item);
85
- }
86
- return matched;
87
- });
88
- pids = processList.map(x => x.pid);
89
-
90
- if (pids.length) {
91
- this.log('got worker/agent pids %j that is not killed by master', pids);
92
- this.killProcesses(pids);
93
- }
94
-
95
- this.log('stopped');
96
- }
97
-
98
- protected async findNodeProcesses(filter: (item: NodeProcess) => boolean): Promise<NodeProcess[]> {
99
- return findNodeProcess(filter);
100
- }
101
-
102
- protected killProcesses(pids: number[], signal: NodeJS.Signals = 'SIGTERM') {
103
- kill(pids, signal);
104
- }
105
- }
package/src/helper.ts DELETED
@@ -1,62 +0,0 @@
1
- import { runScript } from 'runscript';
2
- import path from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
-
5
- export const isWindows = process.platform === 'win32';
6
-
7
- const REGEX = isWindows ? /^(.*)\s+(\d+)\s*$/ : /^\s*(\d+)\s+(.*)/;
8
-
9
- export interface NodeProcess {
10
- pid: number;
11
- cmd: string;
12
- }
13
-
14
- export type FilterFunction = (item: NodeProcess) => boolean;
15
-
16
- export async function findNodeProcess(filterFn?: FilterFunction): Promise<NodeProcess[]> {
17
- const command = isWindows ?
18
- 'wmic Path win32_process Where "Name = \'node.exe\'" Get CommandLine,ProcessId' :
19
- // command, cmd are alias of args, not POSIX standard, so we use args
20
- 'ps -wweo "pid,args"';
21
- const stdio = await runScript(command, { stdio: 'pipe' });
22
- const processList = stdio.stdout!.toString().split('\n')
23
- .reduce<NodeProcess[]>((arr, line) => {
24
- if (!!line && !line.includes('/bin/sh') && line.includes('node')) {
25
- const m = line.match(REGEX);
26
- if (m) {
27
- const item: NodeProcess = isWindows ? { pid: parseInt(m[2]), cmd: m[1] } : { pid: parseInt(m[1]), cmd: m[2] };
28
- if (filterFn?.(item)) {
29
- arr.push(item);
30
- }
31
- }
32
- }
33
- return arr;
34
- }, []);
35
- return processList;
36
- }
37
-
38
- export function kill(pids: number[], signal?: string | number) {
39
- pids.forEach(pid => {
40
- try {
41
- process.kill(pid, signal);
42
- } catch (err: any) {
43
- if (err.code !== 'ESRCH') {
44
- throw err;
45
- }
46
- }
47
- });
48
- }
49
-
50
- export function getSourceDirname() {
51
- if (typeof __dirname === 'string') {
52
- return __dirname;
53
- }
54
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
55
- // @ts-ignore
56
- const __filename = fileURLToPath(import.meta.url);
57
- return path.dirname(__filename);
58
- }
59
-
60
- export function getSourceFilename(filename: string) {
61
- return path.join(getSourceDirname(), filename);
62
- }
package/src/index.ts DELETED
@@ -1,8 +0,0 @@
1
- import Start from './commands/start.js';
2
-
3
- // exports.StopCommand = require('./lib/cmd/stop');
4
-
5
- export * from './baseCommand.js';
6
- export {
7
- Start, Start as StartCommand,
8
- };