From 6db98852c52c29b9737528854d57a0cc1b97f667 Mon Sep 17 00:00:00 2001 From: akmhatey-ai <260399619+akmhatey-ai@users.noreply.github.com> Date: Sun, 17 May 2026 08:30:47 +0200 Subject: [PATCH] fix(npm): fail postinstall when binary download fails Signed-off-by: akmhatey-ai <260399619+akmhatey-ai@users.noreply.github.com> --- .github/workflows/ci.yml | 1 + npm/postinstall.js | 5 ++-- npm/postinstall.test.js | 58 ++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 4 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 npm/postinstall.test.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16a12d7..1db9b1b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,4 +52,5 @@ jobs: with: node-version: "20" - run: OPTIQOR_SKIP_POSTINSTALL=1 npm install --ignore-scripts + - run: npm run test:postinstall - run: npm pack diff --git a/npm/postinstall.js b/npm/postinstall.js index 07bf79b..b05b6dd 100644 --- a/npm/postinstall.js +++ b/npm/postinstall.js @@ -74,9 +74,8 @@ const pipelineP = promisify(pipeline); console.log('optiqor: ready. Run `optiqor --version` to verify.'); } catch (err) { console.error('optiqor: failed to install binary:', err.message); - console.error('optiqor: this is non-fatal — build from source if needed:'); + console.error('optiqor: build from source if needed:'); console.error(' go install github.com/optiqor/optiqor-cli/cmd/optiqor@latest'); - // Exit 0 so npm install does not abort entirely. - process.exit(0); + process.exit(1); } })(); diff --git a/npm/postinstall.test.js b/npm/postinstall.test.js new file mode 100644 index 0000000..ae6f56b --- /dev/null +++ b/npm/postinstall.test.js @@ -0,0 +1,58 @@ +#!/usr/bin/env node + +const assert = require('assert/strict'); +const fs = require('fs'); +const path = require('path'); +const { spawnSync } = require('child_process'); + +const root = path.join(__dirname, '..'); +const vendorDir = path.join(root, 'vendor'); + +const runPostinstallWithHttpFailure = () => { + const script = ` + const https = require('https'); + const { Readable } = require('stream'); + + https.get = (url, callback) => { + const response = new Readable({ + read() { + this.push(null); + }, + }); + response.statusCode = 404; + response.headers = {}; + process.nextTick(() => callback(response)); + + return { + on() { + return this; + }, + }; + }; + + Object.defineProperty(process, 'platform', { value: 'linux' }); + Object.defineProperty(process, 'arch', { value: 'x64' }); + require('./npm/postinstall.js'); + `; + + return spawnSync(process.execPath, ['-e', script], { + cwd: root, + encoding: 'utf8', + }); +}; + +fs.rmSync(vendorDir, { recursive: true, force: true }); + +try { + const result = runPostinstallWithHttpFailure(); + + assert.equal( + result.status, + 1, + `expected failed binary download to exit 1\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}` + ); + assert.match(result.stderr, /failed to install binary: HTTP 404/); + assert.doesNotMatch(result.stderr, /non-fatal/); +} finally { + fs.rmSync(vendorDir, { recursive: true, force: true }); +} diff --git a/package.json b/package.json index ecb38c2..c05aa31 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ }, "scripts": { "postinstall": "node ./npm/postinstall.js", + "test:postinstall": "node ./npm/postinstall.test.js", "test": "node -e \"require('./npm/index.js')\"" }, "files": [