@volcano.dev/cli 0.37.2 → 0.37.3
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 +1 -1
- package/bin/volcano.js +4 -4
- package/package.json +1 -1
- package/scripts/npm/download.js +8 -10
- package/scripts/npm/download.test.js +55 -0
package/README.md
CHANGED
package/bin/volcano.js
CHANGED
|
@@ -22,10 +22,10 @@ async function resolveBinary() {
|
|
|
22
22
|
// resolveTarget() throws for e.g. Windows arm64, which passes the
|
|
23
23
|
// package.json os/cpu gate but has no published binary.
|
|
24
24
|
console.error(`volcano: could not obtain the CLI binary: ${err.message}`);
|
|
25
|
-
|
|
26
|
-
'
|
|
27
|
-
|
|
28
|
-
);
|
|
25
|
+
const manual = process.platform === 'win32'
|
|
26
|
+
? 'https://download.volcano.dev/builds/releases/latest/download/volcano-windows-amd64.exe'
|
|
27
|
+
: 'https://download.volcano.dev/builds/install.sh';
|
|
28
|
+
console.error(`Install it manually from ${manual} or re-install the package.`);
|
|
29
29
|
process.exit(1);
|
|
30
30
|
}
|
|
31
31
|
}
|
package/package.json
CHANGED
package/scripts/npm/download.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
// Shared helpers for downloading and verifying the platform-specific Volcano
|
|
4
|
-
// CLI binary from
|
|
4
|
+
// CLI binary from Volcano downloads. Used by the postinstall script
|
|
5
5
|
// (scripts/npm/install.js) and, as a self-healing fallback, by the launcher
|
|
6
6
|
// shim (bin/volcano.js).
|
|
7
7
|
|
|
@@ -20,7 +20,8 @@ const pkg = require('../../package.json');
|
|
|
20
20
|
// Allow pointing at a mirror (kept consistent with scripts/install-volcano.sh).
|
|
21
21
|
const RELEASES_BASE = (
|
|
22
22
|
process.env.VOLCANO_GITHUB_RELEASES_URL ||
|
|
23
|
-
|
|
23
|
+
process.env.VOLCANO_CLI_RELEASES_URL ||
|
|
24
|
+
'https://download.volcano.dev/builds/releases'
|
|
24
25
|
).replace(/\/+$/, '');
|
|
25
26
|
|
|
26
27
|
// Map Node's platform-arch to the release asset target id.
|
|
@@ -153,7 +154,7 @@ function parseChecksum(manifest, name) {
|
|
|
153
154
|
return null;
|
|
154
155
|
}
|
|
155
156
|
|
|
156
|
-
async function downloadToFile(url, dest) {
|
|
157
|
+
async function downloadToFile(url, dest, expected) {
|
|
157
158
|
const tmp = `${dest}.download-${process.pid}`;
|
|
158
159
|
try {
|
|
159
160
|
const res = await get(url);
|
|
@@ -163,6 +164,9 @@ async function downloadToFile(url, dest) {
|
|
|
163
164
|
const hash = crypto.createHash('sha256');
|
|
164
165
|
await streamPipeline(fs.createReadStream(tmp), hash);
|
|
165
166
|
const digest = hash.digest('hex');
|
|
167
|
+
if (digest !== expected) {
|
|
168
|
+
throw new Error(`Checksum mismatch for ${path.basename(dest)}: expected ${expected}, got ${digest}`);
|
|
169
|
+
}
|
|
166
170
|
// rename does not overwrite an existing destination on Windows, so clear it
|
|
167
171
|
// first (also covers a check-then-write race and force re-downloads).
|
|
168
172
|
fs.rmSync(dest, { force: true });
|
|
@@ -202,13 +206,7 @@ async function ensureBinary({ force = false } = {}) {
|
|
|
202
206
|
}
|
|
203
207
|
|
|
204
208
|
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
205
|
-
|
|
206
|
-
if (actual !== expected) {
|
|
207
|
-
fs.rmSync(dest, { force: true });
|
|
208
|
-
throw new Error(
|
|
209
|
-
`Checksum mismatch for ${name}: expected ${expected}, got ${actual}`
|
|
210
|
-
);
|
|
211
|
-
}
|
|
209
|
+
await downloadToFile(`${base}/${name}`, dest, expected);
|
|
212
210
|
|
|
213
211
|
if (process.platform !== 'win32') {
|
|
214
212
|
fs.chmodSync(dest, 0o755);
|
|
@@ -22,3 +22,58 @@ test('maps supported Node platforms to published release assets', () => {
|
|
|
22
22
|
test('rejects a platform without a published release asset', () => {
|
|
23
23
|
assert.throws(() => assetName('win32', 'arm64'), /Unsupported platform "win32-arm64"/);
|
|
24
24
|
});
|
|
25
|
+
|
|
26
|
+
const fs = require('node:fs');
|
|
27
|
+
const os = require('node:os');
|
|
28
|
+
const path = require('node:path');
|
|
29
|
+
const http = require('node:http');
|
|
30
|
+
const crypto = require('node:crypto');
|
|
31
|
+
const { spawn } = require('node:child_process');
|
|
32
|
+
|
|
33
|
+
for (const corrupt of [false, true]) {
|
|
34
|
+
test(`first-party download ${corrupt ? 'preserves the installed binary on checksum failure' : 'installs without GitHub'}`, async (t) => {
|
|
35
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'volcano-npm-'));
|
|
36
|
+
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
|
|
37
|
+
fs.mkdirSync(path.join(root, 'scripts/npm'), { recursive: true });
|
|
38
|
+
fs.mkdirSync(path.join(root, 'bin'));
|
|
39
|
+
fs.copyFileSync(path.join(__dirname, 'download.js'), path.join(root, 'scripts/npm/download.js'));
|
|
40
|
+
fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify({ version: '1.2.3' }));
|
|
41
|
+
const name = assetName();
|
|
42
|
+
const dest = path.join(root, 'bin', name);
|
|
43
|
+
fs.writeFileSync(dest, 'old binary');
|
|
44
|
+
const payload = 'new binary';
|
|
45
|
+
const checksum = crypto.createHash('sha256').update(corrupt ? 'wrong' : payload).digest('hex');
|
|
46
|
+
const requests = [];
|
|
47
|
+
const server = http.createServer((req, res) => {
|
|
48
|
+
requests.push(req.url);
|
|
49
|
+
if (req.url === '/builds/releases/download/v1.2.3/SHA256SUMS') res.end(`${checksum} ${name}\n`);
|
|
50
|
+
else if (req.url === `/builds/releases/download/v1.2.3/${name}`) res.end(payload);
|
|
51
|
+
else { res.statusCode = 404; res.end(); }
|
|
52
|
+
});
|
|
53
|
+
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
|
|
54
|
+
t.after(() => new Promise(resolve => server.close(resolve)));
|
|
55
|
+
// Intercept only the first-party host. A GitHub dependency fails the test.
|
|
56
|
+
const script = `
|
|
57
|
+
require('https').get = (url, options, callback) => {
|
|
58
|
+
const parsed = new URL(url);
|
|
59
|
+
if (parsed.origin !== 'https://download.volcano.dev') throw Error('unexpected host: ' + parsed.origin);
|
|
60
|
+
return require('http').get('http://127.0.0.1:${server.address().port}' + parsed.pathname, options, callback);
|
|
61
|
+
};
|
|
62
|
+
require('./scripts/npm/download.js').ensureBinary({force:true}).catch(e => {console.error(e.message);process.exitCode=1});
|
|
63
|
+
`;
|
|
64
|
+
const env = { ...process.env };
|
|
65
|
+
delete env.VOLCANO_CLI_RELEASES_URL;
|
|
66
|
+
delete env.VOLCANO_GITHUB_RELEASES_URL;
|
|
67
|
+
// Launched through a package manager, ensureBinary() would also write an install-method marker into bin/.
|
|
68
|
+
delete env.npm_config_user_agent;
|
|
69
|
+
const child = spawn(process.execPath, ['-e', script], { cwd: root, env });
|
|
70
|
+
let stderr = '';
|
|
71
|
+
child.stderr.on('data', chunk => { stderr += chunk; });
|
|
72
|
+
const code = await new Promise(resolve => child.on('close', resolve));
|
|
73
|
+
assert.equal(code, corrupt ? 1 : 0, stderr);
|
|
74
|
+
if (corrupt) assert.match(stderr, /Checksum mismatch/);
|
|
75
|
+
assert.equal(fs.readFileSync(dest, 'utf8'), corrupt ? 'old binary' : payload);
|
|
76
|
+
assert.equal(requests.length, 2);
|
|
77
|
+
assert.deepEqual(fs.readdirSync(path.join(root, 'bin')), [name]);
|
|
78
|
+
});
|
|
79
|
+
}
|