@rfabric/cli 0.0.6 → 0.1.0
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/bin/rfabric.js +11 -9
- package/package.json +1 -1
- package/scripts/postinstall.js +106 -79
package/bin/rfabric.js
CHANGED
|
@@ -8,19 +8,21 @@ const binaryName = process.platform === "win32" ? "rfabric.exe" : "rfabric";
|
|
|
8
8
|
const binaryPath = path.join(__dirname, "..", "vendor", binaryName);
|
|
9
9
|
|
|
10
10
|
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
stdio: "inherit",
|
|
12
|
+
windowsHide: true,
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
child.on("exit", (code, signal) => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
if (signal) {
|
|
17
|
+
process.kill(process.pid, signal);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
process.exit(code ?? 1);
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
child.on("error", (error) => {
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
process.stderr.write(
|
|
25
|
+
`[rfabric] failed to launch ${binaryPath}: ${error.message}\n`,
|
|
26
|
+
);
|
|
27
|
+
process.exit(1);
|
|
26
28
|
});
|
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -18,7 +18,7 @@ const platform = platformMap[process.platform];
|
|
|
18
18
|
const arch = archMap[process.arch];
|
|
19
19
|
|
|
20
20
|
if (!platform || !arch) {
|
|
21
|
-
|
|
21
|
+
fail(`unsupported platform: ${process.platform}/${process.arch}`);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
const isWindows = platform === "windows";
|
|
@@ -33,102 +33,129 @@ const binaryPath = path.join(vendorDir, binaryName);
|
|
|
33
33
|
main().catch((error) => fail(error.message || String(error)));
|
|
34
34
|
|
|
35
35
|
async function main() {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
36
|
+
if (fs.existsSync(binaryPath)) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
fs.mkdirSync(vendorDir, { recursive: true });
|
|
41
|
+
|
|
42
|
+
log(`downloading ${archiveName}`);
|
|
43
|
+
const archiveBuffer = await downloadToBuffer(`${baseUrl}/${archiveName}`);
|
|
44
|
+
|
|
45
|
+
log("verifying checksum");
|
|
46
|
+
const checksumsText = (
|
|
47
|
+
await downloadToBuffer(`${baseUrl}/checksums.txt`)
|
|
48
|
+
).toString("utf8");
|
|
49
|
+
const expectedChecksum = parseChecksum(checksumsText, archiveName);
|
|
50
|
+
const actualChecksum = crypto
|
|
51
|
+
.createHash("sha256")
|
|
52
|
+
.update(archiveBuffer)
|
|
53
|
+
.digest("hex");
|
|
54
|
+
if (expectedChecksum !== actualChecksum) {
|
|
55
|
+
fail(
|
|
56
|
+
`checksum mismatch (expected ${expectedChecksum}, got ${actualChecksum})`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
log("extracting");
|
|
61
|
+
if (isWindows) {
|
|
62
|
+
extractZip(archiveBuffer, vendorDir);
|
|
63
|
+
} else {
|
|
64
|
+
await extractTarGz(archiveBuffer, vendorDir);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!fs.existsSync(binaryPath)) {
|
|
68
|
+
fail(`binary ${binaryName} not found in archive`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!isWindows) {
|
|
72
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
log(`installed to ${binaryPath}`);
|
|
69
76
|
}
|
|
70
77
|
|
|
71
78
|
function downloadToBuffer(url, redirects = 0) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
if (redirects > 5) {
|
|
81
|
+
return reject(new Error(`too many redirects fetching ${url}`));
|
|
82
|
+
}
|
|
83
|
+
https
|
|
84
|
+
.get(
|
|
85
|
+
url,
|
|
86
|
+
{ headers: { "User-Agent": "rfabric-npm-postinstall" } },
|
|
87
|
+
(response) => {
|
|
88
|
+
if (
|
|
89
|
+
response.statusCode === 301 ||
|
|
90
|
+
response.statusCode === 302 ||
|
|
91
|
+
response.statusCode === 307
|
|
92
|
+
) {
|
|
93
|
+
return resolve(
|
|
94
|
+
downloadToBuffer(response.headers.location, redirects + 1),
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
if (response.statusCode !== 200) {
|
|
98
|
+
return reject(
|
|
99
|
+
new Error(`HTTP ${response.statusCode} fetching ${url}`),
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
const chunks = [];
|
|
103
|
+
response.on("data", (chunk) => chunks.push(chunk));
|
|
104
|
+
response.on("end", () => resolve(Buffer.concat(chunks)));
|
|
105
|
+
response.on("error", reject);
|
|
106
|
+
},
|
|
107
|
+
)
|
|
108
|
+
.on("error", reject);
|
|
109
|
+
});
|
|
91
110
|
}
|
|
92
111
|
|
|
93
112
|
function parseChecksum(checksumsText, fileName) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
113
|
+
for (const line of checksumsText.split(/\r?\n/)) {
|
|
114
|
+
const match = line.match(/^([a-fA-F0-9]+)\s+(\S+)$/);
|
|
115
|
+
if (match && match[2] === fileName) {
|
|
116
|
+
return match[1];
|
|
99
117
|
}
|
|
100
|
-
|
|
118
|
+
}
|
|
119
|
+
fail(`no checksum entry for ${fileName}`);
|
|
101
120
|
}
|
|
102
121
|
|
|
103
122
|
async function extractTarGz(buffer, targetDir) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
123
|
+
const result = spawnSync("tar", ["-xzf", "-", "-C", targetDir], {
|
|
124
|
+
input: buffer,
|
|
125
|
+
});
|
|
126
|
+
if (result.status !== 0) {
|
|
127
|
+
fail(
|
|
128
|
+
`tar extraction failed: ${result.stderr?.toString() || "unknown error"}`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
108
131
|
}
|
|
109
132
|
|
|
110
133
|
function extractZip(buffer, targetDir) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
134
|
+
const tmp = path.join(targetDir, `__archive__.zip`);
|
|
135
|
+
fs.writeFileSync(tmp, buffer);
|
|
136
|
+
try {
|
|
137
|
+
const result = spawnSync(
|
|
138
|
+
"powershell",
|
|
139
|
+
[
|
|
140
|
+
"-NoProfile",
|
|
141
|
+
"-Command",
|
|
142
|
+
`Expand-Archive -Force -Path "${tmp}" -DestinationPath "${targetDir}"`,
|
|
143
|
+
],
|
|
144
|
+
{ stdio: "inherit" },
|
|
145
|
+
);
|
|
146
|
+
if (result.status !== 0) {
|
|
147
|
+
fail("zip extraction failed");
|
|
124
148
|
}
|
|
149
|
+
} finally {
|
|
150
|
+
fs.unlinkSync(tmp);
|
|
151
|
+
}
|
|
125
152
|
}
|
|
126
153
|
|
|
127
154
|
function log(message) {
|
|
128
|
-
|
|
155
|
+
process.stdout.write(`[rfabric] ${message}\n`);
|
|
129
156
|
}
|
|
130
157
|
|
|
131
158
|
function fail(message) {
|
|
132
|
-
|
|
133
|
-
|
|
159
|
+
process.stderr.write(`[rfabric] error: ${message}\n`);
|
|
160
|
+
process.exit(1);
|
|
134
161
|
}
|