commitplease
Advanced tools
Comparing version 2.3.1 to 2.4.0
#!/usr/bin/env node | ||
// `commitplease-original` | ||
require( "commitplease" )( process.argv[ 2 ] ); | ||
require('commitplease') |
43
index.js
@@ -1,22 +0,23 @@ | ||
var fs = require( "fs" ), | ||
path = require( "path" ), | ||
root = path.resolve( __dirname, "../.." ), | ||
chalk = require( "chalk" ), | ||
validate = require( "./lib/validate" ), | ||
sanitize = require( "./lib/sanitize" ), | ||
options = fs.existsSync( path.join( root, "package.json" ) ) && | ||
require( path.join( root, "package.json" ) ).commitplease || {}; | ||
var fs = require('fs') | ||
var path = require('path') | ||
var root = path.resolve(__dirname, '../..') | ||
var chalk = require('chalk') | ||
var validate = require('./lib/validate') | ||
var sanitize = require('./lib/sanitize') | ||
var options = fs.existsSync(path.join(root, 'package.json')) && | ||
require(path.join(root, 'package.json')).commitplease || {} | ||
module.exports = function( messageFile ) { | ||
var message = sanitize( fs.readFileSync( messageFile ).toString() ), | ||
errors = validate( message, options ); | ||
if ( errors.length ) { | ||
console.error( "Invalid commit message, please fix the following issues:\n" ); | ||
console.error( chalk.red( "- " + errors.join( "\n- " ) ) ); | ||
console.error(); | ||
console.error( "Commit message was:" ); | ||
console.error(); | ||
console.error( chalk.green( message ) ); | ||
process.exit( 1 ); | ||
} | ||
}; | ||
;(function () { | ||
var messageFile = path.resolve(process.cwd(), '.git/COMMIT_EDITMSG') | ||
var message = sanitize(fs.readFileSync(messageFile, 'utf8').toString()) | ||
var errors = validate(message, options) | ||
if (errors.length) { | ||
console.error('Invalid commit message, please fix the following issues:\n') | ||
console.error(chalk.red('- ' + errors.join('\n- '))) | ||
console.error() | ||
console.error('Commit message was:') | ||
console.error() | ||
console.error(chalk.green(message)) | ||
process.exit(1) | ||
} | ||
}()) |
@@ -1,15 +0,15 @@ | ||
var chalk = require( "chalk" ), | ||
setup = require( "./setup" ); | ||
var chalk = require('chalk') | ||
var setup = require('./setup') | ||
if ( setup.selfmadeHook ) { | ||
console.log( "Found an existing commitplease hook, removing to update it" ); | ||
setup.destroy(); | ||
} else if ( setup.hookExists ) { | ||
console.log( chalk.red( "Detected an existing git commit-msg hook" ) ); | ||
console.log( "" ); | ||
console.log( chalk.red( "Remove it and install this package again to install commitplease properly" ) ); | ||
process.exit( 0 ); | ||
if (setup.selfmadeHook) { | ||
console.log('Found an existing commitplease hook, removing to update it') | ||
setup.destroy() | ||
} else if (setup.hookExists) { | ||
console.log(chalk.red('Detected an existing git commit-msg hook')) | ||
console.log('') | ||
console.log(chalk.red('Remove it and install this package again to install commitplease properly')) | ||
process.exit(0) | ||
} | ||
setup.create(); | ||
console.log( "Installed " + setup.hook ); | ||
setup.create() | ||
console.log('Installed ' + setup.hook) |
@@ -1,6 +0,6 @@ | ||
var scissor = /# ------------------------ >8 ------------------------[\s\S]+/; | ||
module.exports = function( value ) { | ||
return value.replace( scissor, "" ).split( /\n/ ).filter( function( line ) { | ||
return !/^#/.test( line ); | ||
} ).join( "\n" ).trim(); | ||
}; | ||
var scissor = /# ------------------------ >8 ------------------------[\s\S]+/ | ||
module.exports = function (value) { | ||
return value.replace(scissor, '').split(/\n/).filter(function (line) { | ||
return !/^#/.test(line) | ||
}).join('\n').trim() | ||
} |
@@ -1,76 +0,82 @@ | ||
var merge = require( "mout/object/merge" ), | ||
semver = require( "semver" ), | ||
subjectExceptions = /^(fixup|squash)!|^\[[^\]]+\]:/, | ||
defaults = { | ||
component: true, | ||
components: [], | ||
limits: { | ||
subject: 72, | ||
other: 80 | ||
} | ||
}; | ||
var merge = require('mout/object/merge') | ||
var semver = require('semver') | ||
var subjectExceptions = /^(fixup|squash)!|^\[[^\]]+\]:/ | ||
var defaults = { | ||
component: true, | ||
components: [], | ||
markerPattern: '^(clos|fix|resolv)(e[sd]|ing)', | ||
actionPattern: '^([Cc]los|[Ff]ix|[Rr]esolv)(e[sd]|ing)\\s+[^\\s\\d]+(\\s|$)', | ||
ticketPattern: '^(Closes|Fixes) (.*#|gh-|[A-Z]{2,}-)[0-9]+', | ||
limits: { | ||
subject: 72, | ||
other: 80 | ||
} | ||
} | ||
module.exports = function( message, options ) { | ||
options = merge( defaults, options ); | ||
module.exports = function (message, options) { | ||
options = merge(defaults, options) | ||
var errors = []; | ||
var errors = [] | ||
// Can't cancel forEach, so abuse every to stop at first comment | ||
message.split( /\n/ ).every( function( line, index ) { | ||
if ( index === 0 ) { | ||
// Can't cancel forEach, so abuse every to stop at first comment | ||
message.split(/\n/).every(function (line, index) { | ||
if (index === 0) { | ||
// Allow tag commits | ||
if (semver.valid(line)) { | ||
return | ||
} | ||
// Allow tag commits | ||
if ( semver.valid( line ) ) { | ||
return; | ||
} | ||
// Allow merge commits | ||
if (/^Merge branch/.test(line) || /^Merge [0-9a-f]+ into [0-9a-f]+/.test(line)) { | ||
return | ||
} | ||
if (line.length === 0) { | ||
errors.push('First line (subject) must not be empty') | ||
} | ||
if (line.length > options.limits.subject) { | ||
errors.push('First line (subject) must be no longer than ' + | ||
options.limits.subject + ' characters') | ||
} | ||
// Allow merge commits | ||
if ( /^Merge branch/.test( line ) || /^Merge [0-9a-f]+ into [0-9a-f]+/.test( line ) ) { | ||
return; | ||
} | ||
if ( 0 === line.length ) { | ||
errors.push( "First line (subject) must not be empty" ); | ||
} | ||
if ( line.length > options.limits.subject ) { | ||
errors.push( "First line (subject) must be no longer than " + | ||
options.limits.subject + " characters" ); | ||
} | ||
if (!subjectExceptions.test(line)) { | ||
if (options.component && line.indexOf(':') < 1) { | ||
errors.push('First line (subject) must indicate the component') | ||
} | ||
var matchedComponent = line.replace(/:.+/, '') | ||
if (options.components.length && matchedComponent && | ||
options.components.indexOf(matchedComponent) === -1) { | ||
errors.push("Component invalid, was '" + matchedComponent + | ||
"', must be one of these: " + options.components.join(', ')) | ||
} | ||
if (options.component && line.substring(line.indexOf(':') + 1).length < 1) { | ||
errors.push('First line (subject) must have a message after the component') | ||
} | ||
} | ||
} | ||
if (index === 1 && line.length > 0) { | ||
errors.push('Second line must always be empty') | ||
} | ||
if (index > 1 && line.length > options.limits.other) { | ||
errors.push('Commit message line ' + (index + 1) + ' too long: ' + | ||
line.length + ' characters, only ' + options.limits.other + | ||
' allowed. Was: ' + line.substring(0, 20) + '[...]') | ||
} | ||
if ( !subjectExceptions.test( line ) ) { | ||
if ( options.component && line.indexOf( ":" ) < 1 ) { | ||
errors.push( "First line (subject) must indicate the component" ); | ||
} | ||
var matchedComponent = line.replace( /:.+/, "" ); | ||
if ( options.components.length && matchedComponent && | ||
options.components.indexOf( matchedComponent ) === -1 ) { | ||
errors.push( "Component invalid, was '" + matchedComponent + | ||
"', must be one of these: " + options.components.join( ", " ) ); | ||
} | ||
if ( options.component && line.substring( line.indexOf( ":" ) + 1 ).length < 1 ) { | ||
errors.push( "First line (subject) must have a message after the component" ); | ||
} | ||
} | ||
var markerPattern = new RegExp(options.markerPattern, 'i') | ||
var actionPattern = new RegExp(options.actionPattern) | ||
var ticketPattern = new RegExp(options.ticketPattern) | ||
} | ||
if ( index === 1 && line.length > 0 ) { | ||
errors.push( "Second line must always be empty" ); | ||
} | ||
if ( index > 1 && line.length > options.limits.other ) { | ||
errors.push( "Commit message line " + ( index + 1 ) + " too long: " + | ||
line.length + " characters, only " + options.limits.other + | ||
" allowed. Was: " + line.substring( 0, 20 ) + "[...]" ); | ||
} | ||
// Ticket references | ||
if (index > 0 && markerPattern.test(line)) { | ||
if (!actionPattern.test(line) && !ticketPattern.test(line)) { | ||
errors.push('Invalid ticket reference, must be ' + | ||
ticketPattern + ', was: ' + line) | ||
} | ||
} | ||
return true | ||
}) | ||
// Ticket references | ||
if ( index > 0 && /^(clos|fix|resolv)(e[sd]|ing)/i.test( line ) ) { | ||
if ( !/^(Fixes|Closes)\s+[^\s\d]+(\s|$)/.test( line ) && | ||
!/^(Fixes|Closes) (.*#|gh-|[A-Z]{2,}-)[0-9]+/.test( line ) ) { | ||
errors.push( "Invalid ticket reference, must be " + | ||
"/(Fixes|Closes) (.*#|gh-|[A-Z]{2,}-)[0-9]+/, was: " + line ); | ||
} | ||
} | ||
return true; | ||
} ); | ||
return errors | ||
} | ||
return errors; | ||
}; | ||
module.exports.defaults = defaults |
{ | ||
"name": "commitplease", | ||
"version": "2.3.1", | ||
"version": "2.4.0", | ||
"description": "Validates strings as commit messages", | ||
"main": "index.js", | ||
"bin": { | ||
"commitplease": "./commit-msg-hook.js" | ||
}, | ||
"scripts": { | ||
"install": "node install", | ||
"uninstall": "node uninstall", | ||
"test": "jshint lib/ *.js && jscs lib/ *.js && nodeunit test.js" | ||
"test": "standard && babel-node --presets=es2015 -- node_modules/.bin/nodeunit test.js", | ||
"test-watch": "nodemon --exec npm run --silent test" | ||
}, | ||
@@ -26,6 +30,9 @@ "repository": { | ||
"devDependencies": { | ||
"jscs": "^2.11.0", | ||
"jshint": "^2.8.0", | ||
"babel-cli": "^6.10.1", | ||
"babel-preset-es2015": "^6.9.0", | ||
"babel-register": "^6.9.0", | ||
"mout": "^0.12.0", | ||
"nodeunit": "^0.9.1" | ||
"nodemon": "^1.9.1", | ||
"nodeunit": "^0.9.1", | ||
"standard": "^3.0.0" | ||
}, | ||
@@ -32,0 +39,0 @@ "dependencies": { |
@@ -44,2 +44,5 @@ # Commitplease | ||
components: [], | ||
markerPattern: '^(clos|fix|resolv)(e[sd]|ing)', | ||
actionPattern: '^([Cc]los|[Ff]ix|[Rr]esolv)(e[sd]|ing)\\s+[^\\s\\d]+(\\s|$)', | ||
ticketPattern: '^(Closes|Fixes) (.*#|gh-|[A-Z]{2,}-)[0-9]+', | ||
limits: { | ||
@@ -55,2 +58,11 @@ subject: 72, | ||
The following options are experimental and are subject to change: | ||
* `markerPattern`: A (intentionally loose) RegExp that indicates that the line might be a ticket reference. Is caseinsensitive. | ||
* `actionPattern`: A RegExp that makes a line marked by `markerPattern` valid even if the line does not fit `ticketPattern` | ||
* `ticketPattern`: A RegExp that detects ticket references: Closes gh-1, Fixes gh-42, WEB-451 and similar. | ||
The ticket reference match will fail only if `markerPattern` succeeds and __both__ `ticketPattern` and `actionPattern` fail. | ||
When overwriting these patterns in `package.json`, remember to escape special characters. | ||
### Customizing the bundled options | ||
@@ -88,2 +100,17 @@ | ||
When using commitplease together with [husky](https://github.com/typicode/husky), the following configuration will let husky manage all the hooks and trigger commitplease as well: | ||
```json | ||
{ | ||
"scripts": { | ||
"commitmsg": "commitplease" | ||
}, | ||
"commitplease": { | ||
"nohook": true | ||
} | ||
} | ||
``` | ||
However, since husky does not use npm in silent mode (and there is [no easy way](https://github.com/typicode/husky/pull/47) to make it [do so](https://github.com/npm/npm/issues/5452)), there will be a lot of additional output when a message fails validation. Therefore, using commitplease alone is recommended. | ||
## License | ||
@@ -90,0 +117,0 @@ Copyright Jörn Zaefferer |
96
setup.js
@@ -1,62 +0,62 @@ | ||
( function avoidSelfInstall() { | ||
var pkg = require( "./package" ), | ||
nameRegex = new RegExp( "node_modules/" + pkg.name + "$" ); | ||
if ( !nameRegex.test( process.cwd().replace( /\\/g, "/" ) ) ) { | ||
console.log( "running install inside self, no need" ); | ||
process.exit( 0 ); | ||
} | ||
}() ); | ||
;(function avoidSelfInstall () { | ||
var pkg = require('./package') | ||
var nameRegex = new RegExp('node_modules/' + pkg.name + '$') | ||
if (!nameRegex.test(process.cwd().replace(/\\/g, '/'))) { | ||
console.log('running install inside self, no need') | ||
process.exit(0) | ||
} | ||
}()) | ||
var path = require( "path" ); | ||
var fs = require( "fs" ); | ||
var root = path.resolve( __dirname, "../.." ); | ||
var git = path.resolve( root, ".git" ); | ||
var hooks = path.resolve( git, "hooks" ); | ||
var path = require('path') | ||
var fs = require('fs') | ||
var root = path.resolve(__dirname, '../..') | ||
var git = path.resolve(root, '.git') | ||
var hooks = path.resolve(git, 'hooks') | ||
var rootPackage = require( path.resolve( root, "package" ) ); | ||
if ( rootPackage.commitplease && rootPackage.commitplease.nohook ) { | ||
console.error( "package.json indicates to skip hook" ); | ||
process.exit( 0 ); | ||
var rootPackage = require(path.resolve(root, 'package')) | ||
if (rootPackage.commitplease && rootPackage.commitplease.nohook) { | ||
console.error('package.json indicates to skip hook') | ||
process.exit(0) | ||
} | ||
// Check if we are in a git repository so we can bail out early when this is not the case. | ||
if ( !fs.existsSync( git ) || !fs.lstatSync( git ).isDirectory() ) { | ||
console.error( "Could not find git repo in " + git ); | ||
process.exit( 0 ); | ||
if (!fs.existsSync(git) || !fs.lstatSync(git).isDirectory()) { | ||
console.error('Could not find git repo in ' + git) | ||
process.exit(0) | ||
} | ||
if ( !fs.existsSync( hooks ) ) { | ||
fs.mkdirSync( hooks ); | ||
if (!fs.existsSync(hooks)) { | ||
fs.mkdirSync(hooks) | ||
} | ||
var hook = path.resolve( hooks, "commit-msg" ); | ||
var hookFile = path.relative( path.resolve( hooks ), "./commit-msg-hook.js" ); | ||
var hook = path.resolve(hooks, 'commit-msg') | ||
var hookFile = path.relative(path.resolve(hooks), './commit-msg-hook.js') | ||
var context = { | ||
hook: hook, | ||
create: function() { | ||
try { | ||
fs.writeFileSync( hook, fs.readFileSync( hookFile ) ); | ||
fs.chmodSync( hook, "755" ); | ||
} catch ( e ) { | ||
if ( /EPERM/.test( e.message ) ) { | ||
console.error( "Failed to write commit-msg hook. " + | ||
"Make sure you have the necessary permissions." ); | ||
} | ||
throw e; | ||
} | ||
}, | ||
destroy: function() { | ||
fs.unlinkSync( hook ); | ||
} | ||
}; | ||
hook: hook, | ||
create: function () { | ||
try { | ||
fs.writeFileSync(hook, fs.readFileSync(hookFile)) | ||
fs.chmodSync(hook, '755') | ||
} catch (e) { | ||
if (/EPERM/.test(e.message)) { | ||
console.error('Failed to write commit-msg hook. ' + | ||
'Make sure you have the necessary permissions.') | ||
} | ||
throw e | ||
} | ||
}, | ||
destroy: function () { | ||
fs.unlinkSync(hook) | ||
} | ||
} | ||
if ( fs.existsSync( hook ) ) { | ||
context.hookExists = true; | ||
var content = fs.readFileSync( hook, "utf-8" ); | ||
if ( content && content.split( "\n" )[ 1 ] === "// commitplease-original" ) { | ||
context.selfmadeHook = true; | ||
} | ||
if (fs.existsSync(hook)) { | ||
context.hookExists = true | ||
var content = fs.readFileSync(hook, 'utf-8') | ||
if (content && content.split('\n')[ 1 ] === '// commitplease-original') { | ||
context.selfmadeHook = true | ||
} | ||
} | ||
module.exports = context; | ||
module.exports = context |
790
test.js
@@ -1,272 +0,534 @@ | ||
var validate = require( "./lib/validate" ), | ||
sanitize = require( "./lib/sanitize" ); | ||
var merge = require('mout/object/merge') | ||
var validate = require('./lib/validate') | ||
var sanitize = require('./lib/sanitize') | ||
var messageWithMultipleLines = "Component: short message\n" + | ||
"\n" + | ||
"Long description"; | ||
var profile0 = validate.defaults | ||
var profile1 = merge(validate.defaults, {component: false}) | ||
var profile2 = merge(validate.defaults, {components: ['Build', 'Legacy']}) | ||
var messageWithDiff = "Component: short message\n" + | ||
"\n" + | ||
"some text\n" + | ||
"# Long comment that has to be ignored line too long beyond 72 chars line too long beyond" + | ||
"line too long line too long line too long\n" + | ||
"more text\n" + | ||
"# ------------------------ >8 ------------------------\n" + | ||
"# Do not touch the line above.\n" + | ||
"# Everything below will be removed.\n" + | ||
"diff --git a/test.js b/test.js\n" + | ||
"index 36978ce..8edf326 100644\n" + | ||
"diff --git a/foo b/foo\n" + | ||
"- \n" + | ||
"+ }, mment that has to be ignored line too long beyond 72 chars line too long beyond"; | ||
var profile3 = merge( | ||
validate.defaults, { | ||
ticketPattern: '^(Closes|Fixes) ([a-zA-Z]{2,}-)[0-9]+' | ||
} | ||
) | ||
var testComponent = { | ||
components: [ "Test" ] | ||
}; | ||
var profiles0 = [profile0, profile1, profile3] | ||
var valid = [ | ||
{ | ||
msg: "Component: short message" | ||
}, | ||
{ | ||
msg: messageWithMultipleLines | ||
}, | ||
{ | ||
msg: "Component: short message\n" + | ||
"\n" + | ||
"Long description\n" + | ||
"\n" + | ||
"Closes #123" | ||
}, | ||
{ | ||
msg: "Component: short message\n" + | ||
"\n" + | ||
"Closes jquery/jquery-mobile#123" | ||
}, | ||
{ | ||
msg: "short message", | ||
options: { | ||
component: false | ||
} | ||
}, | ||
{ | ||
msg: "Test: short message", | ||
options: { | ||
components: [ "Test" ] | ||
} | ||
}, | ||
{ | ||
msg: "Component: message over default of 50 but below limit off 70", | ||
options: { | ||
limits: { | ||
subject: 70 | ||
} | ||
} | ||
}, | ||
{ | ||
msg: messageWithDiff | ||
}, | ||
{ | ||
msg: "v1.13.0" | ||
}, | ||
{ | ||
msg: "0.0.1" | ||
}, | ||
{ | ||
msg: "Component: Message\n#comment" | ||
}, | ||
{ | ||
msg: "Component: short message\n" + | ||
"\n" + | ||
"Fixes some bug.\n" + | ||
"Fix some other bug.\n" + | ||
"\n" + | ||
"Fixes #123" | ||
}, | ||
{ | ||
msg: "Component: short message\n" + | ||
"\n" + | ||
"Fix some bug.\n" + | ||
"\n" + | ||
"Fixes #123" | ||
}, | ||
{ | ||
msg: "Component: short message\n" + | ||
"\n" + | ||
"Fix some bug.\n" + | ||
"\n" + | ||
"Fixes WEB-1093" | ||
}, | ||
{ | ||
msg: "Merge branch 'one' into two" | ||
}, | ||
{ | ||
msg: "Merge e8d808b into f040453" | ||
}, | ||
{ | ||
msg: "fixup! for git to squash", | ||
options: testComponent | ||
}, | ||
{ | ||
msg: "squash! for git to squish", | ||
options: testComponent | ||
}, | ||
{ | ||
msg: "[fix]: whatever fix", | ||
options: testComponent | ||
}, | ||
{ | ||
msg: "[Tmp]: do this and that", | ||
options: testComponent | ||
}, | ||
{ | ||
msg: "fixedToolbar: Prevent resize" | ||
}, | ||
{ | ||
msg: "Docs:Tests: Remove legacy code & add support comments where needed\n" + | ||
"\n" + | ||
"This commits backports some changes done in the patch to the then-existing\n" + | ||
"compat branch that removed support for old browsers and added some support\n" + | ||
"comments.\n" + | ||
"\n" + | ||
"Refs 90d7cc1d8b2ea7ac75f0eacb42439349c9c73278" | ||
}, | ||
{ | ||
msg: "Support: improve support properties computation\n" + | ||
"\n" + | ||
"* Remove div from the memory if it is not needed anymore\n" + | ||
"\n" + | ||
"* Make `computeStyleTests` method a singleton\n" + | ||
"\n" + | ||
"Fixes gh-3018 Closes gh-3021" | ||
}, | ||
{ | ||
msg: "Tests: add additional test for jQuery.isPlainObject\n" + | ||
"\n" + | ||
"Ref 00575d4d8c7421c5119f181009374ff2e7736127\n" + | ||
"Also see discussion in\n" + | ||
"https://github.com/jquery/jquery/pull/2970#discussion_r54970557" | ||
}, | ||
{ | ||
msg: 'Revert "Offset: account for scroll when calculating position"\n' + | ||
"\n" + | ||
"This reverts commit 2d715940b9b6fdeed005cd006c8bf63951cf7fb2.\n" + | ||
"This commit provoked new issues: gh-2836, gh-2828.\n" + | ||
"At the meeting, we decided to revert offending commit\n" + | ||
"(in all three branches - 2.2-stable, 1.12-stable and master)\n" + | ||
"and tackle this issue in 3.x.\n" + | ||
"\n" + | ||
"Fixes gh-2828" | ||
}, | ||
{ | ||
msg: 'Revert "Attributes: Remove undocumented .toggleClass( boolean ) signature"\n' + | ||
"\n" + | ||
"This reverts commit 53f798cf4d783bb813b4d1ba97411bc752b275f3.\n" + | ||
"\n" + | ||
"- Turns out this is documented, even if not fully. Need to deprecate before removal.", | ||
options: { | ||
limits: { | ||
subject: 90, | ||
other: 90 | ||
} | ||
} | ||
}, | ||
{ | ||
msg: "Event: Separate trigger/simulate into its own module\n" + | ||
"\n" + | ||
"Fixes gh-1864\n" + | ||
"Closes gh-2692\n" + | ||
"\n" + | ||
"This also pulls the focusin/out special event into its own module, since that\n" + | ||
"depends on simulate(). NB: The ajax module triggers events pretty heavily." | ||
} | ||
]; | ||
var messages0 = [ | ||
{ | ||
msg: 'Component: short message' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Long description' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Long description\n\n' + | ||
'That spans many paragraphs' | ||
}, | ||
{ | ||
msg: 'Component: short message\n' + | ||
'#comment' | ||
}, | ||
{ | ||
msg: 'Component: short message\n' + | ||
'# comment' | ||
}, | ||
{ | ||
msg: 'Component: short message\n' + | ||
'# comment' | ||
}, | ||
{ | ||
msg: 'Component: short message\n' + | ||
'text on next line', | ||
reasons: new Map([ | ||
[profile0, [ 'Second line must always be empty' ]], | ||
[profile1, [ 'Second line must always be empty' ]], | ||
[profile3, [ 'Second line must always be empty' ]] | ||
]) | ||
}, | ||
{ | ||
msg: 'No component here, short message', | ||
accepts: [profile1], | ||
reasons: new Map([ | ||
[profile0, ['First line (subject) must indicate the component']], | ||
[profile3, ['First line (subject) must indicate the component']] | ||
]) | ||
}, | ||
{ | ||
msg: 'Build: short message', | ||
accepts: [profile2] | ||
}, | ||
{ | ||
msg: 'Test: short message', | ||
reasons: new Map([ | ||
[profile2, ["Component invalid, was 'Test', must be one of these: Build, Legacy"]] | ||
]) | ||
}, | ||
{ | ||
msg: 'Component: short message\n' + | ||
'# Long comments still have to be ignored' + | ||
'even though they are longer than 72 characters' + | ||
'notice the absense of newline character in test' | ||
}, | ||
{ | ||
msg: 'Component: short message but actually a little bit over default character limit', | ||
reasons: new Map([ | ||
[profile0, [ 'First line (subject) must be no longer than 72 characters' ]], | ||
[profile1, [ 'First line (subject) must be no longer than 72 characters' ]], | ||
[profile2, [ | ||
'First line (subject) must be no longer than 72 characters', | ||
"Component invalid, was 'Component', must be one of these: Build, Legacy" | ||
]], | ||
[profile3, [ 'First line (subject) must be no longer than 72 characters' ]] | ||
]) | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Long description is way past the 80 characters limit' + | ||
'Long description is way past the 80 characters limit', | ||
reasons: new Map([ | ||
[profile0, [ 'Commit message line 3 too long: 104 characters, only 80 allowed. Was: Long description is [...]' ]], | ||
[profile1, [ 'Commit message line 3 too long: 104 characters, only 80 allowed. Was: Long description is [...]' ]], | ||
[profile3, [ 'Commit message line 3 too long: 104 characters, only 80 allowed. Was: Long description is [...]' ]] | ||
]) | ||
}, | ||
{ | ||
msg: 'Build:', | ||
accepts: [profile1], | ||
reasons: new Map([ | ||
[profile0, [ 'First line (subject) must have a message after the component' ]], | ||
[profile2, [ | ||
"Component invalid, was 'Build:', must be one of these: Build, Legacy", | ||
'First line (subject) must have a message after the component' | ||
]], | ||
[profile3, [ 'First line (subject) must have a message after the component' ]] | ||
]) | ||
}, | ||
{ | ||
msg: 'Component0:Component1: short message\n\n' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'1. List element\n' + | ||
'2. List element\n\n' + | ||
'* List element\n' + | ||
'* List element\n\n' + | ||
'- List element\n' + | ||
'- List element\n\n' + | ||
'+ List element\n' + | ||
'+ List element' | ||
}, | ||
{ | ||
msg: 'Component: message over default of 50 but below limit of 70' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Fixes some bug.\n' + | ||
'Fix some other bug.\n' + | ||
'And fix these bugs too.\n' + | ||
'And fixes other bugs too.' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Resolves some issue.\n' + | ||
'Resolve some other issue.\n' + | ||
'And resolve these issues too.\n' + | ||
'And resolves some other issues too.' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Closes some issue.\n' + | ||
'Close some other issue.\n' + | ||
'And close these issues too.\n' + | ||
'And closes some other issues too.' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Fixes #1\n' + | ||
'Fixes #123', | ||
reasons: new Map([ | ||
[profile3, [ | ||
'Invalid ticket reference, must be /' + profile3.ticketPattern + '/, was: Fixes #1', | ||
'Invalid ticket reference, must be /' + profile3.ticketPattern + '/, was: Fixes #123' | ||
]] | ||
]) | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Fixes #1 Fixes #123', | ||
reasons: new Map([ | ||
[profile3, [ | ||
'Invalid ticket reference, must be /' + profile3.ticketPattern + '/, was: Fixes #1 Fixes #123' | ||
]] | ||
]) | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Fixes jquery/jquery#1\n' + | ||
'Fixes jquery/jquery#123', | ||
reasons: new Map([ | ||
[profile3, [ | ||
'Invalid ticket reference, must be /' + profile3.ticketPattern + '/, was: Fixes jquery/jquery#1', | ||
'Invalid ticket reference, must be /' + profile3.ticketPattern + '/, was: Fixes jquery/jquery#123' | ||
]] | ||
]) | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Fixes jquery/jquery#1 Fixes jquery/jquery#123', | ||
reasons: new Map([ | ||
[profile3, [ | ||
'Invalid ticket reference, must be /' + profile3.ticketPattern + '/, was: Fixes jquery/jquery#1 Fixes jquery/jquery#123' | ||
]] | ||
]) | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Fixes gh-1\n' + | ||
'Fixes gh-123' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Fixes gh-1 Fixes gh-123' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Fixes WEB-1\n' + | ||
'Fixes WEB-123' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Fixes WEB-1 Fixes WEB-123' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Fixes CRM-1\n' + | ||
'Fixes CRM-123' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Fixes CRM-1 Fixes CRM-123' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Closes #1\n' + | ||
'Closes #123', | ||
reasons: new Map([ | ||
[profile3, [ | ||
'Invalid ticket reference, must be /' + profile3.ticketPattern + '/, was: Closes #1', | ||
'Invalid ticket reference, must be /' + profile3.ticketPattern + '/, was: Closes #123' | ||
]] | ||
]) | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Closes #1 Closes #123', | ||
reasons: new Map([ | ||
[profile3, [ | ||
'Invalid ticket reference, must be /' + profile3.ticketPattern + '/, was: Closes #1 Closes #123' | ||
]] | ||
]) | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Closes jquery/jquery#1\n' + | ||
'Closes jquery/jquery#123', | ||
reasons: new Map([ | ||
[profile3, [ | ||
'Invalid ticket reference, must be /' + profile3.ticketPattern + '/, was: Closes jquery/jquery#1', | ||
'Invalid ticket reference, must be /' + profile3.ticketPattern + '/, was: Closes jquery/jquery#123' | ||
]] | ||
]) | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Closes jquery/jquery#1 Closes jquery/jquery#123', | ||
reasons: new Map([ | ||
[profile3, [ | ||
'Invalid ticket reference, must be /' + profile3.ticketPattern + '/, was: Closes jquery/jquery#1 Closes jquery/jquery#123' | ||
]] | ||
]) | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Closes gh-1\n' + | ||
'Closes gh-123' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Closes gh-1 Closes gh-123' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Closes WEB-1\n' + | ||
'Closes WEB-123' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Closes WEB-1 Closes WEB-123' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Closes CRM-1\n' + | ||
'Closes CRM-123' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Closes CRM-1 Closes CRM-123' | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Fixes #123\n' + | ||
'Fixes #1 Fixes #123\n' + | ||
'Fixes gh-123\n' + | ||
'Fixes gh-1 Fixes gh-123\n' + | ||
'Fixes WEB-123\n' + | ||
'Fixes WEB-1 Fixes WEB-123\n' + | ||
'Fixes CRM-123\n' + | ||
'Fixes CRM-1 Fixes CRM-123\n', | ||
rejects: [profile3] | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Closes #123\n' + | ||
'Closes #1 Closes #123\n' + | ||
'Closes gh-123\n' + | ||
'Closes gh-1 Closes gh-123\n' + | ||
'Closes WEB-123\n' + | ||
'Closes WEB-1 Closes WEB-123\n' + | ||
'Closes CRM-123\n' + | ||
'Closes CRM-1 Closes CRM-123\n', | ||
rejects: [profile3] | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Closes #123\n' + | ||
'Fixes #1 Closes #123\n' + | ||
'Fixes gh-123\n' + | ||
'Closes gh-1 Fixes gh-123\n' + | ||
'Closes WEB-123\n' + | ||
'Fixes WEB-1 Closes WEB-123\n' + | ||
'Fixes CRM-123\n' + | ||
'Closes CRM-1 Fixes CRM-123\n', | ||
rejects: [profile3] | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Long description\n\n' + | ||
'Closes #42\n\n' + | ||
'An afterthought is ok', | ||
reasons: new Map([ | ||
[profile3, [ 'Invalid ticket reference, must be /' + profile3.ticketPattern + '/, was: Closes #42' ]] | ||
]) | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Closes: gh-1', | ||
reasons: new Map([ | ||
[profile0, [ 'Invalid ticket reference, must be /' + profile0.ticketPattern + '/, was: Closes: gh-1' ]], | ||
[profile1, [ 'Invalid ticket reference, must be /' + profile1.ticketPattern + '/, was: Closes: gh-1' ]], | ||
[profile3, [ 'Invalid ticket reference, must be /' + profile3.ticketPattern + '/, was: Closes: gh-1' ]] | ||
]) | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Closing #1', | ||
reasons: new Map([ | ||
[profile0, [ 'Invalid ticket reference, must be /' + profile0.ticketPattern + '/, was: Closing #1' ]], | ||
[profile1, [ 'Invalid ticket reference, must be /' + profile1.ticketPattern + '/, was: Closing #1' ]], | ||
[profile3, [ 'Invalid ticket reference, must be /' + profile3.ticketPattern + '/, was: Closing #1' ]] | ||
]) | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Fixing gh-1', | ||
reasons: new Map([ | ||
[profile0, [ 'Invalid ticket reference, must be /' + profile0.ticketPattern + '/, was: Fixing gh-1' ]], | ||
[profile1, [ 'Invalid ticket reference, must be /' + profile1.ticketPattern + '/, was: Fixing gh-1' ]], | ||
[profile3, [ 'Invalid ticket reference, must be /' + profile3.ticketPattern + '/, was: Fixing gh-1' ]] | ||
]) | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'Resolving WEB-1', | ||
reasons: new Map([ | ||
[profile0, [ 'Invalid ticket reference, must be /' + profile0.ticketPattern + '/, was: Resolving WEB-1' ]], | ||
[profile1, [ 'Invalid ticket reference, must be /' + profile1.ticketPattern + '/, was: Resolving WEB-1' ]], | ||
[profile3, [ 'Invalid ticket reference, must be /' + profile3.ticketPattern + '/, was: Resolving WEB-1' ]] | ||
]) | ||
}, | ||
{ | ||
msg: '0.0.1' | ||
}, | ||
{ | ||
msg: 'v0.0.1' | ||
}, | ||
{ | ||
msg: '512.4096.65536' | ||
}, | ||
{ | ||
msg: 'v512.4096.65536' | ||
}, | ||
{ | ||
msg: "Merge branch 'one' into two" | ||
}, | ||
{ | ||
msg: 'Merge e8d808b into 0f40453' | ||
}, | ||
{ | ||
msg: 'fixup!' | ||
}, | ||
{ | ||
msg: 'fixup! short message' | ||
}, | ||
{ | ||
msg: 'squash!' | ||
}, | ||
{ | ||
msg: 'squash! short message' | ||
}, | ||
{ | ||
msg: '[fix]: short message' | ||
}, | ||
{ | ||
msg: '[Tmp]: short message' | ||
}, | ||
{ | ||
msg: '', | ||
reasons: new Map([ | ||
[profile0, [ | ||
'First line (subject) must not be empty', | ||
'First line (subject) must indicate the component', | ||
'First line (subject) must have a message after the component' | ||
]], | ||
[profile1, [ 'First line (subject) must not be empty' ]], | ||
[profile2, [ | ||
'First line (subject) must not be empty', | ||
'First line (subject) must indicate the component', | ||
'First line (subject) must have a message after the component' | ||
]], | ||
[profile3, [ | ||
'First line (subject) must not be empty', | ||
'First line (subject) must indicate the component', | ||
'First line (subject) must have a message after the component' | ||
]] | ||
]) | ||
}, | ||
{ | ||
msg: 'Component: short message\n\n' + | ||
'# Please enter the commit message . Lines starting\n' + | ||
"# with '#' will be ignored, an empty message aborts the commit.\n" + | ||
'# On branch commitplease-rocks\n' + | ||
'# Changes to be committed:\n' + | ||
'# modified: test.js\n' + | ||
'# ------------------------ >8 ------------------------\n' + | ||
'# Do not touch the line above.\n' + | ||
'# Everything below will be removed.\n' + | ||
'diff --git a/test.js b/test.js\n' + | ||
'index c689515..706b86f 100644\n' + | ||
'--- a/test.js\n' + | ||
'+++ b/test.js\n' + | ||
'@@ -1,14 +1,10 @@\n' + | ||
"var validate = require('./lib/validate')\n" + | ||
"var sanitize = require('./lib/sanitize')\n" | ||
} | ||
] | ||
var invalid = [ | ||
{ | ||
msg: "", | ||
expected: [ "First line (subject) must not be empty" ], | ||
options: { | ||
component: false | ||
} | ||
}, | ||
{ | ||
msg: "Test: Message", | ||
expected: [ "Component invalid, was 'Test', must be one of these: Build, Legacy" ], | ||
options: { | ||
components: [ "Build", "Legacy" ] | ||
} | ||
}, | ||
{ | ||
msg: "", | ||
expected: [ | ||
"First line (subject) must not be empty", | ||
"First line (subject) must indicate the component", | ||
"First line (subject) must have a message after the component" | ||
] | ||
}, | ||
{ | ||
msg: "Component: short message but actually a little bit over default character limit", | ||
expected: [ "First line (subject) must be no longer than 72 characters" ] | ||
}, | ||
{ | ||
msg: "foo:", | ||
expected: [ "First line (subject) must have a message after the component" ] | ||
}, | ||
{ | ||
msg: "no component here", | ||
expected: [ "First line (subject) must indicate the component" ] | ||
}, | ||
{ | ||
msg: "component: bla\ntext on next line", | ||
expected: [ "Second line must always be empty" ] | ||
}, | ||
{ | ||
msg: "component: bla\n\nline too long beyond 80 chars line too long beyond" + | ||
"line too long line too long line too long", | ||
expected: [ "Commit message line 3 too long: 91 characters, only 80 allowed. Was: line too long beyond[...]" ] | ||
}, | ||
{ | ||
msg: "Docs: Fix a typo\n\nCloses: gh-155", | ||
expected: [ "Invalid ticket reference, must be /(Fixes|Closes) (.*#|gh-|[A-Z]{2,}-)[0-9]+/, was: Closes: gh-155" ] | ||
}, | ||
{ | ||
msg: "Bla: blub\n\nClosing #1", | ||
expected: [ "Invalid ticket reference, must be /(Fixes|Closes) (.*#|gh-|[A-Z]{2,}-)[0-9]+/, was: Closing #1" ] | ||
}, | ||
{ | ||
msg: "Bla: blub\n\nFixing gh-1", | ||
expected: [ "Invalid ticket reference, must be /(Fixes|Closes) (.*#|gh-|[A-Z]{2,}-)[0-9]+/, was: Fixing gh-1" ] | ||
}, | ||
{ | ||
msg: "Bla: blub\n\nResolving xy-9991", | ||
expected: [ "Invalid ticket reference, must be /(Fixes|Closes) (.*#|gh-|[A-Z]{2,}-)[0-9]+/, was: Resolving xy-9991" ] | ||
}, | ||
{ | ||
msg: "bla: blu\n\n# comment\nResolving xy12312312312", | ||
expected: [ "Invalid ticket reference, must be /(Fixes|Closes) (.*#|gh-|[A-Z]{2,}-)[0-9]+/," + | ||
" was: Resolving xy12312312312" ] | ||
} | ||
]; | ||
// reserve for Angular | ||
var profiles1 = [] | ||
var messages1 = [] | ||
exports.valid = function( test ) { | ||
valid.forEach( function( check, index ) { | ||
test.deepEqual( validate( sanitize( check.msg ), check.options ), [], "valid " + index + | ||
" " + check.msg ); | ||
} ); | ||
test.done(); | ||
}; | ||
var groups = new Map([ | ||
[profiles0, messages0], | ||
[profiles1, messages1] | ||
]) | ||
exports.invalid = function( test ) { | ||
invalid.forEach( function( check, index ) { | ||
test.deepEqual( validate( sanitize( check.msg ), check.options ), check.expected, | ||
"invalid " + index + " " + check.msg ); | ||
} ); | ||
test.done(); | ||
}; | ||
exports.tests = function (test) { | ||
for (var group of groups) { | ||
testGroup(test, group) | ||
} | ||
exports.sanitze = function( test ) { | ||
test.strictEqual( sanitize( messageWithMultipleLines ), messageWithMultipleLines ); | ||
test.strictEqual( sanitize( messageWithDiff ), "Component: short message\n\nsome text\nmore text" ); | ||
test.done(); | ||
}; | ||
test.done() | ||
} | ||
function testGroup (test, group) { | ||
let [profiles, messages] = [group[0], group[1]] | ||
for (let profile of profiles) { | ||
for (let message of messages) { | ||
let excludes = message.excludes | ||
if (excludes && excludes.indexOf(profile) !== -1) { | ||
continue | ||
} | ||
let msg = message.msg | ||
let accepts = message.accepts | ||
if (accepts && accepts.indexOf(profile) !== -1) { | ||
let result = validate(sanitize(msg), profile) | ||
test.deepEqual(result, [], '\n' + msg) | ||
continue | ||
} | ||
let rejects = message.rejects | ||
if (rejects && rejects.indexOf(profile) !== -1) { | ||
let result = validate(sanitize(msg), profile) | ||
test.notDeepEqual(result, [], '\n' + msg) | ||
continue | ||
} | ||
let reasons = message.reasons | ||
if (reasons && reasons.get(profile)) { | ||
let reason = reasons.get(profile) | ||
let result = validate(sanitize(msg), profile) | ||
test.deepEqual(result, reason, '\n' + msg) | ||
continue | ||
} | ||
// assume default: profile must pass the test | ||
let result = validate(sanitize(msg), profile) | ||
test.deepEqual(result, [], '\n' + msg) | ||
} | ||
} | ||
for (let message of messages) { | ||
let msg = message.msg | ||
let accepts = message.accepts | ||
if (accepts) { | ||
for (let profile of accepts) { | ||
if (profiles.indexOf(profile) === -1) { | ||
let result = validate(sanitize(msg), profile) | ||
test.deepEqual(result, [], '\n' + msg) | ||
} | ||
} | ||
} | ||
let rejects = message.rejects | ||
if (rejects) { | ||
for (let profile of rejects) { | ||
if (profiles.indexOf(profile) === -1) { | ||
let result = validate(sanitize(msg), profile) | ||
test.notDeepEqual(result, [], '\n' + msg) | ||
} | ||
} | ||
} | ||
let reasons = message.reasons | ||
if (reasons) { | ||
for (let key2val of reasons) { | ||
let [profile, reason] = [key2val[0], key2val[1]] | ||
if (profiles.indexOf(profile) === -1) { | ||
let result = validate(sanitize(msg), profile) | ||
test.deepEqual(result, reason, '\n' + msg) | ||
} | ||
} | ||
} | ||
} | ||
} |
@@ -1,8 +0,8 @@ | ||
var setup = require( "./setup" ); | ||
var setup = require('./setup') | ||
if ( setup.selfmadeHook ) { | ||
console.log( "Found a hook installed by commitplease, removing" ); | ||
setup.destroy(); | ||
if (setup.selfmadeHook) { | ||
console.log('Found a hook installed by commitplease, removing') | ||
setup.destroy() | ||
} else { | ||
console.log( "Didn't find a commit-msg hook installed by this module, doing nothing" ); | ||
console.log("Didn't find a commit-msg hook installed by this module, doing nothing") | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
28427
689
120
7
15
2