From 27df561f3e26f06f5da8d951b9886aee6b7c34c0 Mon Sep 17 00:00:00 2001 From: typicode Date: Tue, 21 Jan 2020 15:18:14 +0100 Subject: [PATCH] Refactor hooks (#654) --- run.js => bin/run.js | 4 +- package.json | 2 +- scripts/test-install.sh | 2 +- sh/husky.sh | 88 ++++++++++ src/__tests__/getConf.ts | 2 +- src/getConf.ts | 2 +- .../{getScript.ts.snap => scripts.ts.snap} | 71 +++++--- src/installer/__tests__/getScript.ts | 9 -- src/installer/__tests__/index.ts | 22 ++- src/installer/__tests__/scripts.ts | 25 +++ src/installer/getBanner.ts | 16 ++ src/installer/getScript.ts | 153 ------------------ src/installer/hooks.ts | 107 ++++++++++++ src/installer/index.ts | 111 ++----------- src/installer/is.ts | 2 +- src/installer/localScript.ts | 30 ++++ src/installer/mainScript.ts | 22 +++ src/runner/index.ts | 2 +- 18 files changed, 373 insertions(+), 297 deletions(-) rename run.js => bin/run.js (83%) create mode 100644 sh/husky.sh rename src/installer/__tests__/__snapshots__/{getScript.ts.snap => scripts.ts.snap} (55%) delete mode 100644 src/installer/__tests__/getScript.ts create mode 100644 src/installer/__tests__/scripts.ts create mode 100644 src/installer/getBanner.ts delete mode 100644 src/installer/getScript.ts create mode 100644 src/installer/hooks.ts create mode 100644 src/installer/localScript.ts create mode 100644 src/installer/mainScript.ts diff --git a/run.js b/bin/run.js similarity index 83% rename from run.js rename to bin/run.js index 1be71b794..2eb0bf4be 100755 --- a/run.js +++ b/bin/run.js @@ -1,7 +1,7 @@ #!/usr/bin/env node /* eslint-disable @typescript-eslint/no-var-requires */ const pleaseUpgradeNode = require('please-upgrade-node') -const pkg = require('./package.json') +const pkg = require('../package.json') // Node version isn't supported, skip pleaseUpgradeNode(pkg, { @@ -11,4 +11,4 @@ pleaseUpgradeNode(pkg, { }) // Node version is supported, continue -require('./lib/runner/bin') +require('../lib/runner/bin') diff --git a/package.json b/package.json index b4edeafae..36f708d70 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "4.0.10", "description": "Prevents bad commit or push (git hooks, pre-commit/precommit, pre-push/prepush, post-merge/postmerge and all that stuff...)", "bin": { - "husky-run": "./run.js", + "husky-run": "./bin/run.js", "husky-upgrade": "./lib/upgrader/bin.js" }, "engines": { diff --git a/scripts/test-install.sh b/scripts/test-install.sh index a193366ef..e0a05b7a8 100644 --- a/scripts/test-install.sh +++ b/scripts/test-install.sh @@ -8,7 +8,7 @@ set -e # Variables # --- -HUSKY_DEBUG=1 +export HUSKY_DEBUG=1 projectDir=/tmp/husky-project hookParamsFile=hook-params diff --git a/sh/husky.sh b/sh/husky.sh new file mode 100644 index 000000000..7ee45d425 --- /dev/null +++ b/sh/husky.sh @@ -0,0 +1,88 @@ +debug () { + if [ "$HUSKY_DEBUG" = "true" ] || [ "$HUSKY_DEBUG" = "1" ]; then + echo "husky:debug $1" + fi +} + +command_exists () { + command -v "$1" >/dev/null 2>&1 +} + +run_command () { + if command_exists "$1"; then + "$@" husky-run $hookName "$gitParams" + + exitCode="$?" + debug "$* husky-run exited with $exitCode exit code" + + if [ $exitCode -eq 127 ]; then + echo "Can't find Husky, skipping $hookName hook" + echo "You can reinstall it using 'npm install husky --save-dev' or delete this hook" + else + exit $exitCode + fi + + else + echo "Can't find $1 in PATH: $PATH" + echo "Skipping $hookName hook" + exit 0 + fi +} + +hookIsDefined () { + grep -qs $hookName \ + package.json \ + .huskyrc \ + .huskyrc.json \ + .huskyrc.yaml \ + .huskyrc.yml \ + .huskyrc.js \ + husky.config.js +} + +huskyVersion="0.0.0" +gitParams="$*" +hookName="$(basename "$0")" + +debug "husky v$huskyVersion - $hookName" + +# Skip if HUSKY_SKIP_HOOKS is set +if [ "$HUSKY_SKIP_HOOKS" = "true" ] || [ "$HUSKY_SKIP_HOOKS" = "1" ]; then + debug "HUSKY_SKIP_HOOKS is set to $HUSKY_SKIP_HOOKS, skipping hook" + exit 0 +fi + +# Source user var and change directory +. "$(dirname $0)/husky.local.sh" +debug "Current working directory is $(pwd)" + +# Skip fast if hookName is not defined +if ! hookIsDefined; then + debug "$hookName config not found, skip" + exit 0 +fi + +# Source user ~/.huskyrc +if [ -f ~/.huskyrc ]; then + debug "source ~/.huskyrc" + . ~/.huskyrc +fi + +# Set HUSKY_GIT_STDIN from stdin +case $hookName in + "pre-push"|"pre-receive"|"post-receive"|"post-rewrite") + export HUSKY_GIT_STDIN="$(cat)";; +esac + +# Windows 10, Git Bash and Yarn 1 installer +if command_exists winpty && test -t 1; then + exec < /dev/tty +fi + +# Run husky-run with the package manager used to install Husky +case $packageManager in + "npm") run_command npx --no-install;; + "pnpm") run_command pnpx --no-install;; + "yarn") run_command yarn run --silent;; + "*") echo "Unknown package manager: $packageManager"; exit 0;; +esac diff --git a/src/__tests__/getConf.ts b/src/__tests__/getConf.ts index 54e210dd7..e494f0ced 100644 --- a/src/__tests__/getConf.ts +++ b/src/__tests__/getConf.ts @@ -1,7 +1,7 @@ import fs from 'fs' import path from 'path' import tempy from 'tempy' -import getConf from '../getConf' +import { getConf } from '../getConf' const testConf = { husky: { foo: 'bar' } } diff --git a/src/getConf.ts b/src/getConf.ts index 35b71005a..6d4ad9837 100644 --- a/src/getConf.ts +++ b/src/getConf.ts @@ -5,7 +5,7 @@ interface Conf { hooks?: { [key: string]: string } } -export default function getConf(dir: string): Conf { +export function getConf(dir: string): Conf { const explorer = cosmiconfigSync('husky') const { config = {} } = explorer.search(dir) || {} diff --git a/src/installer/__tests__/__snapshots__/getScript.ts.snap b/src/installer/__tests__/__snapshots__/scripts.ts.snap similarity index 55% rename from src/installer/__tests__/__snapshots__/getScript.ts.snap rename to src/installer/__tests__/__snapshots__/scripts.ts.snap index 884752f44..d0ec77983 100644 --- a/src/installer/__tests__/__snapshots__/getScript.ts.snap +++ b/src/installer/__tests__/__snapshots__/scripts.ts.snap @@ -4,17 +4,29 @@ exports[`hookScript should match snapshot 1`] = ` "#!/bin/sh # husky -# Hook created by Husky v4.0.10 (https://github.com/typicode/husky#readme) +# Created by Husky v4.0.10 (https://github.com/typicode/husky#readme) +# At: +# From: /home/typicode/projects/foo-package (https://github.com/foo/foo-package) + +. \\"$(dirname \\"$0\\")/husky.sh\\" +" +`; + +exports[`localScript should match snapshot 1`] = ` +"# Created by Husky v4.0.10 (https://github.com/typicode/husky#readme) # At: # From: /home/typicode/projects/foo-package (https://github.com/foo/foo-package) -# With: npm -gitRoot=\\"$(git rev-parse --show-toplevel)\\" -gitParams=\\"$*\\" -hookName=\`basename \\"$0\\"\` packageManager=npm +cd \\".\\" +" +`; -debug() { +exports[`mainScript should match snapshot 1`] = ` +"# Created by Husky v4.0.10 (https://github.com/typicode/husky#readme) +# At: +# From: /home/typicode/projects/foo-package (https://github.com/foo/foo-package) +debug () { if [ \\"$HUSKY_DEBUG\\" = \\"true\\" ] || [ \\"$HUSKY_DEBUG\\" = \\"1\\" ]; then echo \\"husky:debug $1\\" fi @@ -45,38 +57,57 @@ run_command () { fi } -debug \\"husky v4.0.10 (created at )\\" -debug \\"$hookName hook started\\" -debug \\"Current working directory is \`pwd\`\\" - -if [ -f ~/.huskyrc ]; then - debug \\"source ~/.huskyrc\\" - . ~/.huskyrc -fi +hookIsDefined () { + grep -qs $hookName \\\\ + package.json \\\\ + .huskyrc \\\\ + .huskyrc.json \\\\ + .huskyrc.yaml \\\\ + .huskyrc.yml \\\\ + .huskyrc.js \\\\ + husky.config.js +} -if [ -f \\"$gitRoot\\"/.huskyrc.local ]; then - debug \\"source $gitRoot/.huskyrc.local\\" - . \\"$gitRoot\\"/.huskyrc.local -fi +huskyVersion=\\"4.0.10\\" +gitParams=\\"$*\\" +hookName=\\"$(basename \\"$0\\")\\" -debug \\"$hookName hook started\\" +debug \\"husky v$huskyVersion - $hookName\\" +# Skip if HUSKY_SKIP_HOOKS is set if [ \\"$HUSKY_SKIP_HOOKS\\" = \\"true\\" ] || [ \\"$HUSKY_SKIP_HOOKS\\" = \\"1\\" ]; then debug \\"HUSKY_SKIP_HOOKS is set to $HUSKY_SKIP_HOOKS, skipping hook\\" exit 0 fi -cd \\".\\" +# Source user var and change directory +. \\"$(dirname $0)/husky.local.sh\\" +debug \\"Current working directory is $(pwd)\\" + +# Skip fast if hookName is not defined +if ! hookIsDefined; then + debug \\"$hookName config not found, skip\\" + exit 0 +fi + +# Source user ~/.huskyrc +if [ -f ~/.huskyrc ]; then + debug \\"source ~/.huskyrc\\" + . ~/.huskyrc +fi +# Set HUSKY_GIT_STDIN from stdin case $hookName in \\"pre-push\\"|\\"pre-receive\\"|\\"post-receive\\"|\\"post-rewrite\\") export HUSKY_GIT_STDIN=\\"$(cat)\\";; esac +# Windows 10, Git Bash and Yarn 1 installer if command_exists winpty && test -t 1; then exec < /dev/tty fi +# Run husky-run with the package manager used to install Husky case $packageManager in \\"npm\\") run_command npx --no-install;; \\"pnpm\\") run_command pnpx --no-install;; diff --git a/src/installer/__tests__/getScript.ts b/src/installer/__tests__/getScript.ts deleted file mode 100644 index 4cef2f3e7..000000000 --- a/src/installer/__tests__/getScript.ts +++ /dev/null @@ -1,9 +0,0 @@ -import './__env__' -import getScript from '../getScript' - -describe('hookScript', (): void => { - it('should match snapshot', (): void => { - const script = getScript({ relativeUserPkgDir: '.', pmName: 'npm' }) - expect(script).toMatchSnapshot() - }) -}) diff --git a/src/installer/__tests__/index.ts b/src/installer/__tests__/index.ts index bb10e4d36..1083edb33 100644 --- a/src/installer/__tests__/index.ts +++ b/src/installer/__tests__/index.ts @@ -5,7 +5,7 @@ import path from 'path' import tempy from 'tempy' import './__env__' import * as installer from '../' -import { huskyIdentifier } from '../getScript' +import { huskyIdentifier } from '../hooks' // RandomId to verify that scripts get updated const randomId = Math.random().toString() @@ -111,12 +111,16 @@ describe('install', (): void => { writeFile('package.json', pkg) install() + expect(exists('.git/hooks/husky.sh')).toBeTruthy() + expect(exists('.git/hooks/husky.local.sh')).toBeTruthy() expectHookToExist('.git/hooks/pre-commit') - const hook = readFile('.git/hooks/pre-commit') - expect(hook).toMatch('cd "."') + const localScript = readFile('.git/hooks/husky.local.sh') + expect(localScript).toMatch('cd "."') uninstall() + expect(exists('.git/hooks/husky.sh')).toBeFalsy() + expect(exists('.git/hooks/husky.local.sh')).toBeFalsy() expect(exists('.git/hooks/pre-commit')).toBeFalsy() }) @@ -166,11 +170,13 @@ describe('install', (): void => { writeFile('A/B/package.json', pkg) install({ relativeUserPkgDir, userPkgDir: relativeUserPkgDir }) - const hook = readFile('.git/hooks/pre-commit') + const localScript = readFile('.git/hooks/husky.local.sh') - expect(hook).toMatch('cd "A/B/"') + expect(localScript).toMatch('cd "A/B/"') + expectHookToExist('.git/hooks/pre-commit') uninstall({ userPkgDir: relativeUserPkgDir }) + expect(exists('.git/hooks/husky.local.sh')).toBeFalsy() expect(exists('.git/hooks/pre-commit')).toBeFalsy() }) @@ -185,11 +191,13 @@ describe('install', (): void => { gitCommonDir, userPkgDir }) - const hook = readFile('.git/modules/A/B/hooks/pre-commit') + const localScript = readFile('.git/modules/A/B/hooks/husky.local.sh') - expect(hook).toMatch('cd "."') + expect(localScript).toMatch('cd "."') + expectHookToExist('.git/modules/A/B/hooks/pre-commit') uninstall({ gitCommonDir, userPkgDir }) + expect(exists('.git/modules/A/B/hooks/husky.local.sh')).toBeFalsy() expect(exists('.git/modules/A/B/hooks/pre-commit')).toBeFalsy() }) diff --git a/src/installer/__tests__/scripts.ts b/src/installer/__tests__/scripts.ts new file mode 100644 index 000000000..42807a13c --- /dev/null +++ b/src/installer/__tests__/scripts.ts @@ -0,0 +1,25 @@ +import './__env__' +import { getHookScript } from '../hooks' +import { getLocalScript } from '../localScript' +import { getMainScript } from '../mainScript' + +describe('hookScript', (): void => { + it('should match snapshot', (): void => { + const script = getHookScript() + expect(script).toMatchSnapshot() + }) +}) + +describe('localScript', (): void => { + it('should match snapshot', (): void => { + const script = getLocalScript('npm', '.') + expect(script).toMatchSnapshot() + }) +}) + +describe('mainScript', (): void => { + it('should match snapshot', (): void => { + const script = getMainScript() + expect(script).toMatchSnapshot() + }) +}) diff --git a/src/installer/getBanner.ts b/src/installer/getBanner.ts new file mode 100644 index 000000000..1f342a695 --- /dev/null +++ b/src/installer/getBanner.ts @@ -0,0 +1,16 @@ +import path = require('path') +import { readPkg } from '../read-pkg' + +export function getBanner(): string { + const pkgHomepage = process.env.npm_package_homepage + const pkgDirectory = process.env.PWD + + const { homepage: huskyHomepage, version: huskyVersion } = readPkg( + path.join(__dirname, '../..') + ) + + const createdAt = new Date().toLocaleString() + return `# Created by Husky v${huskyVersion} (${huskyHomepage}) +# At: ${createdAt} +# From: ${pkgDirectory} (${pkgHomepage})` +} diff --git a/src/installer/getScript.ts b/src/installer/getScript.ts deleted file mode 100644 index e23b54af8..000000000 --- a/src/installer/getScript.ts +++ /dev/null @@ -1,153 +0,0 @@ -import path from 'path' -import slash from 'slash' -import { readPkg } from '../read-pkg' - -interface Context { - createdAt: string - huskyHomepage?: string - huskyVersion?: string - pkgDirectory?: string - pkgHomepage?: string - pmName: string - relativeUserPkgDir: string -} - -// Used to identify scripts created by Husky -export const huskyIdentifier = '# husky' - -// Experimental -const huskyrc = '.huskyrc' - -// Render script -const render = ({ - createdAt, - huskyHomepage, - huskyVersion, - pkgDirectory, - pkgHomepage, - pmName, - relativeUserPkgDir -}: Context): string => `#!/bin/sh -${huskyIdentifier} - -# Hook created by Husky v${huskyVersion} (${huskyHomepage}) -# At: ${createdAt} -# From: ${pkgDirectory} (${pkgHomepage}) -# With: ${pmName} - -gitRoot="$(git rev-parse --show-toplevel)" -gitParams="$*" -hookName=\`basename "$0"\` -packageManager=${pmName} - -debug() { - if [ "$HUSKY_DEBUG" = "true" ] || [ "$HUSKY_DEBUG" = "1" ]; then - echo "husky:debug $1" - fi -} - -command_exists () { - command -v "$1" >/dev/null 2>&1 -} - -run_command () { - if command_exists "$1"; then - "$@" husky-run $hookName "$gitParams" - - exitCode="$?" - debug "$* husky-run exited with $exitCode exit code" - - if [ $exitCode -eq 127 ]; then - echo "Can't find Husky, skipping $hookName hook" - echo "You can reinstall it using 'npm install husky --save-dev' or delete this hook" - else - exit $exitCode - fi - - else - echo "Can't find $1 in PATH: $PATH" - echo "Skipping $hookName hook" - exit 0 - fi -} - -debug "husky v${huskyVersion} (created at ${createdAt})" -debug "$hookName hook started" -debug "Current working directory is \`pwd\`" - -if [ -f ~/${huskyrc} ]; then - debug "source ~/${huskyrc}" - . ~/${huskyrc} -fi - -if [ -f "$gitRoot"/${huskyrc}.local ]; then - debug "source $gitRoot/${huskyrc}.local" - . "$gitRoot"/${huskyrc}.local -fi - -debug "$hookName hook started" - -if [ "$HUSKY_SKIP_HOOKS" = "true" ] || [ "$HUSKY_SKIP_HOOKS" = "1" ]; then - debug "HUSKY_SKIP_HOOKS is set to $HUSKY_SKIP_HOOKS, skipping hook" - exit 0 -fi - -cd "${relativeUserPkgDir}" - -case $hookName in - "pre-push"|"pre-receive"|"post-receive"|"post-rewrite") - export HUSKY_GIT_STDIN="$(cat)";; -esac - -if command_exists winpty && test -t 1; then - exec < /dev/tty -fi - -case $packageManager in - "npm") run_command npx --no-install;; - "pnpm") run_command pnpx --no-install;; - "yarn") run_command yarn run --silent;; - "*") echo "Unknown package manager: $packageManager"; exit 0;; -esac -` - -/** - * @param {string} relativeUserPkgDir - relative path from git dir to dir containing user package.json - * @param {string} packageManager - e.g. npm, pnpm or yarn - * @returns {string} script - */ -export default function({ - relativeUserPkgDir, - pmName -}: { - relativeUserPkgDir: string - pmName: string -}): string { - const pkgHomepage = process.env.npm_package_homepage - const pkgDirectory = process.env.PWD - - const { homepage: huskyHomepage, version: huskyVersion } = readPkg( - path.join(__dirname, '../..') - ) - - const createdAt = new Date().toLocaleString() - - if (!['npm', 'pnpm', 'yarn'].includes(pmName)) { - throw new Error( - `Unknown package manager: ${pmName} (npm_config_user_agent: ${process.env.npm_config_user_agent})` - ) - } - - const normalizedPath = slash(relativeUserPkgDir) - - // Render script - return render({ - createdAt, - huskyHomepage, - huskyVersion, - relativeUserPkgDir: normalizedPath, - pkgDirectory, - pkgHomepage, - pmName - }) -} diff --git a/src/installer/hooks.ts b/src/installer/hooks.ts new file mode 100644 index 000000000..1e70d4333 --- /dev/null +++ b/src/installer/hooks.ts @@ -0,0 +1,107 @@ +import fs = require('fs') +import path = require('path') +import { isGhooks, isPreCommit, isHusky, isYorkie } from './is' +import { getBanner } from './getBanner' + +export const huskyIdentifier = '# husky' + +export function getHookScript(): string { + return `#!/bin/sh +${huskyIdentifier} + +${getBanner()} + +. "$(dirname "$0")/husky.sh" +` +} + +const hookList = [ + 'applypatch-msg', + 'pre-applypatch', + 'post-applypatch', + 'pre-commit', + 'pre-merge-commit', + 'prepare-commit-msg', + 'commit-msg', + 'post-commit', + 'pre-rebase', + 'post-checkout', + 'post-merge', + 'pre-push', + 'pre-receive', + 'update', + 'post-receive', + 'post-update', + 'push-to-checkout', + 'pre-auto-gc', + 'post-rewrite', + 'sendemail-validate' +] + +function getHooks(gitHooksDir: string): string[] { + return hookList.map((hookName: string): string => + path.join(gitHooksDir, hookName) + ) +} + +function writeHook(filename: string, script: string): void { + fs.writeFileSync(filename, script, 'utf-8') + fs.chmodSync(filename, 0o0755) +} + +function createHook(filename: string): void { + const name = path.basename(filename) + const hookScript = getHookScript() + + // Check if hook exist + if (fs.existsSync(filename)) { + const hook = fs.readFileSync(filename, 'utf-8') + + // Migrate + if (isGhooks(hook)) { + console.log(`migrating existing ghooks script: ${name}`) + return writeHook(filename, hookScript) + } + + // Migrate + if (isPreCommit(hook)) { + console.log(`migrating existing pre-commit script: ${name}`) + return writeHook(filename, hookScript) + } + + // Update + if (isHusky(hook) || isYorkie(hook)) { + return writeHook(filename, hookScript) + } + + // Skip + console.log(`skipping existing user hook: ${name}`) + return + } + + // Create hook if it doesn't exist + writeHook(filename, hookScript) +} + +export function createHooks(gitHooksDir: string): void { + getHooks(gitHooksDir).forEach(createHook) +} + +function canRemove(filename: string): boolean { + if (fs.existsSync(filename)) { + const data = fs.readFileSync(filename, 'utf-8') + return isHusky(data) + } + + return false +} + +function removeHook(filename: string): void { + fs.unlinkSync(filename) +} + +export function removeHooks(gitHooksDir: string): void { + getHooks(gitHooksDir) + .filter(canRemove) + .forEach(removeHook) +} diff --git a/src/installer/index.ts b/src/installer/index.ts index df1928065..546215fc6 100644 --- a/src/installer/index.ts +++ b/src/installer/index.ts @@ -1,92 +1,10 @@ import fs from 'fs' import path from 'path' import { debug } from '../debug' -import getConf from '../getConf' -import getScript from './getScript' -import { isGhooks, isHusky, isPreCommit, isYorkie } from './is' - -const hookList = [ - 'applypatch-msg', - 'pre-applypatch', - 'post-applypatch', - 'pre-commit', - 'pre-merge-commit', - 'prepare-commit-msg', - 'commit-msg', - 'post-commit', - 'pre-rebase', - 'post-checkout', - 'post-merge', - 'pre-push', - 'pre-receive', - 'update', - 'post-receive', - 'post-update', - 'push-to-checkout', - 'pre-auto-gc', - 'post-rewrite', - 'sendemail-validate' -] - -function writeHook(filename: string, script: string): void { - fs.writeFileSync(filename, script, 'utf-8') - fs.chmodSync(filename, 0o0755) -} - -function createHook(filename: string, script: string): void { - // Get name, used for logging - const name = path.basename(filename) - - // Check if hook exist - if (fs.existsSync(filename)) { - const hook = fs.readFileSync(filename, 'utf-8') - - // Migrate - if (isGhooks(hook)) { - console.log(`migrating existing ghooks script: ${name}`) - return writeHook(filename, script) - } - - // Migrate - if (isPreCommit(hook)) { - console.log(`migrating existing pre-commit script: ${name}`) - return writeHook(filename, script) - } - - // Update - if (isHusky(hook) || isYorkie(hook)) { - return writeHook(filename, script) - } - - // Skip - console.log(`skipping existing user hook: ${name}`) - return - } - - // Create hook if it doesn't exist - writeHook(filename, script) -} - -function createHooks(filenames: string[], script: string): void { - filenames.forEach((filename: string): void => createHook(filename, script)) -} - -function canRemove(filename: string): boolean { - if (fs.existsSync(filename)) { - const data = fs.readFileSync(filename, 'utf-8') - return isHusky(data) - } - - return false -} - -function removeHook(filename: string): void { - fs.unlinkSync(filename) -} - -function removeHooks(filenames: string[]): void { - filenames.filter(canRemove).forEach(removeHook) -} +import { getConf } from '../getConf' +import { createHooks, removeHooks } from './hooks' +import { createLocalScript, removeLocalScript } from './localScript' +import { createMainScript, removeMainScript } from './mainScript' // This prevents the case where someone would want to debug a node_module that has // husky as devDependency and run npm install from node_modules directory @@ -98,13 +16,6 @@ function getGitHooksDir(gitDir: string): string { return path.join(gitDir, 'hooks') } -function getHooks(gitDir: string): string[] { - const gitHooksDir = getGitHooksDir(gitDir) - return hookList.map((hookName: string): string => - path.join(gitHooksDir, hookName) - ) -} - export function install({ absoluteGitCommonDir, relativeUserPkgDir, @@ -141,11 +52,9 @@ export function install({ } debug(`Installing hooks in ${gitHooksDir}`) - const hooks = getHooks(absoluteGitCommonDir) - - // Prefix can be an empty string - const script = getScript({ relativeUserPkgDir, pmName }) - createHooks(hooks, script) + createHooks(gitHooksDir) + createLocalScript(gitHooksDir, pmName, relativeUserPkgDir) + createMainScript(gitHooksDir) } export function uninstall({ @@ -163,6 +72,8 @@ export function uninstall({ } // Remove hooks - const hooks = getHooks(absoluteGitCommonDir) - removeHooks(hooks) + const gitHooksDir = getGitHooksDir(absoluteGitCommonDir) + removeHooks(gitHooksDir) + removeLocalScript(gitHooksDir) + removeMainScript(gitHooksDir) } diff --git a/src/installer/is.ts b/src/installer/is.ts index 998ec387b..ab0f029e5 100644 --- a/src/installer/is.ts +++ b/src/installer/is.ts @@ -1,4 +1,4 @@ -import { huskyIdentifier } from './getScript' +import { huskyIdentifier } from './hooks' export function isHusky(data: string): boolean { // Husky v0.14 and prior used #husky as an identifier. diff --git a/src/installer/localScript.ts b/src/installer/localScript.ts new file mode 100644 index 000000000..941d56d1b --- /dev/null +++ b/src/installer/localScript.ts @@ -0,0 +1,30 @@ +import fs = require('fs') +import path = require('path') +import { getBanner } from './getBanner' + +export function getLocalScript( + pmName: string, + relativeUserPkgDir: string +): string { + return `${getBanner()} + +packageManager=${pmName} +cd "${relativeUserPkgDir}" +` +} + +export function createLocalScript( + gitHooksDir: string, + pmName: string, + relativeUserPkgDir: string +): void { + fs.writeFileSync( + path.join(gitHooksDir, 'husky.local.sh'), + getLocalScript(pmName, relativeUserPkgDir), + 'utf-8' + ) +} + +export function removeLocalScript(gitHooksDir: string): void { + fs.unlinkSync(path.join(gitHooksDir, 'husky.local.sh')) +} diff --git a/src/installer/mainScript.ts b/src/installer/mainScript.ts new file mode 100644 index 000000000..963a9a49b --- /dev/null +++ b/src/installer/mainScript.ts @@ -0,0 +1,22 @@ +import fs = require('fs') +import path = require('path') +import { getBanner } from './getBanner' +import { readPkg } from '../read-pkg' + +export function getMainScript(): string { + const pkg = readPkg(path.join(__dirname, '../..')) + + const mainScript = fs + .readFileSync(path.join(__dirname, '../../sh/husky.sh'), 'utf-8') + .replace('huskyVersion="0.0.0"', `huskyVersion="${pkg.version}"`) + + return [getBanner(), mainScript].join('\n') +} + +export function createMainScript(gitHooksDir: string): void { + fs.writeFileSync(path.join(gitHooksDir, 'husky.sh'), getMainScript(), 'utf-8') +} + +export function removeMainScript(gitHooksDir: string): void { + fs.unlinkSync(path.join(gitHooksDir, 'husky.sh')) +} diff --git a/src/runner/index.ts b/src/runner/index.ts index e2c76c7a3..fadf4008b 100644 --- a/src/runner/index.ts +++ b/src/runner/index.ts @@ -1,6 +1,6 @@ import chalk from 'chalk' import { spawnSync } from 'child_process' -import getConf from '../getConf' +import { getConf } from '../getConf' import { readPkg } from '../read-pkg' export interface Env extends NodeJS.ProcessEnv {