nur-cli 0.43.0 → 0.43.2
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.js +42 -5
- package/package.json +1 -1
package/bin.js
CHANGED
|
@@ -17,11 +17,13 @@ const http = require("http");
|
|
|
17
17
|
const fs = require("fs");
|
|
18
18
|
const path = require("path");
|
|
19
19
|
const os = require("os");
|
|
20
|
+
const crypto = require("crypto");
|
|
20
21
|
const { spawnSync } = require("child_process");
|
|
21
22
|
|
|
22
23
|
const REPO = "nuroctane/nur-cli";
|
|
23
|
-
// Falls back to this
|
|
24
|
-
|
|
24
|
+
// Falls back to this package's own release when `latest/download` is
|
|
25
|
+
// unreachable. publish-npm.yml stamps the release tag into package.json.
|
|
26
|
+
const FALLBACK_VERSION = require("./package.json").version;
|
|
25
27
|
|
|
26
28
|
function fail(msg) {
|
|
27
29
|
process.stderr.write(`nur-cli: ${msg}\n`);
|
|
@@ -65,7 +67,9 @@ function fetchBuffer(url, redirects) {
|
|
|
65
67
|
}
|
|
66
68
|
if (status !== 200) {
|
|
67
69
|
res.resume();
|
|
68
|
-
|
|
70
|
+
const err = new Error(`HTTP ${status} for ${url}`);
|
|
71
|
+
err.status = status;
|
|
72
|
+
return reject(err);
|
|
69
73
|
}
|
|
70
74
|
const chunks = [];
|
|
71
75
|
res.on("data", (c) => chunks.push(c));
|
|
@@ -123,14 +127,47 @@ async function downloadAndInstall() {
|
|
|
123
127
|
if (buf.length < 1000000) {
|
|
124
128
|
fail(`downloaded asset too small (${buf.length} bytes) - aborting`);
|
|
125
129
|
}
|
|
130
|
+
await verifyChecksum(picked, buf);
|
|
126
131
|
|
|
132
|
+
// Write beside the target, then rename: an interrupted download never leaves
|
|
133
|
+
// a truncated `nur` behind.
|
|
127
134
|
const dest = installedBinaryPath();
|
|
128
|
-
|
|
129
|
-
|
|
135
|
+
const tmp = `${dest}.tmp-${process.pid}`;
|
|
136
|
+
fs.writeFileSync(tmp, buf);
|
|
137
|
+
if (asset.execBit) fs.chmodSync(tmp, 0o755);
|
|
138
|
+
try {
|
|
139
|
+
fs.renameSync(tmp, dest);
|
|
140
|
+
} catch (e) {
|
|
141
|
+
fs.rmSync(tmp, { force: true });
|
|
142
|
+
if (e.code === "EPERM" || e.code === "EBUSY") {
|
|
143
|
+
fail(`${dest} is in use - close every running nur, then run this again`);
|
|
144
|
+
}
|
|
145
|
+
throw e;
|
|
146
|
+
}
|
|
130
147
|
process.stdout.write(`nur-cli: installed ${dest}\n`);
|
|
131
148
|
return dest;
|
|
132
149
|
}
|
|
133
150
|
|
|
151
|
+
/** Check `buf` against the `<asset>.sha256` the release publishes beside it. */
|
|
152
|
+
async function verifyChecksum(url, buf) {
|
|
153
|
+
const name = url.split("/").pop();
|
|
154
|
+
let record;
|
|
155
|
+
try {
|
|
156
|
+
record = (await fetchBuffer(`${url}.sha256`)).toString("utf8");
|
|
157
|
+
} catch (e) {
|
|
158
|
+
if (e.status === 404) return; // a release from before checksums were published
|
|
159
|
+
fail(`could not fetch the checksum for ${name} (${e.message})`);
|
|
160
|
+
}
|
|
161
|
+
const expected = (record.trim().split(/\s+/)[0] || "").toLowerCase();
|
|
162
|
+
if (!/^[0-9a-f]{64}$/.test(expected)) {
|
|
163
|
+
fail(`${name}.sha256 is not a sha256 record - aborting`);
|
|
164
|
+
}
|
|
165
|
+
const actual = crypto.createHash("sha256").update(buf).digest("hex");
|
|
166
|
+
if (actual !== expected) {
|
|
167
|
+
fail(`${name} does not match its published sha256 (${actual} != ${expected}) - aborting`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
134
171
|
function runNurInstall(bin) {
|
|
135
172
|
// Full one-stop install: PATH wiring, prereqs, ecosystem packs, browser.
|
|
136
173
|
// The binary owns all of it - same as double-clicking a release EXE.
|
package/package.json
CHANGED