1 |
Add a check for common mistakes in commit messages. For now, it is |
2 |
pretty rough and exits immediately but it should be integrated with |
3 |
the editor in the future. |
4 |
--- |
5 |
repoman/pym/repoman/actions.py | 68 ++++++++++++++++++++++++++++++++++++++++++ |
6 |
1 file changed, 68 insertions(+) |
7 |
|
8 |
diff --git a/repoman/pym/repoman/actions.py b/repoman/pym/repoman/actions.py |
9 |
index b76a6e466..97a71d0f5 100644 |
10 |
--- a/repoman/pym/repoman/actions.py |
11 |
+++ b/repoman/pym/repoman/actions.py |
12 |
@@ -124,6 +124,15 @@ class Actions(object): |
13 |
|
14 |
commitmessage = commitmessage.rstrip() |
15 |
|
16 |
+ res, expl = self.verify_commit_message(commitmessage) |
17 |
+ if not res: |
18 |
+ print(bad("RepoMan does not like your commit message:")) |
19 |
+ print(expl) |
20 |
+ if self.options.force: |
21 |
+ print('(but proceeding due to --force)') |
22 |
+ else: |
23 |
+ sys.exit(1) |
24 |
+ |
25 |
# Update copyright for new and changed files |
26 |
year = time.strftime('%Y', time.gmtime()) |
27 |
for fn in chain(mynew, mychanged): |
28 |
@@ -585,3 +594,62 @@ class Actions(object): |
29 |
print("* no commit message? aborting commit.") |
30 |
sys.exit(1) |
31 |
return commitmessage |
32 |
+ |
33 |
+ footer_re = re.compile(r'^[\w-]+:') |
34 |
+ |
35 |
+ def verify_commit_message(self, msg): |
36 |
+ """ |
37 |
+ Check whether the message roughly complies with GLEP66. Returns |
38 |
+ (True, None) if it does, (False, <explanation>) if it does not. |
39 |
+ """ |
40 |
+ |
41 |
+ problems = [] |
42 |
+ paras = msg.strip().split('\n\n') |
43 |
+ summary = paras.pop(0) |
44 |
+ |
45 |
+ if '\n' in summary.strip(): |
46 |
+ problems.append('commit message must start with a *single* line of summary, followed by empty line') |
47 |
+ if ':' not in summary: |
48 |
+ problems.append('summary line must start with a logical unit name, e.g. "cat/pkg:"') |
49 |
+ # accept 69 overall or unit+50, in case of very long package names |
50 |
+ if len(summary.strip()) > 69 and len(summary.split(':', 1)[-1]) > 50: |
51 |
+ problems.append('summary line is too long (max 69 characters)') |
52 |
+ |
53 |
+ multiple_footers = False |
54 |
+ gentoo_bug_used = False |
55 |
+ bug_closes_without_url = False |
56 |
+ body_too_long = False |
57 |
+ |
58 |
+ found_footer = False |
59 |
+ for p in paras: |
60 |
+ lines = p.splitlines() |
61 |
+ # if all lines look like footer material, we assume it's footer |
62 |
+ # else, we assume it's body text |
63 |
+ if all(self.footer_re.match(l) for l in lines if l.strip()): |
64 |
+ # multiple footer-like blocks? |
65 |
+ if found_footer: |
66 |
+ multiple_footers = True |
67 |
+ found_footer = True |
68 |
+ for l in lines: |
69 |
+ if l.startswith('Gentoo-Bug'): |
70 |
+ gentoo_bug_used = True |
71 |
+ elif l.startswith('Bug:') or l.startswith('Closes:'): |
72 |
+ if 'http://' not in l and 'https://' not in l: |
73 |
+ bug_closes_without_url = True |
74 |
+ else: |
75 |
+ for l in lines: |
76 |
+ if len(l.strip()) > 72: |
77 |
+ body_too_long = True |
78 |
+ |
79 |
+ if multiple_footers: |
80 |
+ problems.append('multiple footer-style blocks found, please combine them into one') |
81 |
+ if gentoo_bug_used: |
82 |
+ problems.append('please replace Gentoo-Bug with GLEP 66-compliant Bug/Closes') |
83 |
+ if bug_closes_without_url: |
84 |
+ problems.append('Bug/Closes footers require full URL') |
85 |
+ if body_too_long: |
86 |
+ problems.append('body lines should be wrapped at 72 characters') |
87 |
+ |
88 |
+ if problems: |
89 |
+ return (False, '\n'.join('- %s' % x for x in problems)) |
90 |
+ return (True, None) |
91 |
-- |
92 |
2.16.1 |