From 9fdc3d1fd9208607d575ed038559c449f665f00d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Volbach?= Date: Fri, 19 Sep 2025 11:52:22 +0200 Subject: [PATCH] fix: skip execution gracefully when no git repository is present --- jest.config.cjs | 17 +++++++++++++++++ jest.config.ts | 9 --------- package-lock.json | 12 ++++++------ package.json | 2 +- src/cli.ts | 15 +++++++++++---- src/commands/install.ts | 6 ++---- src/commands/resetHooks.ts | 6 ++---- src/commands/uninstall.ts | 6 ++---- src/utils/git.ts | 9 +++++---- tests/cli.test.ts | 8 ++++++++ 10 files changed, 54 insertions(+), 36 deletions(-) create mode 100644 jest.config.cjs delete mode 100644 jest.config.ts diff --git a/jest.config.cjs b/jest.config.cjs new file mode 100644 index 0000000..2b2bbf7 --- /dev/null +++ b/jest.config.cjs @@ -0,0 +1,17 @@ +/** @type {import('jest').Config} */ +module.exports = { + testEnvironment: 'node', + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + tsconfig: 'tsconfig.json', + useESM: true, + }, + ], + }, + extensionsToTreatAsEsm: ['.ts'], + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, +}; diff --git a/jest.config.ts b/jest.config.ts deleted file mode 100644 index ad3cd1b..0000000 --- a/jest.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { Config } from 'jest'; - -const config: Config = { - testEnvironment: 'node', - transform: { '^.+\\.tsx?$': ['ts-jest', { tsconfig: 'tsconfig.json' }] }, - testMatch: ['**/tests/**/*.test.ts'], -}; - -export default config; diff --git a/package-lock.json b/package-lock.json index d9b0498..1f0de0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "volbrene-git-hooks", - "version": "1.8.1", + "version": "1.9.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "volbrene-git-hooks", - "version": "1.8.1", + "version": "1.9.1", "license": "MIT", "bin": { "volbrene-git-hooks": "dist/cli.js" @@ -23,7 +23,7 @@ "prettier": "^3.6.2", "rimraf": "^6.0.1", "semantic-release": "^24.0.0", - "ts-jest": "^29.4.2", + "ts-jest": "^29.4.3", "ts-node": "^10.9.2", "typescript": "^5.9.2" }, @@ -10240,9 +10240,9 @@ } }, "node_modules/ts-jest": { - "version": "29.4.2", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.2.tgz", - "integrity": "sha512-pBNOkn4HtuLpNrXTMVRC9b642CBaDnKqWXny4OzuoULT9S7Kf8MMlaRe2veKax12rjf5WcpMBhVPbQurlWGNxA==", + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.3.tgz", + "integrity": "sha512-KTWbK2Wot8VXargsLoxhSoEQ9OyMdzQXQoUDeIulWu2Tf7gghuBHeg+agZqVLdTOHhQHVKAaeuctBDRkhWE7hg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index f4896ec..96f5be7 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "prettier": "^3.6.2", "rimraf": "^6.0.1", "semantic-release": "^24.0.0", - "ts-jest": "^29.4.2", + "ts-jest": "^29.4.3", "ts-node": "^10.9.2", "typescript": "^5.9.2" } diff --git a/src/cli.ts b/src/cli.ts index cc4e25a..d416602 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,10 +1,11 @@ #!/usr/bin/env node -import { Command } from './types.js'; -import { log } from './utils/log.js'; -import { handleResetHooks } from './commands/resetHooks.js'; +import { handleInit } from './commands/init.js'; import { handleInstall } from './commands/install.js'; +import { handleResetHooks } from './commands/resetHooks.js'; import { handleUninstall } from './commands/uninstall.js'; -import { handleInit } from './commands/init.js'; +import { Command } from './types.js'; +import { isGitRepository } from './utils/git.js'; +import { log } from './utils/log.js'; function printUsage(): void { log.info('Usage: volbrene-git-hooks \n'); @@ -19,6 +20,12 @@ function printUsage(): void { const [, , ...argv] = process.argv; const command = (argv[0] || 'install') as Command; + if (!isGitRepository()) { + log.info('Not a git repository, skipping...'); + + process.exit(0); + } + switch (command) { case 'init': handleInit(); diff --git a/src/commands/install.ts b/src/commands/install.ts index 8f40dfb..3665375 100644 --- a/src/commands/install.ts +++ b/src/commands/install.ts @@ -2,9 +2,9 @@ import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { assertGitRepo, getHooksPath, resolveRepoRoot } from '../utils/git.js'; -import { log } from '../utils/log.js'; import { fail } from '../utils/errors.js'; +import { getHooksPath, resolveRepoRoot } from '../utils/git.js'; +import { log } from '../utils/log.js'; /** * @@ -61,8 +61,6 @@ function addGitHook(hookName: string, sourceDir: string, targetDir: string): voi * @returns void */ export function handleInstall(): void { - assertGitRepo(); - log.step('Git Hooks Setup'); // Resolve repo root diff --git a/src/commands/resetHooks.ts b/src/commands/resetHooks.ts index 353654a..f622f05 100644 --- a/src/commands/resetHooks.ts +++ b/src/commands/resetHooks.ts @@ -1,13 +1,11 @@ -import { assertGitRepo, getHooksPath, resetHooksPath } from '../utils/git.js'; -import { log } from '../utils/log.js'; import { fail } from '../utils/errors.js'; +import { getHooksPath, resetHooksPath } from '../utils/git.js'; +import { log } from '../utils/log.js'; /** * Handles the 'reset-hooks' command. */ export function handleResetHooks(): void { - assertGitRepo(); - log.step('Resetting core.hooksPath to .git/hooks...'); try { diff --git a/src/commands/uninstall.ts b/src/commands/uninstall.ts index 1a2899c..0c8ebb0 100644 --- a/src/commands/uninstall.ts +++ b/src/commands/uninstall.ts @@ -1,13 +1,11 @@ -import { assertGitRepo, removeHooksDirAndUnset } from '../utils/git.js'; -import { log } from '../utils/log.js'; import { fail } from '../utils/errors.js'; +import { removeHooksDirAndUnset } from '../utils/git.js'; +import { log } from '../utils/log.js'; /** * Handles the 'uninstall' command. */ export function handleUninstall(): void { - assertGitRepo(); - log.link('Uninstalling hooks...'); try { diff --git a/src/utils/git.ts b/src/utils/git.ts index 4d74ab2..90ef919 100644 --- a/src/utils/git.ts +++ b/src/utils/git.ts @@ -1,15 +1,16 @@ import fs from 'node:fs'; -import { sh, shGetOutput } from './exec.js'; -import { fail } from './errors.js'; import path from 'node:path'; +import { sh, shGetOutput } from './exec.js'; /** Ensure we are inside a git repository (cheap sanity check). */ -export function assertGitRepo(): void { +export function isGitRepository(): boolean { try { shGetOutput('git rev-parse --is-inside-work-tree'); } catch (e) { - fail(e, 'Not a git repository'); + return false; } + + return true; } /** diff --git a/tests/cli.test.ts b/tests/cli.test.ts index 246c63d..07a34f1 100644 --- a/tests/cli.test.ts +++ b/tests/cli.test.ts @@ -34,6 +34,14 @@ function runCLI(args: string[], cwd: string): string { } describe('volbrene-git-hooks CLI', () => { + test('should print a warning and skip execution when no git repository is present', () => { + const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'pcm-')); + + const out = runCLI([''], cwd); + + expect(out).toMatch(/Not a git repository,/); + }); + test('init writes prepare script into package.json (no install)', () => { const { cwd } = setupGitRepo();