-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathcheck-pr-commits.sh
More file actions
executable file
·228 lines (191 loc) · 5.33 KB
/
check-pr-commits.sh
File metadata and controls
executable file
·228 lines (191 loc) · 5.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#!/bin/bash
#############################################################################
# Simple PR commit validator.
#
# Exit with nonzero status if any PR commits (commits between the current
# branch and origin/main do not validate, e.g. are themselves non-subtree
# merge commits, or have the word "fixup" or "squash" in the commit subect,
# etc.
#
# Usage: check-pr-commits.sh [upstream ref]
#
set -e
set -o pipefail
# Fix "dubious ownership" error if necessary before first git command:
if git status 2>&1 >/dev/null | grep -q "dubious ownership" || true; then
git config --global --add safe.directory $(pwd)
fi
origin_main() {
git rev-parse --verify origin/main >/dev/null 2>&1 \
&& echo origin/main \
|| echo origin/master
}
HEAD="HEAD"
BASE=${1:-$(origin_main)}
RESULT=0
ERRORS=()
WARNINGS=()
# ok, not ok unicode symbols:
OK='\u2714'
NOK='\u2718'
WARN='\u26A0'
#############################################################################
# error/warning log and output functions:
color=t
loglevel="error"
if test -n "$color"; then
color_fail='\e[1m\e[31m' # bold red
color_pass='\e[1m\e[32m' # bold green
color_warn='\e[1m\e[33m' # bold yellow
color_reset='\e[0m'
else
color_fail=''
color_pass=''
color_warn=''
color_reset=''
fi
ok() { printf "${color_pass}${OK}${color_reset}"; }
notok() { printf "${color_fail}${NOK}${color_reset}"; }
warning() { printf "${color_warn}${WARN}${color_reset}"; }
log() {
if test "$loglevel" = "error"; then
ERRORS+=("${color_fail}$*${color_reset}")
else
WARNINGS+=("${color_warn}$*${color_reset}")
fi
}
dump_errors() {
printf "\nCommit message validation failed::\n"
for line in "${ERRORS[@]}"; do
printf " $line\n"
done
}
dump_warnings() {
if test ${#WARNINGS} -gt 0; then
printf "\nCommit warnings::\n"
for line in "${WARNINGS[@]}"; do
printf " $line\n"
done
fi
}
#############################################################################
# Tests
is_only_child() {
return $(git rev-list --no-walk --count --merges "$@")
}
is_subtree_merge() {
if git show -s --format=%s $1 | grep -q '^Merge commit .* as .*'; then
return 0
fi
return 1
}
# Return zero if commit is a merge commit (more than one parent)
is_merge_commit() {
if ! is_only_child $1 && ! is_subtree_merge $1; then
log "$1 appears to be a non-subtree merge commit"
return 0
fi
return 1
}
# Return zero if commit appears to be labeled a fixup or squash commit
is_fixup_commit() {
if git show -s --format=%s $1 | egrep -q 'fixup|squash'; then
log "$1 appears to be a fixup/squash commit"
return 0
fi
return 1
}
# Return zero if commit subject length is > N characters
subject_length_exceeds() {
local max=$1
local sha=$2
# Skip for subtree merge commits:
is_subtree_merge $sha && return 1
local len=$(git show -s --format=%s $sha | wc -c)
if test $len -gt $max; then
log "$sha has a subject longer than $max characters"
return 0
fi
return 1
}
# Return zero if commit body line length is > N characters
body_line_length_exceeds() {
local max=$1
local sha=$2
local count=0
local rc=1
# Skip for subtree merge commits:
is_subtree_merge $sha && return 1
git show -s --format=%b | while read line; do
if test ${#line} -gt $max; then
log "${sha} commit body line ${count} ${#line} characters long"
rc=0
fi
done
return $rc
}
# Return zero if commit body is empty
body_is_empty() {
sha=$1
# Skip for subtree merge commits:
is_subtree_merge $sha && return 1
nlines=$(git show -s --format=%b $sha | wc -l)
if test $nlines -le 1; then
log "$sha has empty commit body"
return 0
fi
return 1
}
# Run typos check on PR commit messages
has_typos() {
sha=$1
message=$(git show -s --format=%B $sha)
commit_misspellings=$(echo ${message} | typos --diff - | wc -l)
if test ${commit_misspellings} -ne 0; then
# if typos exist, show them and return 0
log "$(echo ${message} | typos -)"
return 0
fi
return 1
}
# Add more test functions here...
#############################################################################
# Check single commit:
check_commit() {
sha=$1
subject=$(git show -s --format=%s $sha)
symbol="$(ok)"
result=0
# First check for errors:
if is_fixup_commit $sha || \
is_merge_commit $sha || \
subject_length_exceeds 70 $sha || \
body_is_empty $sha || \
body_line_length_exceeds 78 $sha || \
has_typos $sha; then
symbol="$(notok)"
result=1
else
# No errors, check warnings:
loglevel="warn"
if subject_length_exceeds 50 $sha || \
body_line_length_exceeds 72 $sha; then
symbol="$(warning)"
fi
fi
printf " ${symbol} ${sha} ${subject}\n"
return $result
}
#############################################################################
# Main loop:
printf "Validating commits on current branch:\n"
COMMITS=$(git log --format=%h ${BASE}..${HEAD})
for sha in $COMMITS; do
if ! check_commit $sha; then
RESULT=1
fi
done
[ $RESULT = 1 ] && dump_errors
dump_warnings
exit $RESULT
# vi: ts=4 sw=4 expandtab