From 0ca3788a9504a089df3ba67d0c240c4e6525ce23 Mon Sep 17 00:00:00 2001 From: Adam Moss Date: Fri, 28 Jul 2017 14:47:11 +0100 Subject: [PATCH] feat(core): add Signed-off-by rule --- @commitlint/core/src/rules/index.js | 1 + @commitlint/core/src/rules/signed-off-by.js | 13 +++ .../core/src/rules/signed-off-by.test.js | 79 +++++++++++++++++++ docs/reference-rules.md | 8 ++ 4 files changed, 101 insertions(+) create mode 100644 @commitlint/core/src/rules/signed-off-by.js create mode 100644 @commitlint/core/src/rules/signed-off-by.test.js diff --git a/@commitlint/core/src/rules/index.js b/@commitlint/core/src/rules/index.js index afaa336c64..4d62e894b8 100644 --- a/@commitlint/core/src/rules/index.js +++ b/@commitlint/core/src/rules/index.js @@ -18,6 +18,7 @@ export default { 'scope-enum': require('./scope-enum'), 'scope-max-length': require('./scope-max-length'), 'scope-min-length': require('./scope-min-length'), + 'signed-off-by': require('./signed-off-by'), 'subject-case': require('./subject-case'), 'subject-empty': require('./subject-empty'), 'subject-full-stop': require('./subject-full-stop'), diff --git a/@commitlint/core/src/rules/signed-off-by.js b/@commitlint/core/src/rules/signed-off-by.js new file mode 100644 index 0000000000..b1a8096cfc --- /dev/null +++ b/@commitlint/core/src/rules/signed-off-by.js @@ -0,0 +1,13 @@ +export default (parsed, when, value) => { + const input = /.*\n(Signed-off-by:).*\n+/g.exec(parsed.raw); + + const negated = when === 'never'; + const hasSignedOffBy = Boolean(input && input[1] === value); + + return [ + negated ? !hasSignedOffBy : hasSignedOffBy, + ['message', negated ? 'must not' : 'must', 'be signed off'] + .filter(Boolean) + .join(' ') + ]; +}; diff --git a/@commitlint/core/src/rules/signed-off-by.test.js b/@commitlint/core/src/rules/signed-off-by.test.js new file mode 100644 index 0000000000..98a5e780dd --- /dev/null +++ b/@commitlint/core/src/rules/signed-off-by.test.js @@ -0,0 +1,79 @@ +import test from 'ava'; +import parse from '../library/parse'; +import check from './signed-off-by'; + +const messages = { + empty: 'chore:\n', + with: `chore: subject\nbody\nfooter\nSigned-off-by:\n\n`, + without: `chore: subject\nbody\nfooter\n\n`, + inSubject: `chore: subject Signed-off-by:\nbody\nfooter\n\n`, + inBody: `chore: subject\nbody Signed-off-by:\nfooter\n\n` +}; + +const parsed = { + empty: parse(messages.empty), + with: parse(messages.with), + without: parse(messages.without), + inSubject: parse(messages.inSubject), + inBody: parse(messages.inBody) +}; + +test('empty against "always signed-off-by" should fail', async t => { + const [actual] = check(await parsed.empty, 'always', 'Signed-off-by:'); + const expected = false; + t.is(actual, expected); +}); + +test('empty against "never signed-off-by" should succeed', async t => { + const [actual] = check(await parsed.empty, 'never', 'Signed-off-by:'); + const expected = true; + t.is(actual, expected); +}); + +test('with against "always signed-off-by" should succeed', async t => { + const [actual] = check(await parsed.with, 'always', 'Signed-off-by:'); + const expected = true; + t.is(actual, expected); +}); + +test('with against "never signed-off-by" should fail', async t => { + const [actual] = check(await parsed.with, 'never', 'Signed-off-by:'); + const expected = false; + t.is(actual, expected); +}); + +test('without against "always signed-off-by" should fail', async t => { + const [actual] = check(await parsed.without, 'always', 'Signed-off-by:'); + const expected = false; + t.is(actual, expected); +}); + +test('without against "never signed-off-by" should succeed', async t => { + const [actual] = check(await parsed.without, 'never', 'Signed-off-by:'); + const expected = true; + t.is(actual, expected); +}); + +test('inSubject against "always signed-off-by" should fail', async t => { + const [actual] = check(await parsed.inSubject, 'always', 'Signed-off-by:'); + const expected = false; + t.is(actual, expected); +}); + +test('inSubject against "never signed-off-by" should succeed', async t => { + const [actual] = check(await parsed.inSubject, 'never', 'Signed-off-by:'); + const expected = true; + t.is(actual, expected); +}); + +test('inBody against "always signed-off-by" should fail', async t => { + const [actual] = check(await parsed.inBody, 'always', 'Signed-off-by:'); + const expected = false; + t.is(actual, expected); +}); + +test('inBody against "never signed-off-by" should succeed', async t => { + const [actual] = check(await parsed.inBody, 'never', 'Signed-off-by:'); + const expected = true; + t.is(actual, expected); +}); diff --git a/docs/reference-rules.md b/docs/reference-rules.md index 6c79ab0f52..ead3a886a5 100644 --- a/docs/reference-rules.md +++ b/docs/reference-rules.md @@ -263,3 +263,11 @@ Rule configurations are either of type `array` residing on a key with the rule's ```js 0 ``` + +#### signed-off-by +* **condition**: `message` has `value` +* **rule**: `always` +* **value** +```js + 'Signed-off-by:' +```