#!/usr/bin/env node /** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** * GIT commit message format enforcement * * Note: this script was originally written by Vojta for AngularJS :-) */ 'use strict'; const config = require('./commit-message.json'); const FIXUP_PREFIX_RE = /^fixup! /i; const SQUASH_PREFIX_RE = /^squash! /i; const REVERT_PREFIX_RE = /^revert:? /i; module.exports = (commitHeader, disallowSquash, nonFixupCommitHeaders) => { if (REVERT_PREFIX_RE.test(commitHeader)) { return true; } const {header, type, scope, isFixup, isSquash} = parseCommitHeader(commitHeader); if (isSquash && disallowSquash) { error('The commit must be manually squashed into the target commit', commitHeader); return false; } // If it is a fixup commit and `nonFixupCommitHeaders` is not empty, we only care to check whether // there is a corresponding non-fixup commit (i.e. a commit whose header is identical to this // commit's header after stripping the `fixup! ` prefix). if (isFixup && nonFixupCommitHeaders) { if (!nonFixupCommitHeaders.includes(header)) { error( 'Unable to find match for fixup commit among prior commits: ' + (nonFixupCommitHeaders.map(x => `\n ${x}`).join('') || '-'), commitHeader); return false; } return true; } if (header.length > config.maxLength) { error(`The commit message header is longer than ${config.maxLength} characters`, commitHeader); return false; } if (!type) { const format = '(): '; error( `The commit message header does not match the format of '${format}' or 'Revert: "${format}"'`, commitHeader); return false; } if (!config.types.includes(type)) { error(`'${type}' is not an allowed type.\n => TYPES: ${config.types.join(', ')}`, commitHeader); return false; } if (scope && !config.scopes.includes(scope)) { error( `'${scope}' is not an allowed scope.\n => SCOPES: ${config.scopes.join(', ')}`, commitHeader); return false; } return true; }; module.exports.FIXUP_PREFIX_RE = FIXUP_PREFIX_RE; module.exports.config = config; // Helpers function error(errorMessage, commitHeader) { console.error(`INVALID COMMIT MSG: ${commitHeader}\n => ERROR: ${errorMessage}`); } function parseCommitHeader(header) { const isFixup = FIXUP_PREFIX_RE.test(header); const isSquash = SQUASH_PREFIX_RE.test(header); header = header.replace(FIXUP_PREFIX_RE, '').replace(SQUASH_PREFIX_RE, ''); const match = /^(\w+)(?:\(([^)]+)\))?\: (.+)$/.exec(header) || []; return { header, type: match[1], scope: match[2], subject: match[3], isFixup, isSquash, }; }