dsh-wechat-remote 1.7.6 → 1.7.9
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 +10 -6
- package/assets/plugin.tgz +0 -0
- package/assets/release.json +11 -51
- package/bin/dsh-discovery.mjs +14 -4
- package/bin/native-recovery.mjs +44 -0
- package/bin/release-selection.mjs +2 -2
- package/bin/setup.mjs +100 -24
- package/lib/install-control.js +53 -29
- package/lib/install-profile.js +114 -159
- package/lib/install-runtime.js +17 -0
- package/lib/native-recovery.js +7478 -0
- package/lib/update-download.js +162 -36
- package/lib/update-policy.js +9 -1
- package/lib/update-worker.js +255 -186
- package/package.json +1 -1
package/lib/install-profile.js
CHANGED
|
@@ -3,127 +3,71 @@
|
|
|
3
3
|
// src/install-profile.ts
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
// src/secure-file.ts
|
|
10
|
-
import { execFileSync } from "node:child_process";
|
|
11
|
-
import {
|
|
12
|
-
chmodSync,
|
|
13
|
-
closeSync,
|
|
14
|
-
existsSync,
|
|
15
|
-
fsyncSync,
|
|
16
|
-
mkdirSync,
|
|
17
|
-
openSync,
|
|
18
|
-
readFileSync,
|
|
19
|
-
renameSync,
|
|
20
|
-
rmSync,
|
|
21
|
-
writeFileSync
|
|
22
|
-
} from "node:fs";
|
|
23
|
-
import { dirname } from "node:path";
|
|
24
|
-
import { userInfo } from "node:os";
|
|
25
|
-
import { randomBytes } from "node:crypto";
|
|
26
|
-
function tightenPrivateFile(file) {
|
|
27
|
-
try {
|
|
28
|
-
chmodSync(file, 384);
|
|
29
|
-
} catch {
|
|
30
|
-
}
|
|
31
|
-
if (process.platform !== "win32") return;
|
|
32
|
-
try {
|
|
33
|
-
execFileSync("icacls", [file, "/inheritance:r", "/grant:r", `${userInfo().username}:F`], {
|
|
34
|
-
timeout: 5e3,
|
|
35
|
-
windowsHide: true,
|
|
36
|
-
stdio: "ignore"
|
|
37
|
-
});
|
|
38
|
-
} catch {
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
function writePrivateJsonAtomic(file, value) {
|
|
42
|
-
const parent = dirname(file);
|
|
43
|
-
mkdirSync(parent, { recursive: true, mode: 448 });
|
|
44
|
-
const temporary = `${file}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
45
|
-
try {
|
|
46
|
-
writeFileSync(temporary, `${JSON.stringify(value, null, 2)}
|
|
47
|
-
`, {
|
|
48
|
-
encoding: "utf8",
|
|
49
|
-
mode: 384,
|
|
50
|
-
flag: "wx"
|
|
51
|
-
});
|
|
52
|
-
const fd = openSync(temporary, "r+");
|
|
53
|
-
try {
|
|
54
|
-
try {
|
|
55
|
-
fsyncSync(fd);
|
|
56
|
-
} catch {
|
|
57
|
-
}
|
|
58
|
-
} finally {
|
|
59
|
-
closeSync(fd);
|
|
60
|
-
}
|
|
61
|
-
tightenPrivateFile(temporary);
|
|
62
|
-
renameSync(temporary, file);
|
|
63
|
-
tightenPrivateFile(file);
|
|
64
|
-
} finally {
|
|
65
|
-
if (existsSync(temporary)) {
|
|
66
|
-
try {
|
|
67
|
-
rmSync(temporary, { force: true });
|
|
68
|
-
} catch {
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// src/install-runtime.ts
|
|
75
|
-
var INSTALL_PNPM_VERSION = "11.22.0";
|
|
76
|
-
|
|
77
|
-
// src/install-profile.ts
|
|
6
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
7
|
+
import { spawn, execFile } from "node:child_process";
|
|
78
8
|
var PLUGIN_PACKAGE = "@harness-remote/dsh-wechat-remote";
|
|
79
9
|
function safeProfileName(value) {
|
|
80
10
|
return /^[A-Za-z0-9_-]{1,80}$/.test(value);
|
|
81
11
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
12
|
+
function backupProfile(profile, backup) {
|
|
13
|
+
if (fs.existsSync(backup)) throw new Error("\u672C\u6B21\u5B89\u88C5\u5907\u4EFD\u5DF2\u5B58\u5728\uFF0C\u672A\u8986\u76D6\u3002");
|
|
14
|
+
fs.cpSync(profile, backup, {
|
|
15
|
+
recursive: true,
|
|
16
|
+
dereference: false,
|
|
17
|
+
verbatimSymlinks: true,
|
|
18
|
+
mode: fs.constants.COPYFILE_FICLONE,
|
|
19
|
+
filter(source, target) {
|
|
20
|
+
if (!fs.lstatSync(source).isSymbolicLink()) return true;
|
|
21
|
+
const type = process.platform === "win32" ? fs.statSync(source, { throwIfNoEntry: false })?.isDirectory() ? "junction" : "file" : void 0;
|
|
22
|
+
const link = fs.readlinkSync(source);
|
|
23
|
+
fs.symlinkSync(type === "junction" ? path.resolve(path.dirname(source), link) : link, target, type);
|
|
24
|
+
return false;
|
|
93
25
|
}
|
|
94
|
-
};
|
|
95
|
-
walk(root);
|
|
26
|
+
});
|
|
96
27
|
}
|
|
97
28
|
function installToolPath(directory, runtime) {
|
|
98
29
|
const bin = path.join(directory, "tool-bin");
|
|
99
30
|
fs.mkdirSync(bin, { mode: 448 });
|
|
100
|
-
const runner = path.join(bin, "pnpm-run.cjs");
|
|
101
|
-
fs.writeFileSync(runner, `require('node:child_process').spawnSync(${JSON.stringify(runtime.executable)},[${JSON.stringify(runtime.cli)},...process.argv.slice(2)],{stdio:'inherit',shell:false,windowsHide:true}).status===0?process.exit(0):process.exit(1)
|
|
102
|
-
`, { mode: 384 });
|
|
103
31
|
if (process.platform === "win32") {
|
|
104
|
-
if (/["\r\n]/.test(runtime.executable)) throw new Error("
|
|
105
|
-
fs.writeFileSync(path.join(bin, "pnpm.cmd"),
|
|
106
|
-
"${runtime.executable.replace(/%/g, "%%")}" "%~dp0pnpm-run.cjs" %*\r
|
|
107
|
-
`, { mode: 448 });
|
|
32
|
+
if (/["\r\n]/.test(runtime.executable + runtime.cli)) throw new Error("\u5B89\u88C5\u5DE5\u5177\u8DEF\u5F84\u5305\u542B\u65E0\u6548\u5B57\u7B26\u3002");
|
|
33
|
+
fs.writeFileSync(path.join(bin, "pnpm.cmd"), '@echo off\r\nsetlocal DisableDelayedExpansion\r\n"%HARNESS_INSTALL_NODE%" "%HARNESS_INSTALL_PNPM%" %*\r\n', { mode: 448 });
|
|
108
34
|
} else {
|
|
109
|
-
|
|
110
|
-
fs.writeFileSync(path.join(bin, "pnpm"), `#!/bin/sh
|
|
111
|
-
exec ${quote(runtime.executable)} ${quote(runner)} "$@"
|
|
112
|
-
`, { mode: 448 });
|
|
35
|
+
fs.writeFileSync(path.join(bin, "pnpm"), '#!/bin/sh\nexec "$HARNESS_INSTALL_NODE" "$HARNESS_INSTALL_PNPM" "$@"\n', { mode: 448 });
|
|
113
36
|
}
|
|
114
37
|
return bin;
|
|
115
38
|
}
|
|
116
|
-
|
|
117
|
-
|
|
39
|
+
var NativeInstallError = class extends Error {
|
|
40
|
+
constructor(message, mayStillBeRunning = false) {
|
|
41
|
+
super(message);
|
|
42
|
+
this.mayStillBeRunning = mayStillBeRunning;
|
|
43
|
+
this.name = "NativeInstallError";
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
function runNativePlugin(cli, profile, home, toolPath, runtime, logFile, archiveName, timeoutMs = 6e5, operation = "add") {
|
|
47
|
+
if (!safeProfileName(profile) || !/^harness-remote-[\w.+-]+\.tgz$/.test(archiveName)) throw new Error("\u65E0\u6548\u7684\u5B89\u88C5\u76EE\u6807\u3002");
|
|
118
48
|
return new Promise((resolve, reject) => {
|
|
119
49
|
const log = fs.openSync(logFile, "a", 384);
|
|
50
|
+
const env = { ...process.env };
|
|
51
|
+
const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path");
|
|
52
|
+
const inheritedPath = pathKey ? env[pathKey] : "";
|
|
53
|
+
if (process.platform === "win32") {
|
|
54
|
+
for (const key of Object.keys(env)) if (key.toLowerCase() === "path") delete env[key];
|
|
55
|
+
}
|
|
56
|
+
Object.assign(env, {
|
|
57
|
+
DSH_HOME: home,
|
|
58
|
+
PATH: toolPath + path.delimiter + (inheritedPath || ""),
|
|
59
|
+
HARNESS_INSTALL_NODE: runtime.executable,
|
|
60
|
+
HARNESS_INSTALL_PNPM: runtime.cli,
|
|
61
|
+
CI: "true",
|
|
62
|
+
COREPACK_ENABLE_AUTO_PIN: "0",
|
|
63
|
+
npm_config_manage_package_manager_versions: "false"
|
|
64
|
+
});
|
|
120
65
|
const child = spawn(runtime.executable, [
|
|
121
66
|
cli,
|
|
122
67
|
"plugin",
|
|
123
68
|
"--profile",
|
|
124
69
|
profile,
|
|
125
|
-
"add",
|
|
126
|
-
"file:harness-remote-update.tgz",
|
|
70
|
+
...operation === "install" ? ["install"] : ["add", `file:${archiveName}`],
|
|
127
71
|
"--ignore-scripts",
|
|
128
72
|
"--config.frozen-lockfile=false",
|
|
129
73
|
"--prefer-offline",
|
|
@@ -133,78 +77,89 @@ function runNativePlugin(cli, profile, home, toolPath, runtime, logFile) {
|
|
|
133
77
|
cwd: home,
|
|
134
78
|
shell: false,
|
|
135
79
|
windowsHide: true,
|
|
80
|
+
detached: process.platform !== "win32",
|
|
136
81
|
stdio: ["ignore", log, log],
|
|
137
|
-
env
|
|
138
|
-
...process.env,
|
|
139
|
-
DSH_HOME: home,
|
|
140
|
-
PATH: toolPath + path.delimiter + (process.env.PATH || ""),
|
|
141
|
-
CI: "true",
|
|
142
|
-
COREPACK_ENABLE_AUTO_PIN: "0",
|
|
143
|
-
npm_config_manage_package_manager_versions: "false"
|
|
144
|
-
}
|
|
82
|
+
env
|
|
145
83
|
});
|
|
146
84
|
fs.closeSync(log);
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
85
|
+
let finished = false, terminating = false, closed = false, terminationTimer;
|
|
86
|
+
const fail = (message, uncertain = false) => finish(new NativeInstallError(`${message} \u5B89\u88C5\u65E5\u5FD7\uFF1A${logFile}`, uncertain));
|
|
87
|
+
const finish = (error) => {
|
|
88
|
+
if (finished) return;
|
|
89
|
+
finished = true;
|
|
152
90
|
clearTimeout(timer);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
91
|
+
clearTimeout(terminationTimer);
|
|
92
|
+
error ? reject(error) : resolve();
|
|
93
|
+
};
|
|
94
|
+
const timer = setTimeout(() => {
|
|
95
|
+
if (closed) return;
|
|
96
|
+
terminating = true;
|
|
97
|
+
terminationTimer = setTimeout(() => fail("\u5B89\u88C5\u8D85\u65F6\uFF0C\u5C1A\u4E0D\u80FD\u786E\u8BA4\u5B89\u88C5\u8FDB\u7A0B\u5DF2\u9000\u51FA\uFF1B\u672A\u81EA\u52A8\u56DE\u9000\uFF0C\u8BF7\u52FF\u91CD\u590D\u5B89\u88C5\u3002", true), 15e3);
|
|
98
|
+
if (process.platform === "win32") {
|
|
99
|
+
if (!child.pid) return fail("\u5B89\u88C5\u7A0B\u5E8F\u672A\u542F\u52A8\u3002");
|
|
100
|
+
execFile(
|
|
101
|
+
path.join(process.env.SystemRoot || "C:\\Windows", "System32", "taskkill.exe"),
|
|
102
|
+
["/PID", String(child.pid), "/T", "/F"],
|
|
103
|
+
{ windowsHide: true, timeout: 1e4 },
|
|
104
|
+
(error) => {
|
|
105
|
+
if (error) fail("\u5B89\u88C5\u8D85\u65F6\uFF0C\u65E0\u6CD5\u786E\u8BA4\u5B89\u88C5\u8FDB\u7A0B\u6811\u5DF2\u9000\u51FA\uFF1B\u672A\u81EA\u52A8\u56DE\u9000\uFF0C\u8BF7\u52FF\u91CD\u590D\u5B89\u88C5\u3002", true);
|
|
106
|
+
else if (closed) fail("\u4E0B\u8F7D\u6216\u5B89\u88C5\u8D85\u65F6\uFF0C\u5B89\u88C5\u8FDB\u7A0B\u5DF2\u505C\u6B62\u3002");
|
|
107
|
+
else child.once("close", () => fail("\u4E0B\u8F7D\u6216\u5B89\u88C5\u8D85\u65F6\uFF0C\u5B89\u88C5\u8FDB\u7A0B\u5DF2\u505C\u6B62\u3002"));
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
} else {
|
|
111
|
+
try {
|
|
112
|
+
if (child.pid) process.kill(-child.pid, "SIGKILL");
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (error.code !== "ESRCH") return fail("\u65E0\u6CD5\u505C\u6B62\u8D85\u65F6\u7684\u5B89\u88C5\u8FDB\u7A0B\uFF1B\u672A\u81EA\u52A8\u56DE\u9000\u3002", true);
|
|
115
|
+
}
|
|
116
|
+
if (closed) fail("\u4E0B\u8F7D\u6216\u5B89\u88C5\u8D85\u65F6\uFF0C\u5B89\u88C5\u8FDB\u7A0B\u5DF2\u505C\u6B62\u3002");
|
|
117
|
+
else child.once("close", () => fail("\u4E0B\u8F7D\u6216\u5B89\u88C5\u8D85\u65F6\uFF0C\u5B89\u88C5\u8FDB\u7A0B\u5DF2\u505C\u6B62\u3002"));
|
|
118
|
+
}
|
|
119
|
+
}, timeoutMs);
|
|
120
|
+
child.once("error", (error) => fail(`\u65E0\u6CD5\u542F\u52A8 DSH \u539F\u751F\u5B89\u88C5\u7A0B\u5E8F\uFF08${error.code || error.name}\uFF09\u3002`));
|
|
121
|
+
child.once("close", (code, signal) => {
|
|
122
|
+
closed = true;
|
|
123
|
+
if (terminating) return;
|
|
124
|
+
if (code === 0) finish();
|
|
125
|
+
else fail(`DSH \u539F\u751F\u5B89\u88C5\u672A\u5B8C\u6210\uFF08${signal ? `\u4FE1\u53F7 ${signal}` : `\u9000\u51FA\u7801 ${code}`}\uFF09\u3002`);
|
|
158
126
|
});
|
|
159
127
|
});
|
|
160
128
|
}
|
|
161
|
-
async function
|
|
162
|
-
const scope = path.basename(job.profile);
|
|
163
|
-
if (!safeProfileName(scope) ||
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
filter: (p) => !["node_modules", ".harness-remote-update.lock"].includes(path.basename(p))
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
let before = null;
|
|
175
|
-
const filename = path.join(staged, "package.json");
|
|
176
|
-
if (fs.existsSync(filename)) {
|
|
177
|
-
before = JSON.parse(fs.readFileSync(filename, "utf8"));
|
|
178
|
-
for (const [name, spec] of Object.entries(before.dependencies || {})) {
|
|
179
|
-
if (name !== PLUGIN_PACKAGE && !/^[~^]?\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(String(spec))) throw new Error("\u5176\u4ED6\u63D2\u4EF6\u4F7F\u7528\u4E86\u672C\u5730\u94FE\u63A5\u6216\u7279\u6B8A\u6765\u6E90\uFF0C\u672A\u4FEE\u6539\u5F53\u524D\u5B89\u88C5\u3002");
|
|
180
|
-
}
|
|
181
|
-
for (const e of fs.readdirSync(staged, { withFileTypes: true })) if (e.isSymbolicLink()) throw new Error("profile \u914D\u7F6E\u5305\u542B\u94FE\u63A5\uFF0C\u672A\u4FEE\u6539\u5F53\u524D\u5B89\u88C5\u3002");
|
|
182
|
-
if (before.packageManager && !/^pnpm@\d+\.\d+\.\d+(?:\+.*)?$/.test(before.packageManager)) throw new Error("\u5F53\u524D profile \u4F7F\u7528\u4E86\u5176\u4ED6\u5305\u7BA1\u7406\u5668\uFF0C\u672A\u4FEE\u6539\u5B89\u88C5\u3002");
|
|
183
|
-
writePrivateJsonAtomic(filename, { ...before, dependencies: {
|
|
184
|
-
...before.dependencies,
|
|
185
|
-
[PLUGIN_PACKAGE]: "file:harness-remote-update.tgz"
|
|
186
|
-
}, packageManager: `pnpm@${INSTALL_PNPM_VERSION}` });
|
|
187
|
-
}
|
|
188
|
-
fs.copyFileSync(path.join(job.directory, "release.tgz"), path.join(staged, "harness-remote-update.tgz"));
|
|
129
|
+
async function installProfile(job) {
|
|
130
|
+
const scope = path.basename(job.profile), home = path.dirname(path.dirname(job.profile));
|
|
131
|
+
if (!safeProfileName(scope) || path.dirname(job.profile) !== path.join(home, "profiles") || !/^[\w.+-]{1,80}$/.test(job.targetVersion)) throw new Error("\u5B89\u88C5\u76EE\u6807\u4E0D\u660E\u786E\u3002");
|
|
132
|
+
fs.mkdirSync(job.profile, { recursive: true, mode: 448 });
|
|
133
|
+
const archive = path.join(job.directory, "release.tgz");
|
|
134
|
+
const digest = createHash("sha256").update(fs.readFileSync(archive)).digest("hex");
|
|
135
|
+
const archiveBase = `harness-remote-${job.targetVersion}-${digest}`;
|
|
136
|
+
const suffix = fs.existsSync(path.join(job.profile, `${archiveBase}.tgz`)) ? `-${randomBytes(8).toString("hex")}` : "";
|
|
137
|
+
const archiveName = `${archiveBase}${suffix}.tgz`;
|
|
138
|
+
fs.copyFileSync(archive, path.join(job.profile, archiveName), fs.constants.COPYFILE_EXCL);
|
|
189
139
|
const tools = installToolPath(job.directory, job.runtime);
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
const
|
|
193
|
-
|
|
140
|
+
const logFile = path.join(job.directory, "install.log");
|
|
141
|
+
const deadline = Date.now() + 6e5;
|
|
142
|
+
const run = (operation = "add") => runNativePlugin(job.cli, scope, home, tools, job.runtime, logFile, archiveName, Math.max(1, deadline - Date.now()), operation);
|
|
143
|
+
try {
|
|
144
|
+
await run();
|
|
145
|
+
} catch (error) {
|
|
146
|
+
const layoutError = /^\s*(?:\[ERR_PNPM_|ERR_PNPM_)(?:UNEXPECTED_VIRTUAL_STORE|UNEXPECTED_STORE|MODULES_BREAKING_CHANGE|VIRTUAL_STORE_DIR_MAX_LENGTH_DIFF|PUBLIC_HOIST_PATTERN_DIFF|HOIST_PATTERN_DIFF)(?:\]|\s)/m;
|
|
147
|
+
if (!(error instanceof NativeInstallError) || error.mayStillBeRunning || !layoutError.test(fs.readFileSync(logFile, "utf8"))) throw error;
|
|
148
|
+
fs.appendFileSync(logFile, "\nRestoring native pnpm layout with dsh plugin install.\n");
|
|
149
|
+
await run("install");
|
|
150
|
+
await run();
|
|
151
|
+
}
|
|
152
|
+
const installed = path.join(job.profile, "node_modules", PLUGIN_PACKAGE);
|
|
194
153
|
if (JSON.parse(fs.readFileSync(path.join(installed, "package.json"), "utf8")).version !== job.targetVersion) throw new Error("\u5B89\u88C5\u540E\u63D2\u4EF6\u7248\u672C\u4E0D\u5339\u914D\u3002");
|
|
195
|
-
const after = JSON.parse(fs.readFileSync(
|
|
154
|
+
const after = JSON.parse(fs.readFileSync(path.join(job.profile, "package.json"), "utf8"));
|
|
196
155
|
if (!after.dsh?.profile?.bundles?.includes(PLUGIN_PACKAGE)) throw new Error("DSH \u5C1A\u672A\u5C06\u63D2\u4EF6\u6CE8\u518C\u4E3A\u539F\u751F profile \u5C42\u3002");
|
|
197
|
-
for (const name of Object.keys(before?.dependencies || {})) if (name !== PLUGIN_PACKAGE) {
|
|
198
|
-
if (hash(path.join(job.profile, "node_modules", name, "package.json")) !== hash(path.join(staged, "node_modules", name, "package.json"))) throw new Error("\u5B89\u88C5\u8BD5\u56FE\u6539\u53D8\u5176\u4ED6\u63D2\u4EF6\uFF0C\u539F\u5B89\u88C5\u4FDD\u6301\u4E0D\u53D8\u3002");
|
|
199
|
-
}
|
|
200
|
-
assertRelocatableProfile(staged);
|
|
201
|
-
return staged;
|
|
202
156
|
}
|
|
203
157
|
export {
|
|
158
|
+
NativeInstallError,
|
|
204
159
|
PLUGIN_PACKAGE,
|
|
205
|
-
|
|
160
|
+
backupProfile,
|
|
161
|
+
installProfile,
|
|
206
162
|
installToolPath,
|
|
207
163
|
runNativePlugin,
|
|
208
|
-
safeProfileName
|
|
209
|
-
stageProfile
|
|
164
|
+
safeProfileName
|
|
210
165
|
};
|
package/lib/install-runtime.js
CHANGED
|
@@ -6,6 +6,22 @@ import path from "node:path";
|
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import { spawn } from "node:child_process";
|
|
8
8
|
var INSTALL_PNPM_VERSION = "11.22.0";
|
|
9
|
+
async function pinInstallRuntime(runtime, directory) {
|
|
10
|
+
let root = path.dirname(runtime.cli);
|
|
11
|
+
while (!fs.existsSync(path.join(root, "package.json"))) {
|
|
12
|
+
const parent = path.dirname(root);
|
|
13
|
+
if (parent === root) throw new Error("\u65E0\u6CD5\u5B9A\u4F4D\u5B89\u88C5\u5DE5\u5177\u5305\u3002");
|
|
14
|
+
root = parent;
|
|
15
|
+
}
|
|
16
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
|
|
17
|
+
if (manifest.name !== "pnpm" || manifest.version !== runtime.version) throw new Error("\u5B89\u88C5\u5DE5\u5177\u7248\u672C\u4E0D\u4E00\u81F4\u3002");
|
|
18
|
+
const target = path.join(directory, "install-runtime");
|
|
19
|
+
if (fs.existsSync(target)) throw new Error("\u672C\u6B21\u5B89\u88C5\u5DE5\u5177\u76EE\u5F55\u5DF2\u5B58\u5728\u3002");
|
|
20
|
+
fs.cpSync(root, target, { recursive: true, dereference: false, verbatimSymlinks: true, mode: fs.constants.COPYFILE_FICLONE });
|
|
21
|
+
const pinned = { ...runtime, cli: path.join(target, path.relative(root, runtime.cli)) };
|
|
22
|
+
await verifyInstallRuntime(pinned);
|
|
23
|
+
return pinned;
|
|
24
|
+
}
|
|
9
25
|
function resolveInstallRuntime(owner, executable = process.execPath, nodeVersion = process.versions.node) {
|
|
10
26
|
const [major, minor] = nodeVersion.split(".").map(Number);
|
|
11
27
|
if (!(major > 22 || major === 22 && minor >= 13)) throw new Error("\u5F53\u524D Node.js \u7248\u672C\u4E0D\u6EE1\u8DB3\u5B89\u88C5\u8981\u6C42\uFF0822.13 \u6216\u66F4\u9AD8\uFF09\uFF0C\u672A\u4FEE\u6539\u63D2\u4EF6\u3002");
|
|
@@ -59,6 +75,7 @@ function verifyInstallRuntime(runtime) {
|
|
|
59
75
|
}
|
|
60
76
|
export {
|
|
61
77
|
INSTALL_PNPM_VERSION,
|
|
78
|
+
pinInstallRuntime,
|
|
62
79
|
resolveInstallRuntime,
|
|
63
80
|
verifyInstallRuntime
|
|
64
81
|
};
|