Skip to content

Instantly share code, notes, and snippets.

@vsemozhetbyt
Last active April 8, 2017 16:30
Show Gist options
  • Select an option

  • Save vsemozhetbyt/25698ab638b51d07811093d4be249991 to your computer and use it in GitHub Desktop.

Select an option

Save vsemozhetbyt/25698ab638b51d07811093d4be249991 to your computer and use it in GitHub Desktop.
Check commit messages in PRs for Node.js repository
javascript: {
const validPathname = /^\/nodejs\/node\/pull\/\d+(?:\/commits)?$/;
const TITLE_MAX_LENGTH = 50;
const LINE_MAX_LENGTH = 72;
const titleFormat = /^([\d_a-z ,-]+)\b: ([a-z'-]+\b)?/;
const lineWithURLFormat = /^(Fixes|Refs?): (https?:)?/;
const LOG_DELIMITER_LENGTH = 20;
const loaderBar = document.querySelector('#js-pjax-loader-bar');
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.oldValue.includes('is-loading') &&
validPathname.test(location.pathname)) check();
});
});
observer.observe(loaderBar,
{ attributes: true, attributeOldValue: true, attributeFilter: ['class'] }
);
check();
function check() {
let OK = 0;
let notOK = 0;
const allErrors = [];
const commitLinks = [...document.body.querySelectorAll('a[href].message')];
const hrefSeen = [];
commitLinks.forEach((commitLink, commitNumber) => {
const href = commitLink.href;
if (hrefSeen.includes(href)) return;
hrefSeen.unshift(href);
const commitErrors = [];
const lines = commitLink.title.split('\n');
const title = lines.shift();
const [, titlePrefix, lcVerb] = title.match(titleFormat) || [];
const titleLength = [...title].length;
if (!titlePrefix) {
commitErrors.push('Begin commit title with subsystem prefix');
}
if (!lcVerb) {
commitErrors.push('Use lower case imperative verb after subsystem prefix');
}
if (titleLength > TITLE_MAX_LENGTH) {
commitErrors.push(
`Keep commit title length within ${
TITLE_MAX_LENGTH} characters (currently ${titleLength})`
);
}
if (lines.length && lines[0] !== '') {
commitErrors.push('Insert empty line after commit title');
}
let lineNumber = 1;
lines.forEach((line) => {
lineNumber++;
const lineLength = [...line].length;
if (lineLength > LINE_MAX_LENGTH) {
commitErrors.push(
`Keep line length within ${
LINE_MAX_LENGTH} characters: line ${
lineNumber} length is ${lineLength}:\n${line}`
);
}
const [, linePrefix, protocol] = line.match(lineWithURLFormat) || [];
if (linePrefix && !protocol) {
commitErrors.push(
`Use full URL with 'Fixes:' and 'Refs:': line ${lineNumber}:\n${line}`
);
}
});
if (!commitErrors.length) {
OK++;
commitLink.style.backgroundColor = 'LightGreen';
} else {
notOK++;
commitLink.style.backgroundColor = 'LightCoral';
const errorMessage = commitErrors.map(error => `* ${error}`).join('\n\n');
allErrors.push(`Commit ${commitNumber + 1} (${title}):\n\n${errorMessage}`);
const errorInfo = getOrCreate(
'span', '.node-check-commit-messages-error', commitLink.parentNode, commitLink.nextSibling
);
errorInfo.title = errorMessage;
errorInfo.textContent = '!';
errorInfo.setAttribute('style',
'padding: 0px 3px 0px 3px; margin: 0px 3px 0px 3px; background-color: LightCoral;'
);
}
});
const results = commitLinks.length ?
`OK: ${OK}${notOK ? `\nNot OK: ${notOK}` : ''}`
:
'No commits found.';
const parent = document.querySelector('#discussion_bucket, #commits_bucket');
const resultsInfo = getOrCreate('pre', '#node-check-commit-messages-results', parent);
resultsInfo.textContent = results;
resultsInfo.setAttribute('style',
`position: fixed; top: 0px; left: 0px; z-index: 10000; background-color: ${
notOK ? 'LightCoral' : OK ? 'LightGreen' : 'LightGray'
}; padding: 3px;`
);
if (notOK) {
resultsInfo.style.cursor = 'pointer';
resultsInfo.title = 'click to copy full log';
const copyImg = getOrCreate(
'img', '#node-check-commit-messages-results-copy-img', resultsInfo
);
copyImg.alt = allErrors.join(`\n\n${'-'.repeat(LOG_DELIMITER_LENGTH)}\n\n`);
copyImg.src =
'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
resultsInfo.addEventListener('click', copyLog);
}
}
function getOrCreate(tag, attr, parent = document.body, nodeToInsertBefore = null) {
let elem = parent.querySelector(`${tag}${attr}`);
if (!elem) {
elem = document.createElement(tag);
elem[attr.startsWith('#') ? 'id' : 'className'] = attr.slice(1);
parent.insertBefore(elem, nodeToInsertBefore);
}
return elem;
}
function copyLog(event) {
const sel = window.getSelection();
sel.removeAllRanges();
const range = document.createRange();
range.selectNode(event.target.querySelector('img'));
sel.addRange(range);
document.execCommand('copy');
sel.removeAllRanges();
}
}
@vsemozhetbyt
Copy link
Copy Markdown
Author

vsemozhetbyt commented Apr 1, 2017

Tampermonkey header example:

/******************************************************************************/
// ==UserScript==
// @name         Check Commits
// @namespace    vsemozhetbyt
// @description  Check commit messages in PRs for Node.js repository
// @version      1.0.0
// @include      https://github.com/nodejs/node/commits/master
// @include      https://github.com/nodejs/node/pull/*
// @exclude      https://github.com/nodejs/node/pull/*/files*
// @noframes     
// @grant        none
// @nocompat     Chrome
// ==/UserScript==
/******************************************************************************/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment