Skip to content

Commit b9fe64d

Browse files
committed
f
1 parent a238b50 commit b9fe64d

File tree

7 files changed

+235
-336
lines changed

7 files changed

+235
-336
lines changed

src/command.js

Lines changed: 0 additions & 116 deletions
This file was deleted.

src/commands/stop.js

Lines changed: 0 additions & 75 deletions
This file was deleted.

src/commands/stop.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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', pids);
63+
this.killProcesses(pids);
64+
// wait for 5s to confirm whether any worker process did not kill by master
65+
await scheduler.wait(flags.timeout);
66+
} else {
67+
this.logToStderr('can\'t detect any running egg process');
68+
}
69+
70+
// node --debug-port=5856 /Users/tz/Workspaces/eggjs/test/showcase/node_modules/[email protected]@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}
71+
// node /Users/tz/Workspaces/eggjs/test/showcase/node_modules/[email protected]@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}
72+
// ~/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}
73+
processList = await this.findNodeProcesses(item => {
74+
const cmd = item.cmd;
75+
const matched = flags.title ?
76+
(osRelated.appWorkerPath.test(cmd) || osRelated.agentWorkerPath.test(cmd)) && cmd.includes(format(osRelated.titleTemplate, flags.title)) :
77+
(osRelated.appWorkerPath.test(cmd) || osRelated.agentWorkerPath.test(cmd));
78+
if (matched) {
79+
debug('find app/agent worker process: %o', item);
80+
}
81+
return matched;
82+
});
83+
pids = processList.map(x => x.pid);
84+
85+
if (pids.length) {
86+
this.log('got worker/agent pids %j that is not killed by master', pids);
87+
this.killProcesses(pids);
88+
}
89+
90+
this.log('stopped');
91+
}
92+
93+
protected async findNodeProcesses(filter: (item: NodeProcess) => boolean): Promise<NodeProcess[]> {
94+
return findNodeProcess(filter);
95+
}
96+
97+
protected killProcesses(pids: number[], signal: NodeJS.Signals = 'SIGTERM') {
98+
kill(pids, signal);
99+
}
100+
}
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
'use strict';
2-
31
module.exports = app => {
4-
app.get('/', function* () {
2+
app.get('/', async function() {
53
this.body = `hi, ${app.config.framework || 'egg'}`;
64
});
75

8-
app.get('/env', function* () {
6+
app.get('/env', async function() {
97
this.body = app.config.env + ', ' + app.config.pre;
108
});
119

12-
app.get('/path', function* () {
10+
app.get('/path', async function() {
1311
this.body = process.env.PATH;
1412
});
1513
};

test/start.test.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,19 @@ import { fileURLToPath } from 'node:url';
33
import { strict as assert } from 'node:assert';
44
import fs from 'node:fs/promises';
55
import { scheduler } from 'node:timers/promises';
6-
import { ChildProcess } from 'node:child_process';
76
import { createServer } from 'node:http';
87
import { once } from 'node:events';
9-
import coffee, { Coffee as _Coffee } from 'coffee';
8+
import coffee from 'coffee';
109
import { request } from 'urllib';
1110
import { mm, restore } from 'mm';
1211
import { exists } from 'utility';
13-
import { cleanup, replaceWeakRefMessage } from './utils.js';
12+
import { cleanup, replaceWeakRefMessage, Coffee } from './utils.js';
1413
import { isWindows, getSourceFilename } from '../src/helper.js';
1514

1615
const version = parseInt(process.version.split('.')[0].substring(1));
1716
const __filename = fileURLToPath(import.meta.url);
1817
const __dirname = path.dirname(__filename);
1918

20-
type Coffee = _Coffee & { proc: ChildProcess, stderr: string, stdout: string, code?: number };
21-
2219
describe('test/start.test.ts', () => {
2320
const eggBin = getSourceFilename('../bin/run.js');
2421
const fixturePath = path.join(__dirname, 'fixtures/example');

0 commit comments

Comments
 (0)