Skip to content

Commit 8bb9818

Browse files
feat: optimize the migration experience from husky (#127)
Co-authored-by: JounQin <[email protected]>
1 parent d9d7823 commit 8bb9818

File tree

4 files changed

+74
-40
lines changed

4 files changed

+74
-40
lines changed

.changeset/smart-queens-ring.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"simple-git-hooks": minor
3+
---
4+
5+
feat: optimize the migration experience from `husky`

README.md

-33
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,6 @@ If you need multiple verbose commands per git hook, flexible configuration or au
9696
3. Run the CLI script to update the git hooks with the commands from the config:
9797

9898
```sh
99-
# [Optional] These 2 steps can be skipped for non-husky users
100-
git config core.hooksPath .git/hooks/
101-
rm -rf .git/hooks
102-
103-
# Update ./git/hooks
10499
npx simple-git-hooks
105100
```
106101

@@ -211,34 +206,6 @@ If you have the option to set arguments or environment variables, you can use th
211206

212207
If these options are not available, you may need to resort to using the terminal for skipping hooks.
213208

214-
### When migrating from `husky` git hooks are not running
215-
216-
**Why is this happening?**
217-
218-
Husky might change the `core.gitHooks` value to `.husky`, this way, git hooks would search `.husky` directory instead of `.git/hooks/`.
219-
220-
Read more on git configuration in [Git book](https://git-scm.com/docs/githooks)
221-
222-
You can check it by running this command inside of your repo:
223-
224-
`git config core.hooksPath`
225-
226-
If it outputs `.husky` then this is your case
227-
228-
**How to fix?**
229-
230-
you need to point `core.gitHooks` value to `your-awesome-project/.git/hooks`. You can use this command:
231-
232-
`git config core.hooksPath .git/hooks/`
233-
234-
validate the value is set:
235-
236-
`git config core.hooksPath`
237-
238-
should output: `.git/hooks/`
239-
240-
Then remove the `.husky` folder that are generated previously by `husky`.
241-
242209
### I am getting "npx: command not found" error in a GUI git client
243210

244211
This happens when using a node version manager such as `nodenv`, `nvm`, `mise` which require

simple-git-hooks.js

+37-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const fs = require('fs')
22
const path = require('path')
33
const url = require('url')
4+
const { execSync } = require('child_process');
45

56
const CONFIG_ERROR = '[ERROR] Config was not found! Please add `.simple-git-hooks.cjs` or `.simple-git-hooks.js` or `.simple-git-hooks.mjs` or `simple-git-hooks.cjs` or `simple-git-hooks.js` or `simple-git-hooks.mjs` or `.simple-git-hooks.json` or `simple-git-hooks.json` or `simple-git-hooks` entry in package.json.\r\nCheck README for details'
67

@@ -185,7 +186,36 @@ async function setHooksFromConfig(projectRootPath=process.cwd(), argv=process.ar
185186
}
186187

187188
/**
188-
* Creates or replaces an existing executable script in .git/hooks/<hook> with provided command
189+
* Returns the absolute path to the Git hooks directory.
190+
* Respects user-defined core.hooksPath from Git config if present;
191+
* otherwise defaults to <gitRoot>/.git/hooks.
192+
*
193+
* @param {string} gitRoot - The absolute path to the Git project root
194+
* @returns {string} - The resolved absolute path to the hooks directory
195+
* @private
196+
*/
197+
function _getHooksDirPath(projectRoot) {
198+
const defaultHooksDirPath = path.join(projectRoot, '.git', 'hooks')
199+
try {
200+
const customHooksDirPath = execSync('git config core.hooksPath', {
201+
cwd: projectRoot,
202+
encoding: 'utf8'
203+
}).trim()
204+
205+
if (!customHooksDirPath) {
206+
return defaultHooksDirPath
207+
}
208+
209+
return path.isAbsolute(customHooksDirPath)
210+
? customHooksDirPath
211+
: path.resolve(projectRoot, customHooksDirPath)
212+
} catch {
213+
return defaultHooksDirPath
214+
}
215+
}
216+
217+
/**
218+
* Creates or replaces an existing executable script in the git hooks directory with provided command
189219
* @param {string} hook
190220
* @param {string} command
191221
* @param {string} projectRoot
@@ -200,12 +230,12 @@ function _setHook(hook, command, projectRoot=process.cwd()) {
200230
}
201231

202232
const hookCommand = PREPEND_SCRIPT + command
203-
const hookDirectory = gitRoot + '/hooks/'
204-
const hookPath = path.normalize(hookDirectory + hook)
233+
const hookDirectory = _getHooksDirPath(projectRoot)
234+
const hookPath = path.join(hookDirectory, hook)
205235

206236
const normalizedHookDirectory = path.normalize(hookDirectory)
207237
if (!fs.existsSync(normalizedHookDirectory)) {
208-
fs.mkdirSync(normalizedHookDirectory)
238+
fs.mkdirSync(normalizedHookDirectory, { recursive: true })
209239
}
210240

211241
fs.writeFileSync(hookPath, hookCommand)
@@ -235,14 +265,14 @@ async function removeHooks(projectRoot = process.cwd()) {
235265
}
236266

237267
/**
238-
* Removes the pre-commit hook from .git/hooks
268+
* Removes the pre-commit hook
239269
* @param {string} hook
240270
* @param {string} projectRoot
241271
* @private
242272
*/
243273
function _removeHook(hook, projectRoot=process.cwd()) {
244-
const gitRoot = getGitProjectRoot(projectRoot)
245-
const hookPath = path.normalize(gitRoot + '/hooks/' + hook)
274+
const hookDirectory = _getHooksDirPath(projectRoot)
275+
const hookPath = path.join(hookDirectory, hook)
246276

247277
if (fs.existsSync(hookPath)) {
248278
fs.unlinkSync(hookPath)

simple-git-hooks.test.js

+32
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,38 @@ describe("Simple Git Hooks tests", () => {
499499
});
500500
});
501501

502+
describe("Custom core.hooksPath", () => {
503+
beforeAll(() => {
504+
execSync('git config core.hooksPath .husky');
505+
});
506+
507+
afterAll(() => {
508+
execSync('git config --unset core.hooksPath');
509+
});
510+
511+
const TEST_HUSKY_PROJECT = PROJECT_WITH_CONF_IN_SEPARATE_JS_ALT;
512+
513+
it("creates git hooks in .husky if core.hooksPath is set to .husky", async () => {
514+
const huskyDir = path.join(TEST_HUSKY_PROJECT, ".husky");
515+
516+
if (!fs.existsSync(huskyDir)) {
517+
fs.mkdirSync(huskyDir);
518+
}
519+
520+
await simpleGitHooks.setHooksFromConfig(TEST_HUSKY_PROJECT);
521+
522+
const installedHooks = getInstalledGitHooks(huskyDir);
523+
expect(isEqual(installedHooks, COMMON_GIT_HOOKS)).toBe(true);
524+
})
525+
526+
it("remove git hooks in .husky if core.hooksPath is set to .husky", async () => {
527+
await simpleGitHooks.removeHooks(TEST_HUSKY_PROJECT);
528+
const huskyDir = path.join(TEST_HUSKY_PROJECT, ".husky");
529+
const installedHooks = getInstalledGitHooks(huskyDir);
530+
expect(isEqual(installedHooks, {})).toBe(true);
531+
})
532+
})
533+
502534
describe("CLI tests", () => {
503535
const testCases = [
504536
["npx", "simple-git-hooks", "./git-hooks.js"],

0 commit comments

Comments
 (0)