Skip to content

Commit

Permalink
✨ Implement hook abort on amend (#510)
Browse files Browse the repository at this point in the history
This reverts commit caf87bd and
implement a fix for missing second hook param #509
  • Loading branch information
Jeremie-Chauvel authored Dec 19, 2020
1 parent e891ad3 commit 5b728e5
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 4 deletions.
4 changes: 2 additions & 2 deletions src/commands/commit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import getEmojis from '../../utils/getEmojis'
import prompts from './prompts'
import withHook, {
registerHookInterruptionHandler,
cancelIfRebasing
cancelIfNeeded
} from './withHook'
import withClient from './withClient'

Expand All @@ -14,7 +14,7 @@ export type CommitMode = 'client' | 'hook'
const commit = (mode: CommitMode) => {
if (mode === 'hook') {
registerHookInterruptionHandler()
return cancelIfRebasing().then(() => promptAndCommit(mode))
return cancelIfNeeded().then(() => promptAndCommit(mode))
}

return promptAndCommit(mode)
Expand Down
19 changes: 19 additions & 0 deletions src/commands/commit/withHook/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,23 @@ export const cancelIfRebasing = () =>
}
)

export const COMMIT_MESSAGE_SOURCE = 4

export const cancelIfAmending = () =>
new Promise<void>((resolve) => {
/*
from https://git-scm.com/docs/githooks#_prepare_commit_msg
the commit message source is passed as second argument and corresponding 4 for gitmoji-cli
`gitmoji --hook $1 $2`
*/
const commitMessageSource: ?string = process.argv[COMMIT_MESSAGE_SOURCE]
if (commitMessageSource && commitMessageSource.startsWith('commit')) {
process.exit(0)
}
resolve()
})

// I avoid Promise.all to avoid race condition in future cancel callbacks
export const cancelIfNeeded = () => cancelIfAmending().then(cancelIfRebasing)

export default withHook
2 changes: 1 addition & 1 deletion src/commands/hook/hook.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const HOOK: Object = {
PATH: '/hooks/prepare-commit-msg',
CONTENTS:
'#!/bin/sh\n# gitmoji as a commit hook\n' +
'exec < /dev/tty\ngitmoji --hook $1\n'
'exec < /dev/tty\ngitmoji --hook $1 $2\n'
}

export default HOOK
2 changes: 1 addition & 1 deletion test/commands/__snapshots__/hook.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Object {
"CONTENTS": "#!/bin/sh
# gitmoji as a commit hook
exec < /dev/tty
gitmoji --hook $1
gitmoji --hook $1 $2
",
"PATH": "/hooks/prepare-commit-msg",
"PERMISSIONS": 509,
Expand Down
27 changes: 27 additions & 0 deletions test/commands/commit.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import commit from '../../src/commands/commit'
import guard from '../../src/commands/commit/guard'
import prompts from '../../src/commands/commit/prompts'
import * as stubs from './stubs'
import { COMMIT_MESSAGE_SOURCE } from '../../src/commands/commit/withHook/index'

jest.mock('../../src/utils/getDefaultCommitContent')
jest.mock('../../src/utils/getEmojis')
Expand Down Expand Up @@ -129,6 +130,7 @@ describe('commit command', () => {
getEmojis.mockResolvedValue(stubs.gitmojis)
mockProcess.mockProcessExit()
process.argv[3] = stubs.argv
process.argv[COMMIT_MESSAGE_SOURCE] = undefined
getDefaultCommitContent.mockReturnValueOnce(
stubs.emptyDefaultCommitContent
)
Expand Down Expand Up @@ -158,6 +160,7 @@ describe('commit command', () => {
getEmojis.mockResolvedValue(stubs.gitmojis)
mockProcess.mockProcessExit()
process.argv[3] = stubs.argv
process.argv[COMMIT_MESSAGE_SOURCE] = stubs.commitSource
getDefaultCommitContent.mockReturnValueOnce(
stubs.emptyDefaultCommitContent
)
Expand Down Expand Up @@ -185,6 +188,7 @@ describe('commit command', () => {
when the hook mode detect that the user is rebasing. (Simulated to not kill the tests)
Simulation needed because if we just mock process exit, then the code execution resume in the test.
*/
process.argv[COMMIT_MESSAGE_SOURCE] = stubs.commitSource
mockProcess.mockProcessExit(new Error('ProcessExit0'))
execa.mockReturnValueOnce(Promise.resolve(stubs.gitAbsoluteDir))
// mock that we found one of the rebase trigger (file existence in .git)
Expand All @@ -200,6 +204,28 @@ describe('commit command', () => {
})
})

describe('when amending', () => {
it('should cancel the hook', async () => {
/*
Use an exception to suspend code execution to simulate the process.exit triggered
when the hook mode detect that the user is rebasing. (Simulated to not kill the tests)
Simulation needed because if we just mock process exit, then the code execution resume in the test.
*/
mockProcess.mockProcessExit(new Error('ProcessExit0'))
execa.mockReturnValueOnce(Promise.resolve(stubs.gitAbsoluteDir))
// mock that we are amending
process.argv[COMMIT_MESSAGE_SOURCE] = 'commit sha123'

try {
await commit('hook')
} catch (e) {
expect(e.message).toMatch('ProcessExit0')
}

expect(process.exit).toHaveBeenCalledWith(0)
})
})

describe('when receiving a signal interrupt', () => {
it('should call process.exit(0)', async () => {
console.warn = jest.fn()
Expand All @@ -224,6 +250,7 @@ describe('commit command', () => {
// Use an exception to suspend code execution to simulate process.exit
mockProcess.mockProcessExit(new Error('SIGINT'))
process.argv[3] = stubs.argv
process.argv[COMMIT_MESSAGE_SOURCE] = stubs.commitSource

getDefaultCommitContent.mockReturnValueOnce(
stubs.emptyDefaultCommitContent
Expand Down
1 change: 1 addition & 0 deletions test/commands/stubs.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const clientCommitAnswersWithScope = {
export const commitResult = 'Commit result'

export const argv = 'commit'
export const commitSource = ''

export const emptyDefaultCommitContent = { title: null, message: null }

Expand Down

0 comments on commit 5b728e5

Please sign in to comment.