@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.
- package/README.md +2 -6
- package/bin/dev.js +0 -0
- package/dist/baseCommand.d.ts +28 -0
- package/dist/baseCommand.js +47 -0
- package/dist/commands/start.d.ts +39 -0
- package/dist/commands/start.js +277 -0
- package/dist/commands/stop.d.ts +21 -0
- package/dist/commands/stop.js +68 -0
- package/dist/helper.d.ts +11 -0
- package/dist/helper.js +35 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -0
- package/{src/types.ts → dist/types.d.ts} +4 -1
- package/dist/types.js +1 -0
- package/package.json +66 -86
- package/scripts/start-cluster.cjs +11 -4
- package/scripts/start-cluster.mjs +6 -2
- package/dist/commonjs/baseCommand.d.ts +0 -25
- package/dist/commonjs/baseCommand.js +0 -62
- package/dist/commonjs/commands/start.d.ts +0 -34
- package/dist/commonjs/commands/start.js +0 -350
- package/dist/commonjs/commands/stop.d.ts +0 -16
- package/dist/commonjs/commands/stop.js +0 -95
- package/dist/commonjs/helper.d.ts +0 -10
- package/dist/commonjs/helper.js +0 -61
- package/dist/commonjs/index.d.ts +0 -3
- package/dist/commonjs/index.js +0 -26
- package/dist/commonjs/package.json +0 -3
- package/dist/commonjs/types.d.ts +0 -9
- package/dist/commonjs/types.js +0 -3
- package/dist/esm/baseCommand.d.ts +0 -25
- package/dist/esm/baseCommand.js +0 -55
- package/dist/esm/commands/start.d.ts +0 -34
- package/dist/esm/commands/start.js +0 -344
- package/dist/esm/commands/stop.d.ts +0 -16
- package/dist/esm/commands/stop.js +0 -92
- package/dist/esm/helper.d.ts +0 -10
- package/dist/esm/helper.js +0 -51
- package/dist/esm/index.d.ts +0 -3
- package/dist/esm/index.js +0 -5
- package/dist/esm/package.json +0 -3
- package/dist/esm/types.d.ts +0 -9
- package/dist/esm/types.js +0 -2
- package/dist/package.json +0 -4
- package/src/baseCommand.ts +0 -68
- package/src/commands/start.ts +0 -384
- package/src/commands/stop.ts +0 -105
- package/src/helper.ts +0 -62
- package/src/index.ts +0 -8
package/README.md
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
# @eggjs/scripts
|
|
2
2
|
|
|
3
3
|
[![NPM version][npm-image]][npm-url]
|
|
4
|
-
[](https://github.com/eggjs/scripts/actions/workflows/nodejs.yml)
|
|
5
|
-
[![Test coverage][codecov-image]][codecov-url]
|
|
6
4
|
[![npm download][download-image]][download-url]
|
|
7
5
|
[](https://nodejs.org/en/download/)
|
|
8
6
|
[](https://makeapullrequest.com)
|
|
9
7
|
|
|
10
8
|
[npm-image]: https://img.shields.io/npm/v/@eggjs/scripts.svg?style=flat-square
|
|
11
9
|
[npm-url]: https://npmjs.org/package/@eggjs/scripts
|
|
12
|
-
[codecov-image]: https://codecov.io/github/eggjs/scripts/coverage.svg?branch=master
|
|
13
|
-
[codecov-url]: https://codecov.io/github/eggjs/scripts?branch=master
|
|
14
10
|
[download-image]: https://img.shields.io/npm/dm/@eggjs/scripts.svg?style=flat-square
|
|
15
11
|
[download-url]: https://npmjs.org/package/@eggjs/scripts
|
|
16
12
|
|
|
@@ -63,7 +59,7 @@ $ eggctl start [options] [baseDir]
|
|
|
63
59
|
- **Options**
|
|
64
60
|
- `port` - listening port, default to `process.env.PORT`, if unset, egg will use `7001` as default.
|
|
65
61
|
- `title` - process title description, use for kill grep, default to `egg-server-${APP_NAME}`.
|
|
66
|
-
- `workers` - numbers of app workers, default to `process.env.EGG_WORKERS`, if unset, egg will use `os.cpus().length`
|
|
62
|
+
- `workers` - numbers of app workers, default to `process.env.EGG_WORKERS`, if unset, egg will use `os.cpus().length` as default.
|
|
67
63
|
- `daemon` - whether run at background daemon mode, don't use it if in docker mode.
|
|
68
64
|
- `framework` - specify framework that can be absolute path or npm package, default to auto detect.
|
|
69
65
|
- `env` - server env, default to `process.env.EGG_SERVER_ENV`, recommended to keep empty then use framwork default env.
|
|
@@ -118,6 +114,6 @@ Please open an issue [here](https://github.com/eggjs/egg/issues?q=is%3Aissue+is%
|
|
|
118
114
|
|
|
119
115
|
## Contributors
|
|
120
116
|
|
|
121
|
-
[](https://github.com/eggjs/egg/graphs/contributors)
|
|
122
118
|
|
|
123
119
|
Made with [contributors-img](https://contrib.rocks).
|
package/bin/dev.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { PackageEgg } from "./types.js";
|
|
2
|
+
import { Command, Interfaces } from "@oclif/core";
|
|
3
|
+
|
|
4
|
+
//#region src/baseCommand.d.ts
|
|
5
|
+
type Flags$1<T extends typeof Command> = Interfaces.InferredFlags<(typeof BaseCommand)['baseFlags'] & T['flags']>;
|
|
6
|
+
type Args$1<T extends typeof Command> = Interfaces.InferredArgs<T['args']>;
|
|
7
|
+
declare abstract class BaseCommand<T extends typeof Command> extends Command {
|
|
8
|
+
static enableJsonFlag: boolean;
|
|
9
|
+
static baseFlags: {};
|
|
10
|
+
protected flags: Flags$1<T>;
|
|
11
|
+
protected args: Args$1<T>;
|
|
12
|
+
protected env: {
|
|
13
|
+
[key: string]: string | undefined;
|
|
14
|
+
TZ?: string;
|
|
15
|
+
};
|
|
16
|
+
protected pkg: Record<string, any>;
|
|
17
|
+
protected isESM: boolean;
|
|
18
|
+
protected pkgEgg: PackageEgg;
|
|
19
|
+
protected globalExecArgv: string[];
|
|
20
|
+
init(): Promise<void>;
|
|
21
|
+
protected initBaseInfo(baseDir: string): Promise<void>;
|
|
22
|
+
protected catch(err: Error & {
|
|
23
|
+
exitCode?: number;
|
|
24
|
+
}): Promise<any>;
|
|
25
|
+
protected finally(_: Error | undefined): Promise<any>;
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { BaseCommand };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { debuglog } from "node:util";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { Command, Interfaces } from "@oclif/core";
|
|
4
|
+
import { readJSON } from "utility";
|
|
5
|
+
|
|
6
|
+
//#region src/baseCommand.ts
|
|
7
|
+
const debug = debuglog("egg/scripts/baseCommand");
|
|
8
|
+
var BaseCommand = class extends Command {
|
|
9
|
+
static enableJsonFlag = false;
|
|
10
|
+
static baseFlags = {};
|
|
11
|
+
flags;
|
|
12
|
+
args;
|
|
13
|
+
env = { ...process.env };
|
|
14
|
+
pkg;
|
|
15
|
+
isESM;
|
|
16
|
+
pkgEgg;
|
|
17
|
+
globalExecArgv = [];
|
|
18
|
+
async init() {
|
|
19
|
+
await super.init();
|
|
20
|
+
debug("[init] raw args: %o, NODE_ENV: %o", this.argv, this.env.NODE_ENV);
|
|
21
|
+
const { args, flags } = await this.parse({
|
|
22
|
+
flags: this.ctor.flags,
|
|
23
|
+
baseFlags: super.ctor.baseFlags,
|
|
24
|
+
enableJsonFlag: this.ctor.enableJsonFlag,
|
|
25
|
+
args: this.ctor.args,
|
|
26
|
+
strict: this.ctor.strict
|
|
27
|
+
});
|
|
28
|
+
this.flags = flags;
|
|
29
|
+
this.args = args;
|
|
30
|
+
}
|
|
31
|
+
async initBaseInfo(baseDir) {
|
|
32
|
+
const pkg = await readJSON(path.join(baseDir, "package.json"));
|
|
33
|
+
this.pkg = pkg;
|
|
34
|
+
this.pkgEgg = pkg.egg ?? {};
|
|
35
|
+
this.isESM = pkg.type === "module";
|
|
36
|
+
debug("[initBaseInfo] baseDir: %o, pkgEgg: %o, isESM: %o", baseDir, this.pkgEgg, this.isESM);
|
|
37
|
+
}
|
|
38
|
+
async catch(err) {
|
|
39
|
+
return super.catch(err);
|
|
40
|
+
}
|
|
41
|
+
async finally(_) {
|
|
42
|
+
return super.finally(_);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
//#endregion
|
|
47
|
+
export { BaseCommand };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { BaseCommand } from "../baseCommand.js";
|
|
2
|
+
import * as _oclif_core_interfaces4 from "@oclif/core/interfaces";
|
|
3
|
+
|
|
4
|
+
//#region src/commands/start.d.ts
|
|
5
|
+
interface FrameworkOptions {
|
|
6
|
+
baseDir: string;
|
|
7
|
+
framework?: string;
|
|
8
|
+
}
|
|
9
|
+
declare class Start<T extends typeof Start> extends BaseCommand<T> {
|
|
10
|
+
#private;
|
|
11
|
+
static description: string;
|
|
12
|
+
static examples: string[];
|
|
13
|
+
static args: {
|
|
14
|
+
baseDir: _oclif_core_interfaces4.Arg<string | undefined, Record<string, unknown>>;
|
|
15
|
+
};
|
|
16
|
+
static flags: {
|
|
17
|
+
title: _oclif_core_interfaces4.OptionFlag<string | undefined, _oclif_core_interfaces4.CustomOptions>;
|
|
18
|
+
framework: _oclif_core_interfaces4.OptionFlag<string | undefined, _oclif_core_interfaces4.CustomOptions>;
|
|
19
|
+
port: _oclif_core_interfaces4.OptionFlag<number | undefined, _oclif_core_interfaces4.CustomOptions>;
|
|
20
|
+
workers: _oclif_core_interfaces4.OptionFlag<number | undefined, _oclif_core_interfaces4.CustomOptions>;
|
|
21
|
+
env: _oclif_core_interfaces4.OptionFlag<string, _oclif_core_interfaces4.CustomOptions>;
|
|
22
|
+
daemon: _oclif_core_interfaces4.BooleanFlag<boolean>;
|
|
23
|
+
stdout: _oclif_core_interfaces4.OptionFlag<string | undefined, _oclif_core_interfaces4.CustomOptions>;
|
|
24
|
+
stderr: _oclif_core_interfaces4.OptionFlag<string | undefined, _oclif_core_interfaces4.CustomOptions>;
|
|
25
|
+
timeout: _oclif_core_interfaces4.OptionFlag<number, _oclif_core_interfaces4.CustomOptions>;
|
|
26
|
+
'ignore-stderr': _oclif_core_interfaces4.BooleanFlag<boolean>;
|
|
27
|
+
node: _oclif_core_interfaces4.OptionFlag<string, _oclif_core_interfaces4.CustomOptions>;
|
|
28
|
+
require: _oclif_core_interfaces4.OptionFlag<string[] | undefined, _oclif_core_interfaces4.CustomOptions>;
|
|
29
|
+
sourcemap: _oclif_core_interfaces4.BooleanFlag<boolean>;
|
|
30
|
+
};
|
|
31
|
+
isReady: boolean;
|
|
32
|
+
protected getFrameworkPath(options: FrameworkOptions): Promise<string>;
|
|
33
|
+
protected getFrameworkName(frameworkPath: string): Promise<string>;
|
|
34
|
+
protected getServerBin(): Promise<string>;
|
|
35
|
+
run(): Promise<void>;
|
|
36
|
+
protected checkStatus(): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { FrameworkOptions, Start as default };
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { BaseCommand } from "../baseCommand.js";
|
|
2
|
+
import { debuglog, promisify } from "node:util";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { Args, Flags } from "@oclif/core";
|
|
5
|
+
import { exists, getDateStringParts, readJSON } from "utility";
|
|
6
|
+
import { scheduler } from "node:timers/promises";
|
|
7
|
+
import { execFile, spawn } from "node:child_process";
|
|
8
|
+
import { mkdir, open, rename, stat } from "node:fs/promises";
|
|
9
|
+
import { homedir } from "node-homedir";
|
|
10
|
+
import { getFrameworkPath, importResolve } from "@eggjs/utils";
|
|
11
|
+
|
|
12
|
+
//#region src/commands/start.ts
|
|
13
|
+
const debug = debuglog("egg/scripts/commands/start");
|
|
14
|
+
const execFile$1 = promisify(execFile);
|
|
15
|
+
var Start = class extends BaseCommand {
|
|
16
|
+
static description = "Start server at prod mode";
|
|
17
|
+
static examples = ["<%= config.bin %> <%= command.id %>"];
|
|
18
|
+
static args = { baseDir: Args.string({
|
|
19
|
+
description: "directory of application",
|
|
20
|
+
required: false
|
|
21
|
+
}) };
|
|
22
|
+
static flags = {
|
|
23
|
+
title: Flags.string({ description: "process title description, use for kill grep, default to `egg-server-${APP_NAME}`" }),
|
|
24
|
+
framework: Flags.string({ description: "specify framework that can be absolute path or npm package" }),
|
|
25
|
+
port: Flags.integer({
|
|
26
|
+
description: "listening port, default to `process.env.PORT`",
|
|
27
|
+
char: "p"
|
|
28
|
+
}),
|
|
29
|
+
workers: Flags.integer({
|
|
30
|
+
char: "c",
|
|
31
|
+
aliases: ["cluster"],
|
|
32
|
+
description: "numbers of app workers, default to `process.env.EGG_WORKERS` or `os.cpus().length`"
|
|
33
|
+
}),
|
|
34
|
+
env: Flags.string({
|
|
35
|
+
description: "server env, default to `process.env.EGG_SERVER_ENV`",
|
|
36
|
+
default: process.env.EGG_SERVER_ENV
|
|
37
|
+
}),
|
|
38
|
+
daemon: Flags.boolean({ description: "whether run at background daemon mode" }),
|
|
39
|
+
stdout: Flags.string({ description: "customize stdout file" }),
|
|
40
|
+
stderr: Flags.string({ description: "customize stderr file" }),
|
|
41
|
+
timeout: Flags.integer({
|
|
42
|
+
description: "the maximum timeout(ms) when app starts",
|
|
43
|
+
default: 300 * 1e3
|
|
44
|
+
}),
|
|
45
|
+
"ignore-stderr": Flags.boolean({ description: "whether ignore stderr when app starts" }),
|
|
46
|
+
node: Flags.string({
|
|
47
|
+
description: "customize node command path",
|
|
48
|
+
default: "node"
|
|
49
|
+
}),
|
|
50
|
+
require: Flags.string({
|
|
51
|
+
summary: "require the given module",
|
|
52
|
+
char: "r",
|
|
53
|
+
multiple: true
|
|
54
|
+
}),
|
|
55
|
+
sourcemap: Flags.boolean({
|
|
56
|
+
summary: "whether enable sourcemap support, will load `source-map-support` etc",
|
|
57
|
+
aliases: ["ts", "typescript"]
|
|
58
|
+
})
|
|
59
|
+
};
|
|
60
|
+
isReady = false;
|
|
61
|
+
#child;
|
|
62
|
+
async getFrameworkPath(options) {
|
|
63
|
+
return getFrameworkPath(options);
|
|
64
|
+
}
|
|
65
|
+
async getFrameworkName(frameworkPath) {
|
|
66
|
+
const pkgPath = path.join(frameworkPath, "package.json");
|
|
67
|
+
let name = "egg";
|
|
68
|
+
try {
|
|
69
|
+
const pkg = await readJSON(pkgPath);
|
|
70
|
+
if (pkg.name) name = pkg.name;
|
|
71
|
+
} catch {}
|
|
72
|
+
return name;
|
|
73
|
+
}
|
|
74
|
+
async getServerBin() {
|
|
75
|
+
const serverBinName = this.isESM ? "start-cluster.mjs" : "start-cluster.cjs";
|
|
76
|
+
return path.join(import.meta.dirname, "../../scripts", serverBinName);
|
|
77
|
+
}
|
|
78
|
+
async run() {
|
|
79
|
+
const { args, flags } = this;
|
|
80
|
+
const HOME = homedir();
|
|
81
|
+
const logDir = path.join(HOME, "logs");
|
|
82
|
+
const cwd = process.cwd();
|
|
83
|
+
let baseDir = args.baseDir || cwd;
|
|
84
|
+
if (!path.isAbsolute(baseDir)) baseDir = path.join(cwd, baseDir);
|
|
85
|
+
await this.initBaseInfo(baseDir);
|
|
86
|
+
flags.framework = await this.getFrameworkPath({
|
|
87
|
+
framework: flags.framework,
|
|
88
|
+
baseDir
|
|
89
|
+
});
|
|
90
|
+
const frameworkName = await this.getFrameworkName(flags.framework);
|
|
91
|
+
flags.title = flags.title || `egg-server-${this.pkg.name}`;
|
|
92
|
+
flags.stdout = flags.stdout || path.join(logDir, "master-stdout.log");
|
|
93
|
+
flags.stderr = flags.stderr || path.join(logDir, "master-stderr.log");
|
|
94
|
+
if (flags.workers === void 0 && process.env.EGG_WORKERS) flags.workers = Number(process.env.EGG_WORKERS);
|
|
95
|
+
this.env.HOME = HOME;
|
|
96
|
+
this.env.NODE_ENV = "production";
|
|
97
|
+
this.env.PATH = this.env.Path = [
|
|
98
|
+
path.join(baseDir, "node_modules/.bin"),
|
|
99
|
+
path.join(baseDir, ".node/bin"),
|
|
100
|
+
this.env.PATH || this.env.Path
|
|
101
|
+
].filter((x) => !!x).join(path.delimiter);
|
|
102
|
+
this.env.ENABLE_NODE_LOG = "YES";
|
|
103
|
+
this.env.NODE_LOG_DIR = this.env.NODE_LOG_DIR || path.join(logDir, "alinode");
|
|
104
|
+
await mkdir(this.env.NODE_LOG_DIR, { recursive: true });
|
|
105
|
+
if (flags.env) this.env.EGG_SERVER_ENV = flags.env;
|
|
106
|
+
const execArgv = ["--no-deprecation", "--trace-warnings"];
|
|
107
|
+
if (this.pkgEgg.revert) {
|
|
108
|
+
const reverts = Array.isArray(this.pkgEgg.revert) ? this.pkgEgg.revert : [this.pkgEgg.revert];
|
|
109
|
+
for (const revert of reverts) execArgv.push(`--security-revert=${revert}`);
|
|
110
|
+
}
|
|
111
|
+
const scriptsConfig = this.pkg.eggScriptsConfig;
|
|
112
|
+
if (scriptsConfig?.require) {
|
|
113
|
+
scriptsConfig.require = Array.isArray(scriptsConfig.require) ? scriptsConfig.require : [scriptsConfig.require];
|
|
114
|
+
flags.require = [...scriptsConfig.require, ...flags.require ?? []];
|
|
115
|
+
}
|
|
116
|
+
if (scriptsConfig) for (const key in scriptsConfig) {
|
|
117
|
+
const v = scriptsConfig[key];
|
|
118
|
+
if (key.startsWith("node-options--")) {
|
|
119
|
+
const newKey = key.replace("node-options--", "");
|
|
120
|
+
if (v === true) execArgv.push(`--${newKey}`);
|
|
121
|
+
else execArgv.push(`--${newKey}=${v}`);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (Reflect.get(flags, key) === void 0) Reflect.set(flags, key, v);
|
|
125
|
+
}
|
|
126
|
+
if (this.pkgEgg.typescript && flags.sourcemap === void 0) flags.sourcemap = true;
|
|
127
|
+
if (flags.sourcemap) {
|
|
128
|
+
const sourceMapSupportPkgPath = importResolve("source-map-support/package.json", { paths: [import.meta.dirname] });
|
|
129
|
+
const sourceMapSupport = path.join(path.dirname(sourceMapSupportPkgPath), "register.js");
|
|
130
|
+
if (this.isESM) execArgv.push("--import", sourceMapSupport);
|
|
131
|
+
else execArgv.push("--require", sourceMapSupport);
|
|
132
|
+
}
|
|
133
|
+
if (flags.port === void 0 && process.env.PORT) flags.port = parseInt(process.env.PORT);
|
|
134
|
+
debug("flags: %o, framework: %o, baseDir: %o, execArgv: %o", flags, frameworkName, baseDir, execArgv);
|
|
135
|
+
const command = flags.node;
|
|
136
|
+
const options = {
|
|
137
|
+
env: this.env,
|
|
138
|
+
stdio: "inherit",
|
|
139
|
+
detached: false,
|
|
140
|
+
cwd: baseDir
|
|
141
|
+
};
|
|
142
|
+
this.log("Starting %s application at %s", frameworkName, baseDir);
|
|
143
|
+
const ignoreKeys = [
|
|
144
|
+
"env",
|
|
145
|
+
"daemon",
|
|
146
|
+
"stdout",
|
|
147
|
+
"stderr",
|
|
148
|
+
"timeout",
|
|
149
|
+
"ignore-stderr",
|
|
150
|
+
"node"
|
|
151
|
+
];
|
|
152
|
+
const clusterOptions = stringify({
|
|
153
|
+
...flags,
|
|
154
|
+
baseDir
|
|
155
|
+
}, ignoreKeys);
|
|
156
|
+
const serverBin = await this.getServerBin();
|
|
157
|
+
const eggArgs = [
|
|
158
|
+
...execArgv,
|
|
159
|
+
serverBin,
|
|
160
|
+
clusterOptions,
|
|
161
|
+
`--title=${flags.title}`
|
|
162
|
+
];
|
|
163
|
+
const spawnScript = `${command} ${eggArgs.map((a) => `'${a}'`).join(" ")}`;
|
|
164
|
+
this.log("Spawn %o", spawnScript);
|
|
165
|
+
if (flags.daemon) {
|
|
166
|
+
this.log(`Save log file to ${logDir}`);
|
|
167
|
+
const [stdout, stderr] = await Promise.all([getRotateLog(flags.stdout), getRotateLog(flags.stderr)]);
|
|
168
|
+
options.stdio = [
|
|
169
|
+
"ignore",
|
|
170
|
+
stdout,
|
|
171
|
+
stderr,
|
|
172
|
+
"ipc"
|
|
173
|
+
];
|
|
174
|
+
options.detached = true;
|
|
175
|
+
const child = this.#child = spawn(command, eggArgs, options);
|
|
176
|
+
this.isReady = false;
|
|
177
|
+
child.on("message", (msg) => {
|
|
178
|
+
if (msg && msg.action === "egg-ready") {
|
|
179
|
+
this.isReady = true;
|
|
180
|
+
this.log("%s started on %s", frameworkName, msg.data.address);
|
|
181
|
+
child.unref();
|
|
182
|
+
child.disconnect();
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
await this.checkStatus();
|
|
186
|
+
} else {
|
|
187
|
+
options.stdio = [
|
|
188
|
+
"inherit",
|
|
189
|
+
"inherit",
|
|
190
|
+
"inherit",
|
|
191
|
+
"ipc"
|
|
192
|
+
];
|
|
193
|
+
const child = this.#child = spawn(command, eggArgs, options);
|
|
194
|
+
child.once("exit", (code) => {
|
|
195
|
+
if (!code) return;
|
|
196
|
+
this.exit(code);
|
|
197
|
+
});
|
|
198
|
+
[
|
|
199
|
+
"SIGINT",
|
|
200
|
+
"SIGQUIT",
|
|
201
|
+
"SIGTERM"
|
|
202
|
+
].forEach((event) => {
|
|
203
|
+
process.once(event, () => {
|
|
204
|
+
debug("Kill child %s with %s", child.pid, event);
|
|
205
|
+
child.kill(event);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async checkStatus() {
|
|
211
|
+
let count = 0;
|
|
212
|
+
let hasError = false;
|
|
213
|
+
let isSuccess = true;
|
|
214
|
+
const timeout = this.flags.timeout / 1e3;
|
|
215
|
+
const stderrFile = this.flags.stderr;
|
|
216
|
+
while (!this.isReady) {
|
|
217
|
+
try {
|
|
218
|
+
const stats = await stat(stderrFile);
|
|
219
|
+
if (stats && stats.size > 0) {
|
|
220
|
+
hasError = true;
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
} catch {}
|
|
224
|
+
if (count >= timeout) {
|
|
225
|
+
this.logToStderr("Start failed, %ds timeout", timeout);
|
|
226
|
+
isSuccess = false;
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
await scheduler.wait(1e3);
|
|
230
|
+
this.log("Wait Start: %d...", ++count);
|
|
231
|
+
}
|
|
232
|
+
if (hasError) {
|
|
233
|
+
try {
|
|
234
|
+
const args = [
|
|
235
|
+
"-n",
|
|
236
|
+
"100",
|
|
237
|
+
stderrFile
|
|
238
|
+
];
|
|
239
|
+
this.logToStderr("tail %s", args.join(" "));
|
|
240
|
+
const { stdout: headStdout } = await execFile$1("head", args);
|
|
241
|
+
const { stdout: tailStdout } = await execFile$1("tail", args);
|
|
242
|
+
this.logToStderr("Got error when startup: ");
|
|
243
|
+
this.logToStderr(headStdout);
|
|
244
|
+
this.logToStderr("...");
|
|
245
|
+
this.logToStderr(tailStdout);
|
|
246
|
+
} catch (err) {
|
|
247
|
+
this.logToStderr("ignore tail error: %s", err);
|
|
248
|
+
}
|
|
249
|
+
isSuccess = this.flags["ignore-stderr"];
|
|
250
|
+
this.logToStderr("Start got error, see %o", stderrFile);
|
|
251
|
+
this.logToStderr("Or use `--ignore-stderr` to ignore stderr at startup.");
|
|
252
|
+
}
|
|
253
|
+
if (!isSuccess) {
|
|
254
|
+
this.#child.kill("SIGTERM");
|
|
255
|
+
await scheduler.wait(1e3);
|
|
256
|
+
this.exit(1);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
function stringify(obj, ignore) {
|
|
261
|
+
const result = {};
|
|
262
|
+
Object.keys(obj).forEach((key) => {
|
|
263
|
+
if (!ignore.includes(key)) result[key] = obj[key];
|
|
264
|
+
});
|
|
265
|
+
return JSON.stringify(result);
|
|
266
|
+
}
|
|
267
|
+
async function getRotateLog(logFile) {
|
|
268
|
+
await mkdir(path.dirname(logFile), { recursive: true });
|
|
269
|
+
if (await exists(logFile)) {
|
|
270
|
+
const [YYYY, MM, DD, HH, mm, ss] = getDateStringParts();
|
|
271
|
+
await rename(logFile, logFile + `.${YYYY}${MM}${DD}.${HH}${mm}${ss}`);
|
|
272
|
+
}
|
|
273
|
+
return (await open(logFile, "a")).fd;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
//#endregion
|
|
277
|
+
export { Start as default };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { BaseCommand } from "../baseCommand.js";
|
|
2
|
+
import { NodeProcess } from "../helper.js";
|
|
3
|
+
import * as _oclif_core_interfaces0 from "@oclif/core/interfaces";
|
|
4
|
+
|
|
5
|
+
//#region src/commands/stop.d.ts
|
|
6
|
+
declare class Stop<T extends typeof Stop> extends BaseCommand<T> {
|
|
7
|
+
static description: string;
|
|
8
|
+
static examples: string[];
|
|
9
|
+
static args: {
|
|
10
|
+
baseDir: _oclif_core_interfaces0.Arg<string | undefined, Record<string, unknown>>;
|
|
11
|
+
};
|
|
12
|
+
static flags: {
|
|
13
|
+
title: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
14
|
+
timeout: _oclif_core_interfaces0.OptionFlag<number, _oclif_core_interfaces0.CustomOptions>;
|
|
15
|
+
};
|
|
16
|
+
run(): Promise<void>;
|
|
17
|
+
protected findNodeProcesses(filter: (item: NodeProcess) => boolean): Promise<NodeProcess[]>;
|
|
18
|
+
protected killProcesses(pids: number[], signal?: NodeJS.Signals): void;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { Stop as default };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { BaseCommand } from "../baseCommand.js";
|
|
2
|
+
import { findNodeProcess, isWindows, kill } from "../helper.js";
|
|
3
|
+
import { debuglog, format } from "node:util";
|
|
4
|
+
import { Args, Flags } from "@oclif/core";
|
|
5
|
+
import { scheduler } from "node:timers/promises";
|
|
6
|
+
|
|
7
|
+
//#region src/commands/stop.ts
|
|
8
|
+
const debug = debuglog("egg/scripts/commands/stop");
|
|
9
|
+
const osRelated = {
|
|
10
|
+
titleTemplate: isWindows ? "\\\"title\\\":\\\"%s\\\"" : "\"title\":\"%s\"",
|
|
11
|
+
appWorkerPath: /@eggjs[/\\]cluster[/\\]dist[/\\](commonjs|esm)[/\\]app_worker\.js/i,
|
|
12
|
+
agentWorkerPath: /@eggjs[/\\]cluster[/\\]dist[/\\](commonjs|esm)[/\\]agent_worker\.js/i
|
|
13
|
+
};
|
|
14
|
+
var Stop = class extends BaseCommand {
|
|
15
|
+
static description = "Stop server";
|
|
16
|
+
static examples = ["<%= config.bin %> <%= command.id %>"];
|
|
17
|
+
static args = { baseDir: Args.string({
|
|
18
|
+
description: "directory of application",
|
|
19
|
+
required: false
|
|
20
|
+
}) };
|
|
21
|
+
static flags = {
|
|
22
|
+
title: Flags.string({ description: "process title description, use for kill grep" }),
|
|
23
|
+
timeout: Flags.integer({
|
|
24
|
+
description: "the maximum timeout(ms) when app stop",
|
|
25
|
+
default: 5e3
|
|
26
|
+
})
|
|
27
|
+
};
|
|
28
|
+
async run() {
|
|
29
|
+
const { flags } = this;
|
|
30
|
+
this.log(`stopping egg application${flags.title ? ` with --title=${flags.title}` : ""}`);
|
|
31
|
+
let processList = await this.findNodeProcesses((item) => {
|
|
32
|
+
const cmd = item.cmd;
|
|
33
|
+
const matched = flags.title ? cmd.includes("start-cluster") && cmd.includes(format(osRelated.titleTemplate, flags.title)) : cmd.includes("start-cluster");
|
|
34
|
+
if (matched) debug("find master process: %o", item);
|
|
35
|
+
return matched;
|
|
36
|
+
});
|
|
37
|
+
let pids = processList.map((x) => x.pid);
|
|
38
|
+
if (pids.length) {
|
|
39
|
+
this.log("got master pid %j, list:", pids);
|
|
40
|
+
this.log("");
|
|
41
|
+
for (const item of processList) this.log("- %s: %o", item.pid, item.cmd);
|
|
42
|
+
this.log("");
|
|
43
|
+
this.killProcesses(pids);
|
|
44
|
+
await scheduler.wait(flags.timeout);
|
|
45
|
+
} else this.logToStderr("can't detect any running egg process");
|
|
46
|
+
processList = await this.findNodeProcesses((item) => {
|
|
47
|
+
const cmd = item.cmd;
|
|
48
|
+
const matched = flags.title ? (osRelated.appWorkerPath.test(cmd) || osRelated.agentWorkerPath.test(cmd)) && cmd.includes(format(osRelated.titleTemplate, flags.title)) : osRelated.appWorkerPath.test(cmd) || osRelated.agentWorkerPath.test(cmd);
|
|
49
|
+
if (matched) debug("find app/agent worker process: %o", item);
|
|
50
|
+
return matched;
|
|
51
|
+
});
|
|
52
|
+
pids = processList.map((x) => x.pid);
|
|
53
|
+
if (pids.length) {
|
|
54
|
+
this.log("got worker/agent pids %j that is not killed by master", pids);
|
|
55
|
+
this.killProcesses(pids);
|
|
56
|
+
}
|
|
57
|
+
this.log("stopped");
|
|
58
|
+
}
|
|
59
|
+
async findNodeProcesses(filter) {
|
|
60
|
+
return findNodeProcess(filter);
|
|
61
|
+
}
|
|
62
|
+
killProcesses(pids, signal = "SIGTERM") {
|
|
63
|
+
kill(pids, signal);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
//#endregion
|
|
68
|
+
export { Stop as default };
|
package/dist/helper.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/helper.d.ts
|
|
2
|
+
declare const isWindows: boolean;
|
|
3
|
+
interface NodeProcess {
|
|
4
|
+
pid: number;
|
|
5
|
+
cmd: string;
|
|
6
|
+
}
|
|
7
|
+
type FilterFunction = (item: NodeProcess) => boolean;
|
|
8
|
+
declare function findNodeProcess(filterFn?: FilterFunction): Promise<NodeProcess[]>;
|
|
9
|
+
declare function kill(pids: number[], signal?: string | number): void;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { FilterFunction, NodeProcess, findNodeProcess, isWindows, kill };
|
package/dist/helper.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { runScript } from "runscript";
|
|
2
|
+
|
|
3
|
+
//#region src/helper.ts
|
|
4
|
+
const isWindows = process.platform === "win32";
|
|
5
|
+
const REGEX = isWindows ? /^(.*)\s+(\d+)\s*$/ : /^\s*(\d+)\s+(.*)/;
|
|
6
|
+
async function findNodeProcess(filterFn) {
|
|
7
|
+
return (await runScript(isWindows ? "wmic Path win32_process Where \"Name = 'node.exe'\" Get CommandLine,ProcessId" : "ps -wweo \"pid,args\"", { stdio: "pipe" })).stdout.toString().split("\n").reduce((arr, line) => {
|
|
8
|
+
if (!!line && !line.includes("/bin/sh") && line.includes("node")) {
|
|
9
|
+
const m = line.match(REGEX);
|
|
10
|
+
if (m) {
|
|
11
|
+
const item = isWindows ? {
|
|
12
|
+
pid: parseInt(m[2]),
|
|
13
|
+
cmd: m[1]
|
|
14
|
+
} : {
|
|
15
|
+
pid: parseInt(m[1]),
|
|
16
|
+
cmd: m[2]
|
|
17
|
+
};
|
|
18
|
+
if (filterFn?.(item)) arr.push(item);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return arr;
|
|
22
|
+
}, []);
|
|
23
|
+
}
|
|
24
|
+
function kill(pids, signal) {
|
|
25
|
+
pids.forEach((pid) => {
|
|
26
|
+
try {
|
|
27
|
+
process.kill(pid, signal);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
if (err.code !== "ESRCH") throw err;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
export { findNodeProcess, isWindows, kill };
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
interface PackageEgg {
|
|
2
3
|
framework?: boolean;
|
|
3
4
|
typescript?: boolean;
|
|
4
5
|
tscompiler?: string;
|
|
@@ -7,3 +8,5 @@ export interface PackageEgg {
|
|
|
7
8
|
require?: string | string[];
|
|
8
9
|
import?: string | string[];
|
|
9
10
|
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { PackageEgg };
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|