Skip to content

Commit 0d83c18

Browse files
committed
rules: add co-authored-by-is-trailer
Refs: nodejs/node-core-utils#602
1 parent 1eca2be commit 0d83c18

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
lines changed
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict'
2+
3+
const id = 'co-authored-by-is-trailer'
4+
5+
module.exports = {
6+
id: id,
7+
meta: {
8+
description: 'enforce that "Co-authored-by:" lines are trailers',
9+
recommended: true
10+
},
11+
defaults: {},
12+
options: {},
13+
validate: (context, rule) => {
14+
const parsed = context.toJSON()
15+
const lines = parsed.body.map((line, i) => [line, i])
16+
const re = /^\s*Co-authored-by:/gi
17+
const coauthors = lines.filter(([line]) => re.test(line))
18+
if (coauthors.length !== 0) {
19+
const firstCoauthor = coauthors[0]
20+
const emptyLines = lines.filter(([text]) => text.trim().length === 0)
21+
// There must be at least one empty line, and the last empty line must be
22+
// above the first Co-authored-by line.
23+
const isTrailer = (emptyLines.length !== 0) &&
24+
emptyLines.pop()[1] < firstCoauthor[1]
25+
if (isTrailer) {
26+
context.report({
27+
id: id,
28+
message: 'Co-authored-by is a trailer',
29+
string: '',
30+
level: 'pass'
31+
})
32+
} else {
33+
context.report({
34+
id: id,
35+
message: 'Co-authored-by must be a trailer',
36+
string: firstCoauthor[0],
37+
line: firstCoauthor[1],
38+
column: 0,
39+
level: 'fail'
40+
})
41+
}
42+
} else {
43+
context.report({
44+
id: id,
45+
message: 'no Co-authored-by metadata',
46+
string: '',
47+
level: 'pass'
48+
})
49+
}
50+
}
51+
}
+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
'use strict'
2+
3+
const test = require('tap').test
4+
const Rule = require('../../lib/rules/co-authored-by-is-trailer')
5+
const Commit = require('gitlint-parser-node')
6+
const Validator = require('../../')
7+
8+
test('rule: co-authored-by-is-trailer', (t) => {
9+
t.test('no co-authors', (tt) => {
10+
tt.plan(4)
11+
const v = new Validator()
12+
const context = new Commit({
13+
sha: 'e7c077c610afa371430180fbd447bfef60ebc5ea',
14+
author: {
15+
name: 'Foo',
16+
17+
date: '2016-04-12T19:42:23Z'
18+
},
19+
message: 'test: fix something\n\nfhqwhgads'
20+
}, v)
21+
22+
context.report = (opts) => {
23+
tt.pass('called report')
24+
tt.equal(opts.id, 'co-authored-by-is-trailer', 'id')
25+
tt.equal(opts.message, 'no Co-authored-by metadata', 'message')
26+
tt.equal(opts.level, 'pass', 'level')
27+
}
28+
29+
Rule.validate(context)
30+
})
31+
32+
t.test('no empty lines above', (tt) => {
33+
tt.plan(7)
34+
const v = new Validator()
35+
const context = new Commit({
36+
sha: 'e7c077c610afa371430180fbd447bfef60ebc5ea',
37+
author: {
38+
name: 'Foo',
39+
40+
date: '2016-04-12T19:42:23Z'
41+
},
42+
message: 'test: fix something\nCo-authored-by: Someone <[email protected]>'
43+
}, v)
44+
45+
context.report = (opts) => {
46+
tt.pass('called report')
47+
tt.equal(opts.id, 'co-authored-by-is-trailer', 'id')
48+
tt.equal(opts.message, 'Co-authored-by must be a trailer', 'message')
49+
tt.equal(opts.string, 'Co-authored-by: Someone <[email protected]>', 'string')
50+
tt.equal(opts.line, 0, 'line')
51+
tt.equal(opts.column, 0, 'column')
52+
tt.equal(opts.level, 'fail', 'level')
53+
}
54+
55+
Rule.validate(context)
56+
})
57+
58+
t.test('not trailer', (tt) => {
59+
tt.plan(7)
60+
const v = new Validator()
61+
const context = new Commit({
62+
sha: 'e7c077c610afa371430180fbd447bfef60ebc5ea',
63+
author: {
64+
name: 'Foo',
65+
66+
date: '2016-04-12T19:42:23Z'
67+
},
68+
message: 'test: fix something\n\nSome description.\n\nCo-authored-by: Someone <[email protected]>\n\nReviewed-By: Bar <[email protected]>'
69+
}, v)
70+
71+
context.report = (opts) => {
72+
tt.pass('called report')
73+
tt.equal(opts.id, 'co-authored-by-is-trailer', 'id')
74+
tt.equal(opts.message, 'Co-authored-by must be a trailer', 'message')
75+
tt.equal(opts.string, 'Co-authored-by: Someone <[email protected]>', 'string')
76+
tt.equal(opts.line, 3, 'line')
77+
tt.equal(opts.column, 0, 'column')
78+
tt.equal(opts.level, 'fail', 'level')
79+
}
80+
81+
Rule.validate(context)
82+
})
83+
84+
t.test('not all are trailers', (tt) => {
85+
tt.plan(7)
86+
const v = new Validator()
87+
const context = new Commit({
88+
sha: 'e7c077c610afa371430180fbd447bfef60ebc5ea',
89+
author: {
90+
name: 'Foo',
91+
92+
date: '2016-04-12T19:42:23Z'
93+
},
94+
message: 'test: fix something\n\nSome description.\n\nCo-authored-by: Someone <[email protected]>\n\nCo-authored-by: Someone Else <[email protected]>\nReviewed-By: Bar <[email protected]>'
95+
}, v)
96+
97+
context.report = (opts) => {
98+
tt.pass('called report')
99+
tt.equal(opts.id, 'co-authored-by-is-trailer', 'id')
100+
tt.equal(opts.message, 'Co-authored-by must be a trailer', 'message')
101+
tt.equal(opts.string, 'Co-authored-by: Someone <[email protected]>', 'string')
102+
tt.equal(opts.line, 3, 'line')
103+
tt.equal(opts.column, 0, 'column')
104+
tt.equal(opts.level, 'fail', 'level')
105+
}
106+
107+
Rule.validate(context)
108+
})
109+
110+
t.test('is trailer', (tt) => {
111+
tt.plan(4)
112+
const v = new Validator()
113+
const context = new Commit({
114+
sha: 'e7c077c610afa371430180fbd447bfef60ebc5ea',
115+
author: {
116+
name: 'Foo',
117+
118+
date: '2016-04-12T19:42:23Z'
119+
},
120+
message: 'test: fix something\n\nSome description.\n\nMore description.\n\nCo-authored-by: Someone <[email protected]>\nReviewed-By: Bar <[email protected]>'
121+
}, v)
122+
123+
context.report = (opts) => {
124+
tt.pass('called report')
125+
tt.equal(opts.id, 'co-authored-by-is-trailer', 'id')
126+
tt.equal(opts.message, 'Co-authored-by is a trailer', 'message')
127+
tt.equal(opts.level, 'pass', 'level')
128+
}
129+
130+
Rule.validate(context)
131+
})
132+
133+
t.end()
134+
})

0 commit comments

Comments
 (0)