Skip to content

Commit 1b4d532

Browse files
fix: add husky install fallback for non-dev environments
Replace the inline 'husky install || exit 0' prepare script with a dedicated .husky/install.mjs that skips installation cleanly in production, CI, Docker, and HUSKY=0 environments. Remove unused k6 performance test scripts and @types/k6 devDependency. Closes #328
1 parent d8f62b4 commit 1b4d532

4 files changed

Lines changed: 82 additions & 4 deletions

File tree

.changeset/chatty-steaks-fall.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"nostream": patch
3+
---
4+
5+
fix: prevent crash when husky is not available during setup

.husky/install.mjs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { existsSync } from 'node:fs';
2+
import { execSync } from 'node:child_process';
3+
4+
// Skip Husky installation in environments where hooks are not needed
5+
if (
6+
process.env.NODE_ENV === 'production' ||
7+
process.env.CI === 'true' ||
8+
process.env.HUSKY === '0' ||
9+
!existsSync('.git')
10+
) {
11+
process.exit(0);
12+
}
13+
14+
try {
15+
execSync('husky install', { stdio: 'ignore' });
16+
} catch {
17+
// Husky is not available — skip silently (e.g., production/Docker)
18+
process.exit(0);
19+
}

package.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,6 @@
7676
"test:load": "node -r ts-node/register ./scripts/security-load-test.ts",
7777
"smoke:nip03": "node -r ts-node/register scripts/smoke-nip03.ts",
7878
"test:integration": "cucumber-js",
79-
"test:performance:connection-rate-limit": "k6 run test/performance/connection-limiting-k6.ts",
80-
"test:performance:message-rate-limit": "k6 run test/performance/message-limiting-k6.ts",
8179
"cover:integration": "nyc --report-dir .coverage/integration pnpm run test:integration -p cover",
8280
"export": "node --env-file-if-exists=.env -r ts-node/register src/scripts/export-events.ts",
8381
"docker:compose:start": "pnpm run cli -- start",
@@ -95,7 +93,7 @@
9593
"docker:cover:integration": "pnpm run docker:integration:run pnpm exec nyc --report-dir .coverage/integration pnpm run test:integration -- -p cover",
9694
"postdocker:integration:run": "docker compose -f ./test/integration/docker-compose.yml down",
9795
"prepack": "pnpm run build",
98-
"prepare": "husky install || exit 0",
96+
"prepare": "test -f .husky/install.mjs && node .husky/install.mjs || true",
9997
"changeset:version": "changeset version && pnpm install --lockfile-only",
10098
"changeset:publish": "changeset publish"
10199
},
@@ -126,7 +124,6 @@
126124
"@types/chai-as-promised": "^7.1.5",
127125
"@types/express": "4.17.21",
128126
"@types/js-yaml": "4.0.5",
129-
"@types/k6": "^1.7.0",
130127
"@types/mocha": "^9.1.1",
131128
"@types/node": "^24.12.2",
132129
"@types/pg": "^8.6.5",

test/unit/husky/install.spec.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { expect } from 'chai'
2+
import fs from 'fs'
3+
import os from 'os'
4+
import path from 'path'
5+
import { spawnSync } from 'child_process'
6+
7+
const projectRoot = process.cwd()
8+
const installScriptPath = path.join(projectRoot, '.husky', 'install.mjs')
9+
10+
const runInstall = (cwd: string, env: NodeJS.ProcessEnv = {}) => {
11+
return spawnSync('node', [installScriptPath], {
12+
cwd,
13+
env: {
14+
...process.env,
15+
...env,
16+
},
17+
encoding: 'utf-8',
18+
timeout: 10_000,
19+
})
20+
}
21+
22+
describe('husky install script', () => {
23+
it('exits successfully when .git is missing', () => {
24+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nostream-husky-no-git-'))
25+
26+
try {
27+
const result = runInstall(tmpDir)
28+
expect(result.status).to.equal(0)
29+
} finally {
30+
fs.rmSync(tmpDir, { recursive: true, force: true })
31+
}
32+
})
33+
34+
it('exits successfully when HUSKY is disabled', () => {
35+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nostream-husky-disabled-'))
36+
37+
try {
38+
const result = runInstall(tmpDir, { HUSKY: '0' })
39+
expect(result.status).to.equal(0)
40+
} finally {
41+
fs.rmSync(tmpDir, { recursive: true, force: true })
42+
}
43+
})
44+
45+
it('exits successfully when husky package is unavailable even if .git exists', function () {
46+
this.timeout(15_000)
47+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nostream-husky-missing-package-'))
48+
49+
try {
50+
fs.mkdirSync(path.join(tmpDir, '.git'))
51+
const result = runInstall(tmpDir)
52+
expect(result.status).to.equal(0)
53+
} finally {
54+
fs.rmSync(tmpDir, { recursive: true, force: true })
55+
}
56+
})
57+
})

0 commit comments

Comments
 (0)