@@ -20,7 +20,7 @@ class EndsSigned(CommitRule):
20
20
id = "UC2"
21
21
22
22
def validate (self , commit ):
23
- r"""Validate user defined gitlint rules.
23
+ r"""Validates Signed-off-by and Co-authored-by tags as Linux's scripts/checkpatch.pl
24
24
25
25
>>> from gitlint.tests.base import BaseTestCase
26
26
>>> from gitlint.rules import RuleViolation
@@ -36,8 +36,8 @@ def validate(self, commit):
36
36
[]
37
37
>>> msg2 = (
38
38
... f"Title\n\nMessage.\n\n"
39
- ... f"Signed-off -by: name <email>\n\n"
40
- ... f"Co-authored -by: name <email>"
39
+ ... f"Co-authored -by: name <email>\n\n"
40
+ ... f"Signed-off -by: name <email>"
41
41
... )
42
42
>>> commit2 = BaseTestCase.gitcommit(msg2)
43
43
>>> ends_signed.validate(commit2)
@@ -48,8 +48,7 @@ def validate(self, commit):
48
48
>>> commit3 = BaseTestCase.gitcommit(msg3)
49
49
>>> vio3 = ends_signed.validate(commit3)
50
50
>>> vio_msg3 = (
51
- ... f"'Signed-off-by:' not found "
52
- ... f"in commit message body"
51
+ ... f"'Signed-off-by:' not found in commit message body"
53
52
... )
54
53
>>> vio3 == [RuleViolation("UC2", vio_msg3)]
55
54
True
@@ -60,86 +59,92 @@ def validate(self, commit):
60
59
>>> commit4 = BaseTestCase.gitcommit(msg4)
61
60
>>> vio4 = ends_signed.validate(commit4)
62
61
>>> vio_msg4 = (
63
- ... f"Non 'Co-authored-by:' or 'Signed-off-by:'"
64
- ... f" string found following 1st 'Signed-off-by:'"
62
+ ... f"Non 'Co-authored-by:' or 'Signed-off-by:' string found following 1st 'Signed-off-by:'"
65
63
... )
66
64
>>> vio4 == [RuleViolation("UC2", vio_msg4, None, 5)]
67
65
True
68
66
>>> msg5 = (
69
67
... f"Title\n\nMessage.\n\n"
70
- ... f"Co-authored-by: name <email@domain>\n\n"
71
- ... f"a sentence."
68
+ ... f"Co-authored-by: name <email@domain>"
72
69
... )
73
70
>>> commit5 = BaseTestCase.gitcommit(msg5)
74
71
>>> vio5 = ends_signed.validate(commit5)
75
72
>>> vio_msg5 = (
76
- ... f"'Co-authored -by:' found before 'Signed-off -by:'"
73
+ ... f"Missing 'Signed-off -by:' following 'Co-authored -by:'"
77
74
... )
78
- >>> vio5 == [RuleViolation("UC2", vio_msg5, None, 3 )]
75
+ >>> vio5 == [RuleViolation("UC2", vio_msg5, None, 2 )]
79
76
True
80
77
>>> msg6 = (
81
78
... f"Title\n\nMessage.\n\n"
82
- ... f"Signed-off-by: name <email@domain>\n\n"
83
79
... f"Co-authored-by: name <email@domain>\n\n"
84
- ... f"a sentence "
80
+ ... f"Signed-off-by: different name <email@domain> "
85
81
... )
86
82
>>> commit6 = BaseTestCase.gitcommit(msg6)
87
83
>>> vio6 = ends_signed.validate(commit6)
88
84
>>> vio_msg6 = (
89
- ... f"Non 'Co-authored-by:' string found "
90
- ... f"after 1st 'Co-authored-by:'"
85
+ ... f"'Co-authored-by:' and 'Signed-off-by:' name/email do not match"
91
86
... )
92
87
>>> vio6 == [RuleViolation("UC2", vio_msg6, None, 6)]
93
88
True
94
89
"""
95
90
91
+ violations = []
92
+
96
93
# Utilities
97
- def rtn (stmt , i ):
98
- return [ RuleViolation (self .id , stmt , None , i )]
94
+ def vln (stmt , i ):
95
+ return RuleViolation (self .id , stmt , None , i )
99
96
100
97
co_auth = "Co-authored-by:"
101
98
sig = "Signed-off-by:"
102
99
103
100
message_iter = enumerate (commit .message .original .split ("\n " ))
104
101
105
- # Checks commit message contains a `sig` string
106
- found = False
102
+ # Skip ahead to the first signoff or co-author tag
103
+
104
+ # Checks commit message contains a `Signed-off-by` string
107
105
for i , line in message_iter :
108
- # We check that no co-authors are declared before signatures.
109
- if line .startswith (co_auth ):
110
- return rtn (f"'{ co_auth } ' found before '{ sig } '" , i )
111
- if line .startswith (sig ):
112
- found = True
106
+ if line .startswith (sig ) or line .startswith (co_auth ):
113
107
break
114
-
115
- # If no signature was found in the message
116
- # (before `message_iter` ended)
117
- if not found :
118
- return rtn (f"'{ sig } ' not found in commit message body" , None )
119
-
120
- # Checks lines following signature are
121
- # either signatures or co-authors
108
+ else :
109
+ # No signature was found in the message (before `message_iter` ended)
110
+ # This check here can have false-negatives (e.g. if the body ends with only
111
+ # a 'Co-authored-by' tag), but then below will realize that the co-authored-by
112
+ # tag isnt followed by a Signed-off-by tag and fail (and also the DCO check will
113
+ # complain).
114
+ violations .append (vln (f"'{ sig } ' not found in commit message body" , None ))
115
+
116
+ # Check that from here on out we only have signatures and co-authors, and that
117
+ # every co-author is immediately followed by a signature with the same name/email.
122
118
for i , line in message_iter :
123
- if line .startswith (sig ) or not line .strip ():
124
- continue
125
-
126
- # Once we encounter the first co-author,
127
- # we no longer accept signatures
128
119
if line .startswith (co_auth ):
129
- break
120
+ try :
121
+ _ , next_line = next (message_iter )
122
+ except StopIteration :
123
+ violations .append (
124
+ vln (f"Missing '{ sig } ' tag following '{ co_auth } '" , i )
125
+ )
126
+ else :
127
+ if not next_line .startswith (sig ):
128
+ violations .append (
129
+ vln (f"Missing '{ sig } ' tag following '{ co_auth } '" , i + 1 )
130
+ )
131
+ continue
132
+
133
+ if next_line .split (":" )[1 ].strip () != line .split (":" )[1 ].strip ():
134
+ violations .append (
135
+ vln (f"{ co_auth } and { sig } name/email do not match" , i + 1 )
136
+ )
137
+ continue
130
138
131
- return rtn (
132
- f"Non '{ co_auth } ' or '{ sig } ' string found " f"following 1st '{ sig } '" ,
133
- i ,
134
- )
139
+ if line .startswith (sig ) or not line .strip ():
140
+ continue
135
141
136
- # Checks lines following co-author are only additional co-authors.
137
- for i , line in message_iter :
138
- if line and not line .startswith (co_auth ):
139
- return rtn (
140
- f"Non '{ co_auth } ' string found after 1st '{ co_auth } '" ,
142
+ violations .append (
143
+ vln (
144
+ f"Non '{ co_auth } ' or '{ sig } ' string found following 1st '{ sig } '" ,
141
145
i ,
142
146
)
147
+ )
143
148
144
- # Return no errors
145
- return []
149
+ # Return errors
150
+ return violations
0 commit comments