-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #245 from ipfs/lint-commit-messages
feat: lint commit messages
- Loading branch information
Showing
4 changed files
with
178 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,23 @@ | ||
'use strict' | ||
|
||
module.exports = { | ||
command: 'lint-commits', | ||
desc: 'Lint commit messages', | ||
builder: { | ||
from: { | ||
alias: 'f', | ||
describe: 'The commit-ish to lint from', | ||
default: 'remotes/origin/master' | ||
}, | ||
to: { | ||
alias: 't', | ||
describe: 'The commit-ish to lint to', | ||
default: 'HEAD' | ||
} | ||
}, | ||
handler (argv) { | ||
const lintCommits = require('../src/lint-commits') | ||
const onError = require('../src/error-handler') | ||
lintCommits(argv).catch(onError) | ||
} | ||
} |
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
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,64 @@ | ||
'use strict' | ||
|
||
const loadCommitLintConfig = require('@commitlint/load') | ||
const readCommits = require('@commitlint/read') | ||
const lintCommitMessage = require('@commitlint/lint') | ||
const conventionalCommits = require('@commitlint/config-conventional') | ||
|
||
function lintCommitMessages (opts = {}) { | ||
const from = opts.from || 'remotes/origin/master' | ||
const to = opts.to || 'HEAD' | ||
|
||
return Promise.all([ | ||
loadCommitLintConfig({ | ||
rules: conventionalCommits.rules | ||
}), | ||
readCommits({ | ||
from, | ||
to | ||
}) | ||
]) | ||
.then(([ { rules, parserPreset }, commits ]) => { | ||
return Promise.all( | ||
commits.map(commit => { | ||
return lintCommitMessage(commit, rules, parserPreset ? { parserOpts: parserPreset.parserOpts } : {}) | ||
}) | ||
) | ||
}) | ||
.then(results => { | ||
let valid = true | ||
|
||
results.forEach(report => { | ||
if (valid === true) { | ||
valid = report.valid | ||
} | ||
|
||
const firstLine = `${report.input.trim().split('\n')[0]}`.trim() | ||
|
||
if (!report.valid) { | ||
console.log(`Commit message '${firstLine}' failed validation:`) | ||
console.log('') | ||
|
||
report.errors.forEach(error => { | ||
console.log(' [ERROR]', `${error.name}:`, error.message) | ||
}) | ||
|
||
report.warnings.forEach(warning => { | ||
console.log(' [WARNING]', `${warning.name}:`, warning.message) | ||
}) | ||
|
||
console.log('') | ||
} | ||
}) | ||
|
||
if (!valid) { | ||
throw new Error(`Linting commits ${from}..${to} failed`) | ||
} | ||
}) | ||
} | ||
|
||
function lintCommits (opts) { | ||
return lintCommitMessages(opts) | ||
} | ||
|
||
module.exports = lintCommits |
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,87 @@ | ||
/* eslint-env mocha */ | ||
'use strict' | ||
|
||
const lintCommits = require('../src/lint-commits') | ||
const expect = require('chai').expect | ||
const path = require('path') | ||
const os = require('os') | ||
const fs = require('fs') | ||
const series = require('async/series') | ||
const child = require('child_process') | ||
|
||
function commitFile (directory, name, contents, message, callback) { | ||
series([ | ||
(cb) => fs.writeFile(path.join(directory, name), contents, cb), | ||
(cb) => child.exec('git add -A', { | ||
cwd: directory | ||
}, cb), | ||
(cb) => child.exec(`git commit -m "${message}"`, { | ||
cwd: directory | ||
}, cb) | ||
], callback) | ||
} | ||
|
||
const setupProject = (commitMessage = 'chore: initial commit') => { | ||
const tmpDir = path.join(os.tmpdir(), `test-${Math.random()}`) | ||
|
||
return new Promise((resolve, reject) => { | ||
series([ | ||
(cb) => fs.mkdir(tmpDir, cb), | ||
(cb) => child.exec('git init', { | ||
cwd: tmpDir | ||
}, cb), | ||
(cb) => child.exec('git config user.email "you@example.com"', { | ||
cwd: tmpDir | ||
}, cb), | ||
(cb) => child.exec('git config user.name "test"', { | ||
cwd: tmpDir | ||
}, cb), | ||
(cb) => commitFile(tmpDir, 'hello.txt', 'Amazing', 'chore: initial commit', cb), | ||
(cb) => commitFile(tmpDir, 'goodbye.txt', 'Amazing', commitMessage, cb) | ||
], (error) => { | ||
if (error) { | ||
return reject(error) | ||
} | ||
|
||
process.chdir(tmpDir) | ||
|
||
resolve() | ||
}) | ||
}) | ||
} | ||
|
||
const commitMessageShouldPassLinting = (commitMessage) => { | ||
return setupProject(commitMessage) | ||
.then(() => lintCommits({ | ||
from: 'HEAD~1' | ||
})) | ||
} | ||
|
||
const commitMessageShouldFailLinting = (commitMessage) => { | ||
return setupProject(commitMessage) | ||
.then(() => lintCommits({ | ||
from: 'HEAD~1' | ||
})) | ||
.then(() => { | ||
throw new Error('Should have failed!') | ||
}) | ||
.catch(error => { | ||
expect(error.message).to.contain('Linting commits HEAD~1..HEAD failed') | ||
}) | ||
} | ||
|
||
describe('lint commit messages', () => { | ||
const cwd = process.cwd() | ||
|
||
after(() => { | ||
process.chdir(cwd) | ||
}) | ||
|
||
it('passes with good commit messages', function () { | ||
return commitMessageShouldPassLinting('chore: such a chore') | ||
}) | ||
|
||
it('fails with bad commit messages', function () { | ||
return commitMessageShouldFailLinting('Ahahallolol!!!11shift+1') | ||
}) | ||
}) |