Skip to content

Commit

Permalink
use spawnSync and SHELL env for runner (#590)
Browse files Browse the repository at this point in the history
Co-authored-by: 刘祺 <gucong@gmail.com>
  • Loading branch information
typicode and gucong3000 committed Oct 12, 2019
1 parent fd7c5e8 commit 0aea79a
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 90 deletions.
6 changes: 2 additions & 4 deletions package.json
Expand Up @@ -11,10 +11,8 @@
},
"scripts": {
"test": "npm run lint && jest",
"_install": "node husky install",
"install": "node husky install",
"preuninstall": "node husky uninstall",
"devinstall": "npm run build && cross-env HUSKY_DEBUG=1 npm run _install -- node_modules/husky && node scripts/dev-fix-path",
"devuninstall": "npm run build && cross-env HUSKY_DEBUG=1 npm run preuninstall -- node_modules/husky",
"build": "del-cli lib && tsc",
"version": "jest -u && git add -A src/installer/__tests__/__snapshots__",
"postversion": "git push && git push --tags",
Expand All @@ -23,7 +21,7 @@
"lint": "eslint . --ext .js,.ts --ignore-path .gitignore",
"fix": "npm run lint -- --fix",
"doc": "markdown-toc -i README.md",
"_postinstall": "opencollective-postinstall || exit 0"
"postinstall": "opencollective-postinstall || exit 0"
},
"repository": {
"type": "git",
Expand Down
5 changes: 3 additions & 2 deletions scripts/install.sh
Expand Up @@ -30,7 +30,6 @@ commit() {
HUSKY_SKIP_HOOKS=$2 git commit -m "$1 msg"
}


# ---
# Setup
# ---
Expand All @@ -47,8 +46,11 @@ mv husky-*.tgz $projectDir
# Init a blank git/npm project and install husky
cd $projectDir
git init
git config user.name foo # Needed by AppVeyor
git config user.email foo@example.com # Needed by AppVeyor
npm init -y


# Create .huskyrc with skipCI: false before installing husky
cat > .huskyrc << EOL
{
Expand All @@ -63,7 +65,6 @@ npm install husky-*.tgz
# Show hook content
cat .git/hooks/commit-msg


# ---
# Tests
# ---
Expand Down
4 changes: 3 additions & 1 deletion src/installer/getScript.ts
@@ -1,5 +1,6 @@
import fs from 'fs'
import path from 'path'
import slash from 'slash'

interface Context {
createdAt: string
Expand Down Expand Up @@ -132,7 +133,8 @@ export default function(
createdAt,
homepage,
packageManager,
pathToUserPkgDir,
// Normalize path
pathToUserPkgDir: slash(pathToUserPkgDir),
pkgDirectory,
pkgHomepage,
version
Expand Down
63 changes: 27 additions & 36 deletions src/runner/__tests__/index.ts
@@ -1,19 +1,34 @@
import execa from 'execa'
import fs from 'fs'
import cp from 'child_process'
import mkdirp from 'mkdirp'
import path from 'path'
import tempy from 'tempy'
import index from '../'
import index, { Env } from '../'

let spy: jest.SpyInstance

// On AppVeyor $SHELL is not set
process.env.SHELL = process.env.SHELL || 'sh'

function getScriptPath(dir: string): string {
return path.join(dir, 'node_modules/husky/runner/index.js')
}

function expectSpawnSyncToHaveBeenCalledWith(
cwd: string,
cmd: string,
env: Env = {}
): void {
expect(cp.spawnSync).toHaveBeenCalledWith(process.env.SHELL, ['-c', cmd], {
cwd,
env: { ...process.env, ...env },
stdio: 'inherit'
})
}

describe('run', (): void => {
beforeEach((): void => {
spy = jest.spyOn(execa, 'shellSync')
spy = jest.spyOn(cp, 'spawnSync')
})

afterEach((): void => {
Expand All @@ -38,11 +53,7 @@ describe('run', (): void => {
)

const status = await index(['', getScriptPath(dir), 'pre-commit'])
expect(execa.shellSync).toHaveBeenCalledWith('echo success', {
cwd: dir,
env: {},
stdio: 'inherit'
})
expectSpawnSyncToHaveBeenCalledWith(dir, 'echo success')
expect(status).toBe(0)
})

Expand All @@ -65,11 +76,7 @@ describe('run', (): void => {
)

const status = await index(['', getScriptPath(subDir), 'pre-commit'])
expect(execa.shellSync).toHaveBeenCalledWith('echo success', {
cwd: subDir,
env: {},
stdio: 'inherit'
})
expectSpawnSyncToHaveBeenCalledWith(subDir, 'echo success')
expect(status).toBe(0)
})

Expand All @@ -88,7 +95,7 @@ describe('run', (): void => {
)

const status = await index(['', getScriptPath(dir), 'pre-commit'])
expect(execa.shellSync).not.toBeCalled()
expect(cp.spawnSync).not.toBeCalled()
expect(status).toBe(0)
})

Expand All @@ -109,11 +116,7 @@ describe('run', (): void => {
)

const status = await index(['', getScriptPath(dir), 'pre-commit'])
expect(execa.shellSync).toHaveBeenCalledWith('echo fail && exit 2', {
cwd: dir,
env: {},
stdio: 'inherit'
})
expectSpawnSyncToHaveBeenCalledWith(dir, 'echo fail && exit 2')
expect(status).toBe(2)
})

Expand All @@ -132,11 +135,7 @@ describe('run', (): void => {
)

const status = await index(['', getScriptPath(dir), 'pre-commit'])
expect(execa.shellSync).toHaveBeenCalledWith('echo success', {
cwd: dir,
env: {},
stdio: 'inherit'
})
expectSpawnSyncToHaveBeenCalledWith(dir, 'echo success')
expect(status).toBe(0)
})

Expand All @@ -160,12 +159,8 @@ describe('run', (): void => {
['', getScriptPath(dir), 'pre-push'],
(): Promise<string> => Promise.resolve('foo')
)
expect(execa.shellSync).toHaveBeenCalledWith('echo success', {
cwd: dir,
env: {
HUSKY_GIT_STDIN: 'foo'
},
stdio: 'inherit'
expectSpawnSyncToHaveBeenCalledWith(dir, 'echo success', {
HUSKY_GIT_STDIN: 'foo'
})
expect(status).toBe(0)
})
Expand All @@ -191,12 +186,8 @@ describe('run', (): void => {
'commit-msg',
'git fake param'
])
expect(execa.shellSync).toHaveBeenCalledWith('echo success', {
cwd: dir,
env: {
HUSKY_GIT_PARAMS: 'git fake param'
},
stdio: 'inherit'
expectSpawnSyncToHaveBeenCalledWith(dir, 'echo success', {
HUSKY_GIT_PARAMS: 'git fake param'
})
expect(status).toBe(0)
})
Expand Down
114 changes: 67 additions & 47 deletions src/runner/index.ts
@@ -1,4 +1,4 @@
import execa from 'execa'
import { spawnSync } from 'child_process'
import getStdin from 'get-stdin'
import path from 'path'
import readPkg from 'read-pkg'
Expand All @@ -10,6 +10,38 @@ export interface Env extends NodeJS.ProcessEnv {
HUSKY_GIT_PARAMS?: string
}

function runCmd(cwd: string, hookName: string, cmd: string, env: Env): number {
const shellPath = process.env.SHELL || 'sh'
let status

console.log(`husky > ${hookName} (node ${process.version})`)

try {
status = spawnSync(shellPath as string, ['-c', cmd], {
cwd,
env: { ...process.env, ...env },
stdio: 'inherit'
}).status
} catch {
status = 1
}

if (status !== 0) {
const noVerifyMessage = [
'commit-msg',
'pre-commit',
'pre-rebase',
'pre-push'
].includes(hookName)
? '(add --no-verify to bypass)'
: '(cannot be bypassed with --no-verify due to Git specs)'

console.log(`husky > ${hookName} hook failed ${noVerifyMessage}`)
}

return status || 0
}

/**
* @param {array} argv - process.argv
* @param {promise} getStdinFn - used for mocking only
Expand Down Expand Up @@ -43,56 +75,44 @@ export default async function run(
const oldCommand: string | undefined =
pkg && pkg.scripts && pkg.scripts[hookName.replace('-', '')]

// Run command
try {
const env: Env = {}

if (HUSKY_GIT_PARAMS) {
env.HUSKY_GIT_PARAMS = HUSKY_GIT_PARAMS
}
// Add HUSKY_GIT_PARAMS to env
const env: Env = {}

if (
['pre-push', 'pre-receive', 'post-receive', 'post-rewrite'].includes(
hookName
)
) {
// Wait for stdin
env.HUSKY_GIT_STDIN = await getStdinFn()
}
if (HUSKY_GIT_PARAMS) {
env.HUSKY_GIT_PARAMS = HUSKY_GIT_PARAMS
}

if (oldCommand) {
console.log()
console.log(
`Warning: Setting ${hookName} script in package.json > scripts will be deprecated`
)
console.log(
`Please move it to husky.hooks in package.json, a .huskyrc file, or a husky.config.js file`
)
console.log(
`Or run ./node_modules/.bin/husky-upgrade for automatic update`
)
console.log()
console.log(`See https://github.com/typicode/husky for usage`)
console.log()
}
// Read stdin
if (
['pre-push', 'pre-receive', 'post-receive', 'post-rewrite'].includes(
hookName
)
) {
// Add HUSKY_GIT_STDIN to env
env.HUSKY_GIT_STDIN = await getStdinFn()
}

if (command || oldCommand) {
console.log(`husky > ${hookName} (node ${process.version})`)
execa.shellSync(command || oldCommand, { cwd, env, stdio: 'inherit' })
}
if (oldCommand) {
console.log()
console.log(
`Warning: Setting ${hookName} script in package.json > scripts will be deprecated`
)
console.log(
`Please move it to husky.hooks in package.json, a .huskyrc file, or a husky.config.js file`
)
console.log(`Or run ./node_modules/.bin/husky-upgrade for automatic update`)
console.log()
console.log(`See https://github.com/typicode/husky for usage`)
console.log()
}

return 0
} catch (err) {
const noVerifyMessage = [
'commit-msg',
'pre-commit',
'pre-rebase',
'pre-push'
].includes(hookName)
? '(add --no-verify to bypass)'
: '(cannot be bypassed with --no-verify due to Git specs)'
if (command) {
return runCmd(cwd, hookName, command, env)
}

console.log(`husky > ${hookName} hook failed ${noVerifyMessage}`)
return err.code
if (oldCommand) {
return runCmd(cwd, hookName, oldCommand, env)
}

return 0
}

0 comments on commit 0aea79a

Please sign in to comment.