-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit a70e4f5
Showing
6 changed files
with
678 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# JIRA Smart Commits | ||
|
||
A Node.js git hook script to prefix commits automatically with the JIRA ticket, based on a branch name. | ||
|
||
## Usage | ||
|
||
### Installation | ||
1. Install [Husky](https://www.npmjs.com/package/husky) in your project to configure Git hooks easily | ||
``` | ||
npm install --save-dev husky | ||
``` | ||
2. Install this package in your project: | ||
``` | ||
npm install --save-dev jira-smart-commit | ||
``` | ||
3. Configure scripts in `package.json`. The script expects his first argument to be the JIRA tag of the project. | ||
4. Do your git commits like usual. If the branch was prefixed with a JIRA tag, your commit message will get prefixed with | ||
the same tag. E.g. | ||
``` | ||
Branch: TAG-411-husky-git-hooks | ||
Commit message: "Add git hooks to project" → "TAG-411 Add git hooks to project" | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
#!/usr/bin/env node | ||
|
||
const fs = require("fs"); | ||
|
||
/** | ||
* If commit message title: | ||
* - Doesn't start with Merge branch | ||
* - Doesn't start with Merge pull request | ||
* - Branch name starts with ${jiraTag}-XXX (e.g. TAG-123-branch-description) | ||
* - Branch name is not a forbidden branch (master/develop) | ||
* then prepend the JIRA issue tag to the commit message. | ||
* E.g My awesome commit -> TAG-123 My awesome commit | ||
*/ | ||
|
||
if (!process.argv[2]) { | ||
console.error("Please run this script with the JIRA ticket prefix as CLI argument (e.g. node smart-commit-msg.js SPAN)"); | ||
process.exit(1); | ||
} | ||
|
||
/** | ||
* @param {string} commitMessage | ||
* @returns {boolean} | ||
*/ | ||
const isInvalidMessage = (commitMessage) => { | ||
const startsWithMergeBranch = (commitMessage) => commitMessage.indexOf("Merge branch") === 0; | ||
const startsWithMergePR = (commitMessage) => commitMessage.indexOf("Merge pull request") === 0; | ||
return !startsWithMergeBranch(commitMessage) && !startsWithMergePR(commitMessage); | ||
}; | ||
|
||
/** | ||
* @returns {string} | ||
*/ | ||
const getBranchName = () => { | ||
const branchName = fetchBranchNameFromGit(); | ||
if (["master", "develop"].includes(branchName)) { | ||
console.error(`Hold your horses! You cannot commit directly to '${branchName}'. Please set up a PR to 'master' from GitHub and merge from there`); | ||
process.exit(1); | ||
} | ||
return branchName; | ||
}; | ||
|
||
/** | ||
* @returns {String} | ||
*/ | ||
const fetchBranchNameFromGit = () => { | ||
return require("child_process").execSync("git rev-parse --abbrev-ref HEAD", { encoding: "utf-8" }).split("\n")[0] | ||
}; | ||
|
||
/** | ||
* @param {string} branchName | ||
* @returns {boolean} | ||
*/ | ||
const getIssueTagFromBranchName = (branchName) => { | ||
const matched = branchName.match(tagMatcher); | ||
return matched && matched[0]; | ||
}; | ||
|
||
const jiraTag = process.argv[2]; | ||
const tagMatcher = new RegExp(`^${jiraTag}-\\d+`, "i"); | ||
const commitMsgFile = process.env.GIT_PARAMS; | ||
const commitMsg = fs.readFileSync(commitMsgFile, { encoding: "utf-8" }); | ||
const commitMsgTitle = commitMsg.split("\n")[0]; | ||
const branchName = getBranchName(); | ||
const issueTag = getIssueTagFromBranchName(branchName); | ||
|
||
if (issueTag && isInvalidMessage(commitMsgTitle)) { | ||
// Add the JIRA issue tag to commit message title | ||
const messageLines = commitMsg.split("\n"); | ||
messageLines[0] = `${issueTag.toUpperCase()} ${commitMsgTitle}`; | ||
fs.writeFileSync(commitMsgFile, messageLines.join("\n"), { encoding: "utf-8" }); | ||
} else { | ||
fs.writeFileSync(commitMsgFile, commitMsg, { encoding: "utf-8" }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
const sinon = require('sinon'); | ||
const chai = require('chai'); | ||
const expect = chai.expect; | ||
const proxyquire = require( 'proxyquire' ).noCallThru().noPreserveCache(); | ||
|
||
describe('index.js', () => { | ||
it('should add a prefix to my unprefixed commit message', () => { | ||
expect(executeScriptMock("SPAN", "SPAN-1-proof-of-concept", "Initial commit✨")).to.equal("SPAN-1 Initial commit✨"); | ||
expect(executeScriptMock("PROJECT", "PROJECT-3-githook-test", "Add support for githooks")).to.equal("PROJECT-3 Add support for githooks"); | ||
expect(executeScriptMock("TAG", "TAG-5032-add-readme", "Add readme to project")).to.equal("TAG-5032 Add readme to project"); | ||
}); | ||
|
||
it('should not add a prefix in merge pull requests', () => { | ||
const commitMergeMessage = "Merge branch 'develop' of github.com:jessedobbelaere/jira-smart-commit into bugfixes"; | ||
expect(executeScriptMock("SPAN", "SPAN-1-proof-of-concept", commitMergeMessage)).to.equal(commitMergeMessage); | ||
}); | ||
|
||
it('should not add a prefix if branch was not prefixed', () => { | ||
expect(executeScriptMock("TAG", "conquer-the-world-PoC", "Initial commit")).to.equal("Initial commit"); | ||
}); | ||
|
||
/** | ||
* | ||
* @param {string} jiraProjectPrefix | ||
* @param {string} gitBranchName The branch name | ||
* @param {string} commitMessage | ||
* @returns {string} | ||
*/ | ||
const executeScriptMock = (jiraProjectPrefix, gitBranchName, commitMessage) => { | ||
process.argv = ["node", "index.js", jiraProjectPrefix]; | ||
const readFileSync = sinon.stub().returns(commitMessage); | ||
const writeFileSync = sinon.stub(); | ||
|
||
// Mock the script dependencies | ||
proxyquire('./index.js', { | ||
fs: { | ||
readFileSync, | ||
writeFileSync | ||
}, | ||
child_process: { execSync: () => gitBranchName } | ||
}); | ||
|
||
// Return the prefixed commit message | ||
return writeFileSync.args[0][1]; | ||
}; | ||
}); |
Oops, something went wrong.