Skip to content

Commit a07573f

Browse files
fix: make husky prepare resilient when husky is unavailable
1 parent 21ec149 commit a07573f

2 files changed

Lines changed: 71 additions & 13 deletions

File tree

.husky/install.mjs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,31 @@
1-
// Skip Husky installation in environments where dev dependencies may be omitted
1+
import { existsSync } from 'node:fs';
2+
3+
// Skip Husky installation in environments where hooks are not needed
24
if (
3-
process.env.NODE_ENV === 'production' ||
4-
process.env.CI === 'true' ||
5-
process.env.HUSKY === '0'
5+
process.env.NODE_ENV === 'production' ||
6+
process.env.CI === 'true' ||
7+
process.env.HUSKY === '0' ||
8+
!existsSync('.git')
69
) {
7-
process.exit(0);
10+
process.exit(0);
811
}
912

1013
try {
11-
const { install } = await import('husky');
14+
const { install } = await import('husky');
1215

1316
// Only call install if it exists (safe check)
14-
if (typeof install === 'function') {
17+
if (typeof install === 'function') {
1518
install();
16-
}
19+
}
1720
} catch (error) {
1821
// Ignore error if husky is not installed (e.g., production/CI environments)
19-
if (
22+
if (
2023
error?.code === 'ERR_MODULE_NOT_FOUND' ||
2124
error?.message?.includes("Cannot find package 'husky'")
22-
) {
25+
) {
2326
process.exit(0);
24-
}
27+
}
2528

2629
// Re-throw other unexpected errors
27-
throw error;
28-
}
30+
throw error;
31+
}

test/unit/husky/install.spec.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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+
})
19+
}
20+
21+
describe('husky install script', () => {
22+
it('exits successfully when .git is missing', () => {
23+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nostream-husky-no-git-'))
24+
25+
try {
26+
const result = runInstall(tmpDir)
27+
expect(result.status).to.equal(0)
28+
} finally {
29+
fs.rmSync(tmpDir, { recursive: true, force: true })
30+
}
31+
})
32+
33+
it('exits successfully when HUSKY is disabled', () => {
34+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nostream-husky-disabled-'))
35+
36+
try {
37+
const result = runInstall(tmpDir, { HUSKY: '0' })
38+
expect(result.status).to.equal(0)
39+
} finally {
40+
fs.rmSync(tmpDir, { recursive: true, force: true })
41+
}
42+
})
43+
44+
it('exits successfully when husky package is unavailable even if .git exists', () => {
45+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nostream-husky-missing-package-'))
46+
47+
try {
48+
fs.mkdirSync(path.join(tmpDir, '.git'))
49+
const result = runInstall(tmpDir)
50+
expect(result.status).to.equal(0)
51+
} finally {
52+
fs.rmSync(tmpDir, { recursive: true, force: true })
53+
}
54+
})
55+
})

0 commit comments

Comments
 (0)