@paulnsorensen/tilth-nightly 0.0.0-experimental.0 → 0.0.0-experimental.100.1
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/install.js +93 -19
- package/package.json +1 -1
- package/run.js +25 -8
package/install.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
const https = require("https");
|
|
6
6
|
const fs = require("fs");
|
|
7
7
|
const path = require("path");
|
|
8
|
+
const crypto = require("crypto");
|
|
8
9
|
const { execSync } = require("child_process");
|
|
9
10
|
|
|
10
11
|
const PLATFORM_MAP = {
|
|
@@ -41,10 +42,24 @@ fs.mkdirSync(binDir, { recursive: true });
|
|
|
41
42
|
console.log(`tilth: downloading ${target} nightly binary...`);
|
|
42
43
|
|
|
43
44
|
const MAX_REDIRECTS = 5;
|
|
45
|
+
const MAX_ATTEMPTS = 4;
|
|
46
|
+
const RETRY_BACKOFF_MS = [1000, 2000, 4000];
|
|
47
|
+
|
|
48
|
+
function isRetryableStatus(code) {
|
|
49
|
+
return code === 429 || code === 404 || (code >= 500 && code < 600);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function retryDownload(url, depth, callback, attempt, reason) {
|
|
53
|
+
const delayMs = RETRY_BACKOFF_MS[attempt - 1];
|
|
54
|
+
console.error(
|
|
55
|
+
`tilth: download failed (${reason}), retry ${attempt}/${MAX_ATTEMPTS - 1} in ${delayMs / 1000}s`,
|
|
56
|
+
);
|
|
57
|
+
setTimeout(() => follow(url, depth, callback, attempt + 1), delayMs);
|
|
58
|
+
}
|
|
44
59
|
|
|
45
60
|
// HTTPS-only, depth-capped: this binary is executed after download, so never
|
|
46
61
|
// let a redirect downgrade to plaintext or loop.
|
|
47
|
-
function follow(url, depth, callback) {
|
|
62
|
+
function follow(url, depth, callback, attempt = 1) {
|
|
48
63
|
if (!url.startsWith("https:")) {
|
|
49
64
|
console.error(`tilth: refusing non-HTTPS download URL: ${url}`);
|
|
50
65
|
process.exit(1);
|
|
@@ -53,9 +68,14 @@ function follow(url, depth, callback) {
|
|
|
53
68
|
console.error(`tilth: too many redirects (>${MAX_REDIRECTS})`);
|
|
54
69
|
process.exit(1);
|
|
55
70
|
}
|
|
56
|
-
https.get(url, { headers: { "User-Agent": "tilth-npm" } }, (res) => {
|
|
71
|
+
const req = https.get(url, { headers: { "User-Agent": "tilth-npm" } }, (res) => {
|
|
57
72
|
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
58
|
-
|
|
73
|
+
res.resume();
|
|
74
|
+
// Location may be relative; resolve against the current URL before recursing.
|
|
75
|
+
follow(new URL(res.headers.location, url).href, depth + 1, callback, attempt);
|
|
76
|
+
} else if (isRetryableStatus(res.statusCode) && attempt < MAX_ATTEMPTS) {
|
|
77
|
+
res.resume();
|
|
78
|
+
retryDownload(url, depth, callback, attempt, `HTTP ${res.statusCode}`);
|
|
59
79
|
} else if (res.statusCode !== 200) {
|
|
60
80
|
console.error(`tilth: download failed (HTTP ${res.statusCode})`);
|
|
61
81
|
console.error(`URL: ${url}`);
|
|
@@ -64,33 +84,65 @@ function follow(url, depth, callback) {
|
|
|
64
84
|
} else {
|
|
65
85
|
callback(res);
|
|
66
86
|
}
|
|
67
|
-
})
|
|
87
|
+
});
|
|
88
|
+
req.on("error", (err) => {
|
|
89
|
+
if (attempt < MAX_ATTEMPTS) {
|
|
90
|
+
retryDownload(url, depth, callback, attempt, err.message);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
68
93
|
console.error(`tilth: download failed: ${err.message}`);
|
|
69
94
|
console.error("Install manually: cargo install --git https://github.com/paulnsorensen/tilth");
|
|
70
95
|
process.exit(1);
|
|
71
96
|
});
|
|
97
|
+
req.setTimeout(30000, () => {
|
|
98
|
+
req.destroy(new Error("timeout after 30s"));
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function buffer(res, callback) {
|
|
103
|
+
const chunks = [];
|
|
104
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
105
|
+
res.on("end", () => callback(Buffer.concat(chunks)));
|
|
106
|
+
res.on("error", (err) => {
|
|
107
|
+
console.error(`tilth: download failed: ${err.message}`);
|
|
108
|
+
console.error("Install manually: cargo install --git https://github.com/paulnsorensen/tilth");
|
|
109
|
+
process.exit(1);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function parseChecksum(text, sidecarUrl) {
|
|
114
|
+
const match = /^([0-9a-fA-F]{64})\s/.exec(text);
|
|
115
|
+
if (!match) {
|
|
116
|
+
console.error(`tilth: malformed checksum sidecar at ${sidecarUrl}`);
|
|
117
|
+
console.error("Install manually: cargo install --git https://github.com/paulnsorensen/tilth");
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
return match[1].toLowerCase();
|
|
72
121
|
}
|
|
73
122
|
|
|
74
|
-
|
|
123
|
+
function extract(buf) {
|
|
75
124
|
if (isWindows) {
|
|
125
|
+
// Extract with PowerShell's Expand-Archive, not `tar`: the `tar` resolved on
|
|
126
|
+
// a Windows PATH may be GNU tar (shipped by Git for Windows), which cannot
|
|
127
|
+
// read the zip format and fails. Expand-Archive is built into Windows
|
|
128
|
+
// PowerShell and handles zip natively.
|
|
76
129
|
const tmpZip = path.join(binDir, "tilth.zip");
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
});
|
|
130
|
+
fs.writeFileSync(tmpZip, buf);
|
|
131
|
+
try {
|
|
132
|
+
execSync(
|
|
133
|
+
`powershell -NoProfile -NonInteractive -Command "Expand-Archive -LiteralPath '${tmpZip}' -DestinationPath '${binDir}' -Force"`,
|
|
134
|
+
{ stdio: "inherit" },
|
|
135
|
+
);
|
|
136
|
+
fs.unlinkSync(tmpZip);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
console.error(`tilth: failed to extract: ${err.message}`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
89
141
|
} else {
|
|
90
142
|
const tar = require("child_process").spawn("tar", ["xz", "-C", binDir], {
|
|
91
143
|
stdio: ["pipe", "inherit", "inherit"],
|
|
92
144
|
});
|
|
93
|
-
|
|
145
|
+
tar.stdin.end(buf);
|
|
94
146
|
tar.on("close", (code) => {
|
|
95
147
|
if (code !== 0) {
|
|
96
148
|
console.error("tilth: failed to extract.");
|
|
@@ -100,4 +152,26 @@ follow(url, 0, (res) => {
|
|
|
100
152
|
console.log("tilth: installed successfully");
|
|
101
153
|
});
|
|
102
154
|
}
|
|
103
|
-
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const sidecarUrl = `${url}.sha256`;
|
|
158
|
+
|
|
159
|
+
follow(sidecarUrl, 0, (sidecarRes) => {
|
|
160
|
+
buffer(sidecarRes, (sidecarBuf) => {
|
|
161
|
+
const expected = parseChecksum(sidecarBuf.toString("utf8"), sidecarUrl);
|
|
162
|
+
|
|
163
|
+
follow(url, 0, (archiveRes) => {
|
|
164
|
+
buffer(archiveRes, (archiveBuf) => {
|
|
165
|
+
const actual = crypto.createHash("sha256").update(archiveBuf).digest("hex");
|
|
166
|
+
if (actual !== expected) {
|
|
167
|
+
console.error("tilth: checksum mismatch — refusing to install");
|
|
168
|
+
console.error(`Expected: ${expected}`);
|
|
169
|
+
console.error(`Actual: ${actual}`);
|
|
170
|
+
console.error("Install manually: cargo install --git https://github.com/paulnsorensen/tilth");
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
extract(archiveBuf);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paulnsorensen/tilth-nightly",
|
|
3
|
-
"version": "0.0.0-experimental.
|
|
3
|
+
"version": "0.0.0-experimental.100.1",
|
|
4
4
|
"description": "EXPERIMENTAL rolling build of paulnsorensen/tilth off main — unofficial fork, NOT the official `tilth` package",
|
|
5
5
|
"bin": {
|
|
6
6
|
"tilth": "run.js"
|
package/run.js
CHANGED
|
@@ -2,20 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const {
|
|
5
|
+
const { spawn } = require("child_process");
|
|
6
6
|
const path = require("path");
|
|
7
7
|
|
|
8
8
|
const isWindows = process.platform === "win32";
|
|
9
9
|
const binName = isWindows ? "tilth.exe" : "tilth";
|
|
10
10
|
const bin = path.join(__dirname, "bin", binName);
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
const child = spawn(bin, process.argv.slice(2), { stdio: "inherit" });
|
|
13
|
+
|
|
14
|
+
const SIGNALS = ["SIGTERM", "SIGINT", "SIGHUP"];
|
|
15
|
+
const forwarders = {};
|
|
16
|
+
for (const sig of SIGNALS) {
|
|
17
|
+
forwarders[sig] = () => {
|
|
18
|
+
if (!child.killed) child.kill(sig);
|
|
19
|
+
};
|
|
20
|
+
process.on(sig, forwarders[sig]);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
child.on("error", (err) => {
|
|
18
24
|
console.error(`tilth: failed to run binary at ${bin}`);
|
|
19
25
|
console.error(err.message);
|
|
20
26
|
process.exit(1);
|
|
21
|
-
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
child.on("exit", (code, signal) => {
|
|
30
|
+
if (signal) {
|
|
31
|
+
// Remove only our own forwarders before re-raising, so the signal takes the
|
|
32
|
+
// default terminate action instead of being swallowed by a live handler.
|
|
33
|
+
for (const sig of SIGNALS) process.removeListener(sig, forwarders[sig]);
|
|
34
|
+
process.kill(process.pid, signal);
|
|
35
|
+
} else {
|
|
36
|
+
process.exit(code ?? 0);
|
|
37
|
+
}
|
|
38
|
+
});
|