Skip to content

Commit c4b0c72

Browse files
chore(validator): enforce overall structure
1 parent ee01a3e commit c4b0c72

File tree

4 files changed

+369
-2
lines changed

4 files changed

+369
-2
lines changed

Diff for: README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ Behind the hood, the script use `git log` to list all the commit thus any syntax
185185
See the [open issues](https://github.com/lumapps/commit-msg-validator/issues) for a list of proposed features (and known issues).
186186

187187
- [x] list all the commit, and run validation on each
188-
- [ ] enforce the overall commit message structure
188+
- [x] enforce the overall commit message structure
189189
- [ ] enforce the overall commit header structure
190190
- [ ] enforce the overall commit header lenght
191191
- [ ] enforce the commit type
@@ -206,6 +206,7 @@ Contributions are what make the open source community such an amazing place to b
206206
1. Fork the Project
207207
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
208208
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
209+
4. Run the tests (`bats -j 100 validator.bats`)
209210
4. Push to the Branch (`git push origin feature/AmazingFeature`)
210211
5. Open a Pull Request
211212

Diff for: check.sh

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
#!/bin/bash -e
22

3+
source ./validator.sh
4+
35
COMMITS=`git log --no-merges --pretty="%H" --no-decorate $1`
46

57
while IFS= read -r COMMIT
68
do
79
MESSAGE=`git log -1 --pretty='%B' $COMMIT`
8-
echo "$MESSAGE"
10+
echo "checking commit ${COMMIT}..."
11+
validate "$MESSAGE"
912
done <<< $COMMITS
1013

1114
echo "All commits succesfully checked"

Diff for: validator.bats

+254
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
#!/usr/bin/env bats
2+
3+
source $BATS_TEST_DIRNAME/validator.sh
4+
5+
@test "structure: one trailing line" {
6+
COMMIT="plop plop
7+
"
8+
run validate_overall_structure "$COMMIT"
9+
[ "$status" -eq $ERROR_STRUCTURE ]
10+
}
11+
12+
@test "structure: missing empty line after the header" {
13+
COMMIT="plop plop
14+
plop
15+
16+
plop
17+
"
18+
run validate_overall_structure "$COMMIT"
19+
[ "$status" -eq $ERROR_STRUCTURE ]
20+
}
21+
22+
@test "structure: missing empty line after the header for jira" {
23+
COMMIT="plop plop
24+
ABC-1234
25+
"
26+
run validate_overall_structure "$COMMIT"
27+
[ "$status" -eq $ERROR_STRUCTURE ]
28+
}
29+
30+
@test "structure: missing empty line after the header for broken" {
31+
COMMIT="plop plop
32+
BROKEN:
33+
"
34+
run validate_overall_structure "$COMMIT"
35+
[ "$status" -eq $ERROR_STRUCTURE ]
36+
}
37+
38+
@test "structure: missing empty line after the body with jira ref" {
39+
COMMIT="plop plop
40+
41+
plop
42+
plop
43+
plop
44+
plop
45+
LUM-1234
46+
"
47+
run validate_overall_structure "$COMMIT"
48+
[ "$status" -eq $ERROR_STRUCTURE ]
49+
}
50+
51+
@test "structure: missing empty line after the body with broken stuff" {
52+
COMMIT="plop plop
53+
54+
plop
55+
plop
56+
plop
57+
plop
58+
BROKEN:
59+
"
60+
run validate_overall_structure "$COMMIT"
61+
[ "$status" -eq $ERROR_STRUCTURE ]
62+
}
63+
64+
65+
@test "structure: empty line after the body" {
66+
COMMIT="plop plop
67+
68+
plop
69+
plop
70+
plop
71+
plop
72+
73+
"
74+
run validate_overall_structure "$COMMIT"
75+
[ "$status" -eq $ERROR_STRUCTURE ]
76+
}
77+
78+
@test "structure: valid commit message with header only" {
79+
COMMIT="plop plop"
80+
81+
validate_overall_structure "$COMMIT"
82+
[[ $GLOBAL_HEADER == "plop plop" ]]
83+
[[ $GLOBAL_BODY == "" ]]
84+
[[ $GLOBAL_JIRA == "" ]]
85+
[[ $GLOBAL_FOOTER == "" ]]
86+
}
87+
88+
@test "structure: valid commit message with header and JIRA" {
89+
COMMIT="plop plop
90+
91+
ABC-1234"
92+
93+
validate_overall_structure "$COMMIT"
94+
[[ $GLOBAL_HEADER == "plop plop" ]]
95+
[[ $GLOBAL_BODY == "" ]]
96+
[[ $GLOBAL_JIRA == "ABC-1234" ]]
97+
[[ $GLOBAL_FOOTER == "" ]]
98+
}
99+
100+
@test "structure: valid commit message with header and multiple JIRA" {
101+
COMMIT="plop plop
102+
103+
ABC-1234 DEF-1234"
104+
105+
validate_overall_structure "$COMMIT"
106+
[[ $GLOBAL_HEADER == "plop plop" ]]
107+
[[ $GLOBAL_BODY == "" ]]
108+
[[ $GLOBAL_JIRA == "ABC-1234 DEF-1234" ]]
109+
[[ $GLOBAL_FOOTER == "" ]]
110+
}
111+
112+
@test "structure: valid commit message with header and broken" {
113+
COMMIT="plop plop
114+
115+
BROKEN:
116+
- plop
117+
- plop"
118+
119+
validate_overall_structure "$COMMIT"
120+
[[ $GLOBAL_HEADER == "plop plop" ]]
121+
[[ $GLOBAL_BODY == "" ]]
122+
[[ $GLOBAL_JIRA == "" ]]
123+
[[ $GLOBAL_FOOTER == "- plop\n- plop\n" ]]
124+
}
125+
126+
@test "structure: valid commit message with header, jira and broken" {
127+
COMMIT="plop plop
128+
129+
ABC-1234
130+
BROKEN:
131+
- plop
132+
- plop"
133+
134+
validate_overall_structure "$COMMIT"
135+
[[ $GLOBAL_HEADER == "plop plop" ]]
136+
[[ $GLOBAL_BODY == "" ]]
137+
[[ $GLOBAL_JIRA == "ABC-1234" ]]
138+
[[ $GLOBAL_FOOTER == "- plop\n- plop\n" ]]
139+
}
140+
141+
@test "structure: valid commit message with header and body" {
142+
COMMIT="plop plop
143+
144+
hello"
145+
146+
validate_overall_structure "$COMMIT"
147+
[[ $GLOBAL_HEADER == "plop plop" ]]
148+
[[ $GLOBAL_BODY == "hello\n" ]]
149+
[[ $GLOBAL_JIRA == "" ]]
150+
[[ $GLOBAL_FOOTER == "" ]]
151+
}
152+
153+
@test "structure: valid commit message with header and multiline body" {
154+
COMMIT="plop plop
155+
156+
hello
157+
158+
plopplop
159+
plopplop
160+
161+
toto"
162+
163+
validate_overall_structure "$COMMIT"
164+
[[ $GLOBAL_HEADER == "plop plop" ]]
165+
[[ $GLOBAL_BODY == "hello\nplopplop\nplopplop\ntoto\n" ]]
166+
[[ $GLOBAL_JIRA == "" ]]
167+
[[ $GLOBAL_FOOTER == "" ]]
168+
}
169+
170+
@test "structure: valid commit message with header, multiline body and jira" {
171+
COMMIT="plop plop
172+
173+
hello
174+
175+
plopplop
176+
plopplop
177+
178+
toto
179+
180+
ABC-1234"
181+
182+
validate_overall_structure "$COMMIT"
183+
[[ $GLOBAL_HEADER == "plop plop" ]]
184+
[[ $GLOBAL_BODY == "hello\nplopplop\nplopplop\ntoto\n" ]]
185+
[[ $GLOBAL_JIRA == "ABC-1234" ]]
186+
[[ $GLOBAL_FOOTER == "" ]]
187+
}
188+
189+
@test "structure: valid commit message with header, multiline body and broken" {
190+
COMMIT="plop plop
191+
192+
hello
193+
194+
plopplop
195+
plopplop
196+
197+
toto
198+
199+
BROKEN:
200+
- plop
201+
- plop"
202+
203+
validate_overall_structure "$COMMIT"
204+
[[ $GLOBAL_HEADER == "plop plop" ]]
205+
[[ $GLOBAL_BODY == "hello\nplopplop\nplopplop\ntoto\n" ]]
206+
[[ $GLOBAL_JIRA == "" ]]
207+
[[ $GLOBAL_FOOTER == "- plop\n- plop\n" ]]
208+
}
209+
210+
@test "structure: valid commit message with header, multiline body, jira and broken" {
211+
COMMIT="plop plop
212+
213+
hello
214+
215+
plopplop
216+
plopplop
217+
218+
toto
219+
220+
ABC-1234
221+
BROKEN:
222+
- plop
223+
- plop"
224+
225+
validate_overall_structure "$COMMIT"
226+
[[ $GLOBAL_HEADER == "plop plop" ]]
227+
[[ $GLOBAL_BODY == "hello\nplopplop\nplopplop\ntoto\n" ]]
228+
[[ $GLOBAL_JIRA == "ABC-1234" ]]
229+
[[ $GLOBAL_FOOTER == "- plop\n- plop\n" ]]
230+
}
231+
232+
@test "overall validation invalid structure" {
233+
MESSAGE='plop
234+
plop'
235+
236+
run validate "$MESSAGE"
237+
[[ "$status" -eq $ERROR_STRUCTURE ]]
238+
}
239+
240+
@test "overall validation" {
241+
MESSAGE='feat(scope1): subject
242+
243+
Commit about stuff\"plop \" dezd
244+
245+
plop
246+
247+
LUM-2345
248+
BROKEN:
249+
- plop
250+
- plop'
251+
252+
run validate "$MESSAGE"
253+
[[ "$status" -eq 0 ]]
254+
}

Diff for: validator.sh

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#!/bin/bash -e
2+
3+
readonly JIRA_PATTERN="^([A-Z]{2,4}-[0-9]{1,6} ?)+$"
4+
readonly BROKE_PATTERN="^BROKEN:$"
5+
6+
readonly ERROR_STRUCTURE=1
7+
8+
GLOBAL_HEADER=""
9+
GLOBAL_BODY=""
10+
GLOBAL_JIRA=""
11+
GLOBAL_FOOTER=""
12+
13+
validate_overall_structure() {
14+
local MESSAGE="$1"
15+
16+
local WAITING_HEADER=0
17+
local WAITING_EMPTY=1
18+
local START_TEXT=2
19+
local READING_BODY=3
20+
local READING_BROKEN=4
21+
local READING_FOOTER=5
22+
23+
local STATE="$WAITING_HEADER"
24+
25+
while IFS= read -r LINE ; do
26+
27+
if [[ $STATE -eq $WAITING_HEADER ]]; then
28+
GLOBAL_HEADER="$LINE"
29+
STATE="$WAITING_EMPTY"
30+
31+
elif [[ $STATE -eq $WAITING_EMPTY ]]; then
32+
if [[ $LINE != "" ]]; then
33+
echo -e "missing empty line in commit message between header and body or body and footer"
34+
exit $ERROR_STRUCTURE
35+
fi
36+
STATE="$START_TEXT"
37+
38+
elif [[ $STATE -eq $START_TEXT ]]; then
39+
if [[ $LINE = "" ]]; then
40+
echo -e "double empty line is not allowed"
41+
exit $ERROR_STRUCTURE
42+
fi
43+
44+
if [[ $LINE =~ $BROKE_PATTERN ]]; then
45+
STATE="$READING_FOOTER"
46+
elif [[ $LINE =~ $JIRA_PATTERN ]]; then
47+
STATE="$READING_BROKEN"
48+
GLOBAL_JIRA=${BASH_REMATCH[0]}
49+
else
50+
STATE="$READING_BODY"
51+
GLOBAL_BODY="${GLOBAL_BODY}${LINE}\n"
52+
fi
53+
54+
elif [[ $STATE -eq $READING_BODY ]]; then
55+
if [[ $LINE =~ $BROKE_PATTERN ]]; then
56+
echo -e "missing empty line before broke part"
57+
exit $ERROR_STRUCTURE
58+
fi
59+
60+
if [[ $LINE =~ $JIRA_PATTERN ]]; then
61+
echo -e "missing empty line before JIRA reference"
62+
exit $ERROR_STRUCTURE
63+
fi
64+
65+
if [[ $LINE = "" ]]; then
66+
STATE=$START_TEXT
67+
else
68+
GLOBAL_BODY="${GLOBAL_BODY}${LINE}\n"
69+
fi
70+
71+
elif [[ $STATE -eq $READING_BROKEN ]]; then
72+
if [[ $LINE =~ $BROKE_PATTERN ]]; then
73+
STATE="$READING_FOOTER"
74+
else
75+
echo -e "only broken part could be after the JIRA reference"
76+
exit $ERROR_STRUCTURE
77+
fi
78+
79+
elif [[ $STATE -eq $READING_FOOTER ]]; then
80+
if [[ $LINE = "" ]]; then
81+
echo -e "no empty line allowed in broken part"
82+
exit $ERROR_STRUCTURE
83+
fi
84+
85+
GLOBAL_FOOTER="${GLOBAL_FOOTER}${LINE}\n"
86+
87+
else
88+
echo -e "unknown state in parsing machine"
89+
exit $ERROR_STRUCTURE
90+
fi
91+
92+
done <<< "$MESSAGE"
93+
94+
if [[ $STATE -eq $START_TEXT ]]; then
95+
echo -e "new line at the end of the commit is not allowed"
96+
exit $ERROR_STRUCTURE
97+
fi
98+
}
99+
100+
validate() {
101+
local COMMIT_MSG="$1"
102+
103+
validate_overall_structure "$COMMIT_MSG"
104+
105+
local HEADER="$GLOBAL_HEADER"
106+
local BODY="$GLOBAL_BODY"
107+
local JIRA="$GLOBAL_JIRA"
108+
local FOOTER="$GLOBAL_FOOTER"
109+
}

0 commit comments

Comments
 (0)