@volcano.dev/cli 0.37.1 → 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 CHANGED
@@ -38,7 +38,7 @@ volcano --help
38
38
  Or install manually:
39
39
 
40
40
  ```bash
41
- curl -fsSL https://github.com/Kong/volcano-cli/releases/latest/download/install.sh | sh
41
+ curl -fsSL https://download.volcano.dev/builds/install.sh | sh
42
42
  volcano --help
43
43
  ```
44
44
 
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
- console.error(
26
- 'Install it manually from https://github.com/Kong/volcano-cli/releases ' +
27
- 'or re-install the package.'
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volcano.dev/cli",
3
- "version": "0.37.1",
3
+ "version": "0.37.3",
4
4
  "description": "CLI for Volcano's hosting platform.",
5
5
  "keywords": [
6
6
  "volcano",
@@ -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 GitHub Releases. Used by the postinstall script
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
- 'https://github.com/Kong/volcano-cli/releases'
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
- const actual = (await downloadToFile(`${base}/${name}`, dest)).toLowerCase();
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
+ }