UNPKG

@zkochan/pnpm

Version:

A fast implementation of npm install

180 lines (141 loc) 5.37 kB
#!/usr/bin/env node /** * Git COMMIT-MSG hook for validating commit message * See https://docs.google.com/document/d/1rk04jEuGfk9kYzfqCuOlPTSJw3hEDZJTBN5E5f1SALo/edit * * Installation: * >> cd <angular-repo> * >> ln -s ../../validate-commit-msg.js .git/hooks/commit-msg */ 'use strict'; var fs = require('fs'); var util = require('util'); var resolve = require('path').resolve; var findup = require('findup'); var semverRegex = require('semver-regex') var config = getConfig(); var MAX_LENGTH = config.maxSubjectLength || 100; var IGNORED = new RegExp(util.format('(^WIP)|(^%s$)', semverRegex().source)); var TYPES = config.types = config.types || ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'revert']; // fixup! and squash! are part of Git, commits tagged with them are not intended to be merged, cf. https://git-scm.com/docs/git-commit var PATTERN = /^((fixup! |squash! )?(\w+)(?:\(([^\)\s]+)\))?: (.+))(?:\n|$)/; var MERGE_COMMIT_PATTERN = /^Merge /; var error = function() { // gitx does not display it // http://gitx.lighthouseapp.com/projects/17830/tickets/294-feature-display-hook-error-message-when-hook-fails // https://groups.google.com/group/gitx/browse_thread/thread/a03bcab60844b812 console[config.warnOnFail ? 'warn' : 'error']('INVALID COMMIT MSG: ' + util.format.apply(null, arguments)); }; var validateMessage = function(raw) { var messageWithBody = (raw || '').split('\n').filter(function (str) { return str.indexOf('#') !== 0; }).join('\n'); var message = messageWithBody.split('\n').shift(); if (message === '') { console.log('Aborting commit due to empty commit message.'); return false; } var isValid = true; if(MERGE_COMMIT_PATTERN.test(message)){ console.log('Merge commit detected.'); return true } if (IGNORED.test(message)) { console.log('Commit message validation ignored.'); return true; } var match = PATTERN.exec(message); if (!match) { error('does not match "<type>(<scope>): <subject>" !'); isValid = false; } else { var firstLine = match[1]; var squashing = !!match[2]; var type = match[3]; var scope = match[4]; var subject = match[5]; var SUBJECT_PATTERN = new RegExp(config.subjectPattern || '.+'); var SUBJECT_PATTERN_ERROR_MSG = config.subjectPatternErrorMsg || 'subject does not match subject pattern!'; if (firstLine.length > MAX_LENGTH && !squashing) { error('is longer than %d characters !', MAX_LENGTH); isValid = false; } if (TYPES !== '*' && TYPES.indexOf(type) === -1) { error('"%s" is not allowed type ! Valid types are: %s', type, config.types.join(', ')); isValid = false; } if (!SUBJECT_PATTERN.exec(subject)) { error(SUBJECT_PATTERN_ERROR_MSG); isValid = false; } } // Some more ideas, do want anything like this ? // - Validate the rest of the message (body, footer, BREAKING CHANGE annotations) // - allow only specific scopes (eg. fix(docs) should not be allowed ? // - auto correct the type to lower case ? // - auto correct first letter of the subject to lower case ? // - auto add empty line after subject ? // - auto remove empty () ? // - auto correct typos in type ? // - store incorrect messages, so that we can learn isValid = isValid || config.warnOnFail; if (isValid) { // exit early and skip messaging logics return true; } var argInHelp = config.helpMessage && config.helpMessage.indexOf('%s') !== -1; if (argInHelp) { console.log(config.helpMessage, messageWithBody); } else if (message) { console.log(message); } if (!argInHelp && config.helpMessage) { console.log(config.helpMessage); } return false; }; // publish for testing exports.validateMessage = validateMessage; exports.getGitFolder = getGitFolder; exports.config = config; // hacky start if not run by mocha :-D // istanbul ignore next if (process.argv.join('').indexOf('mocha') === -1) { var commitMsgFile = process.argv[2] || getGitFolder() + '/COMMIT_EDITMSG'; var incorrectLogFile = commitMsgFile.replace('COMMIT_EDITMSG', 'logs/incorrect-commit-msgs'); var hasToString = function hasToString(x) { return x && typeof x.toString === 'function'; }; fs.readFile(commitMsgFile, function(err, buffer) { var msg = getCommitMessage(buffer); if (!validateMessage(msg)) { fs.appendFile(incorrectLogFile, msg + '\n', function() { process.exit(1); }); } else { process.exit(0); } function getCommitMessage(buffer) { return hasToString(buffer) && buffer.toString(); } }); } function getConfig() { var pkgFile = findup.sync(process.cwd(), 'package.json'); var pkg = JSON.parse(fs.readFileSync(resolve(pkgFile, 'package.json'))); return pkg && pkg.config && pkg.config['validate-commit-msg'] || {}; } function getGitFolder() { var gitDirLocation = './.git'; if (!fs.existsSync(gitDirLocation)) { throw new Error('Cannot find file ' + gitDirLocation); } if(!fs.lstatSync(gitDirLocation).isDirectory()) { var unparsedText = '' + fs.readFileSync(gitDirLocation); gitDirLocation = unparsedText.substring('gitdir: '.length).trim(); } if (!fs.existsSync(gitDirLocation)) { throw new Error('Cannot find file ' + gitDirLocation); } return gitDirLocation; }