summaryrefslogtreecommitdiffstats
path: root/scripts/commitcheck.sh
blob: f377bb91284adaa953ea1bfa51df3d02eec3988e (plain)
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
#!/bin/bash

REF="HEAD"

# test a url
function test_url()
{
    url="$1"
    if ! curl --output /dev/null --max-time 60 \
		--silent --head --fail "$url" ; then
        echo "\"$url\" is unreachable"
        return 1
    fi

    return 0
}

# test commit body for length
# lines containing urls are exempt for the length limit.
function test_commit_bodylength()
{
    length="72"
    body=$(git log -n 1 --pretty=%b "$REF" | grep -Ev "http(s)*://" | grep -E -m 1 ".{$((length + 1))}")
    if [ -n "$body" ]; then
        echo "error: commit message body contains line over ${length} characters"
        return 1
    fi

    return 0
}

# check for a tagged line
function check_tagged_line()
{
    regex='^\s*'"$1"':\s[[:print:]]+\s<[[:graph:]]+>$'
    foundline=$(git log -n 1 "$REF" | grep -E -m 1 "$regex")
    if [ -z "$foundline" ]; then
        echo "error: missing \"$1\""
        return 1
    fi

    return 0
}

# check for a tagged line and check that the link is valid
function check_tagged_line_with_url()
{
    regex='^\s*'"$1"':\s\K([[:graph:]]+)$'
    foundline=$(git log -n 1 "$REF" | grep -Po "$regex")
    if [ -z "$foundline" ]; then
        echo "error: missing \"$1\""
        return 1
    fi

    OLDIFS=$IFS
    IFS=$'\n'
    for url in $(echo -e "$foundline"); do
        if ! test_url "$url"; then
            return 1
        fi
    done
    IFS=$OLDIFS

    return 0
}

# check commit message for a normal commit
function new_change_commit()
{
    error=0

    # subject is not longer than 50 characters
    long_subject=$(git log -n 1 --pretty=%s "$REF" | grep -E -m 1 '.{51}')
    if [ -n "$long_subject" ]; then
        echo "error: commit subject over 50 characters"
        error=1
    fi

    # need a signed off by
    if ! check_tagged_line "Signed-off-by" ; then
        error=1
    fi

    # ensure that no lines in the body of the commit are over 72 characters
    if ! test_commit_bodylength ; then
        error=1
    fi

    return $error
}

function is_openzfs_port()
{
    # subject starts with OpenZFS means it's an openzfs port
    subject=$(git log -n 1 --pretty=%s "$REF" | grep -E -m 1 '^OpenZFS')
    if [ -n "$subject" ]; then
        return 0
    fi

    return 1
}

function openzfs_port_commit()
{
    error=0

    # subject starts with OpenZFS dddd
    subject=$(git log -n 1 --pretty=%s "$REF" | grep -E -m 1 '^OpenZFS [[:digit:]]+(, [[:digit:]]+)* - ')
    if [ -z "$subject" ]; then
        echo "error: OpenZFS patch ports must have a subject line that starts with \"OpenZFS dddd - \""
        error=1
    fi

    # need an authored by line
    if ! check_tagged_line "Authored by" ; then
        error=1
    fi

    # need a reviewed by line
    if ! check_tagged_line "Reviewed by" ; then
        error=1
    fi

    # need ported by line
    if ! check_tagged_line "Ported-by" ; then
        error=1
    fi

    # need a url to openzfs commit and it should be valid
    if ! check_tagged_line_with_url "OpenZFS-commit" ; then
        error=1
    fi

    # need a url to illumos issue and it should be valid
    if ! check_tagged_line_with_url "OpenZFS-issue" ; then
        error=1
    fi

    return $error
}

function is_coverity_fix()
{
    # subject starts with Fix coverity defects means it's a coverity fix
    subject=$(git log -n 1 --pretty=%s "$REF" | grep -E -m 1 '^Fix coverity defects')
    if [ -n "$subject" ]; then
        return 0
    fi

    return 1
}

function coverity_fix_commit()
{
    error=0

    # subject starts with Fix coverity defects: CID dddd, dddd...
    subject=$(git log -n 1 --pretty=%s "$REF" |
        grep -E -m 1 'Fix coverity defects: CID [[:digit:]]+(, [[:digit:]]+)*')
    if [ -z "$subject" ]; then
        echo "error: Coverity defect fixes must have a subject line that starts with \"Fix coverity defects: CID dddd\""
        error=1
    fi

    # need a signed off by
    if ! check_tagged_line "Signed-off-by" ; then
        error=1
    fi

    # test each summary line for the proper format
    OLDIFS=$IFS
    IFS=$'\n'
    for line in $(git log -n 1 --pretty=%b "$REF" | grep -E '^CID'); do
        echo "$line" | grep -E '^CID [[:digit:]]+: ([[:graph:]]+|[[:space:]])+ \(([[:upper:]]|\_)+\)' > /dev/null
        # shellcheck disable=SC2181
        if [[ $? -ne 0 ]]; then
            echo "error: commit message has an improperly formatted CID defect line"
            error=1
        fi
    done
    IFS=$OLDIFS

    # ensure that no lines in the body of the commit are over 72 characters
    if ! test_commit_bodylength; then
        error=1
    fi

    return $error
}

if [ -n "$1" ]; then
    REF="$1"
fi

# if openzfs port, test against that
if is_openzfs_port; then
    if ! openzfs_port_commit ; then
        exit 1
    else
        exit 0
    fi
fi

# if coverity fix, test against that
if is_coverity_fix; then
    if ! coverity_fix_commit; then
        exit 1
    else
        exit 0
    fi
fi

# have a normal commit
if ! new_change_commit ; then
    exit 1
fi

exit 0