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
|
#!/usr/bin/env bash
# Copyright (C) 2016 Kernc, Google Inc., authors, and contributors
# Licensed under http://www.apache.org/licenses/LICENSE-2.0
# Created By: miha@reciprocitylabs.com
set -o pipefail
set -o nounset
set -o errexit
ARG1=${1:-}
GIT_REPO="$(pwd)"
TMP_REPO="$GIT_REPO/$(mktemp -d pylint_diff.XXXXXXX)"
CACHE_DIR="$GIT_REPO/.pylint_cache"
UNCOMMITED_PATCH="$TMP_REPO/uncommited.patch"
SCRIPT=$(basename "$0")
PYLINT="$(command -v pylint 2>/dev/null || true)"
RADON="$(command -v radon 2>/dev/null || true)"
PYLINT_ARGS="--msg-template='{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}'"
RADON_ARGS='cc --min C --no-assert --show-closures --show-complexity --average'
trap "status=\$?; cd '$GIT_REPO'; rm -rf '$TMP_REPO'; exit \$status" EXIT
mkdir -p "$CACHE_DIR"
print_help ()
{
echo "
Usage: $SCRIPT [TEST_COMMIT | -h]
This script will compare pylint error count from two different commits.
Note: all changes that are not committed will be ignored.
The script will work only if the current commit is a merge commit, or if the
second test_commit argument is provided.
Given the commit tree:
D---E---F---G---H
\\ /
A---B---C
- Running '$SCRIPT' on H will check the diff between G and H.
- Running '$SCRIPT F' on H will check the diff between F and H.
- Running '$SCRIPT F' on C will check the diff between E and C. The E commit is
set by the merge base of the current head and the specified commit F.
"
exit 0
}
case $ARG1 in -h|--help) print_help ; esac
if [ ! "$PYLINT$RADON" ]; then
echo 'Error: pylint and/or radon required'
exit 3
fi
# Make a local clone: prevents copying of objects
# Handle shallow git clones
is_shallow=$([ -f "$GIT_REPO/.git/shallow" ] && echo true || echo)
if [ "$is_shallow" ]; then
mv "$GIT_REPO/.git/shallow" "$GIT_REPO/.git/shallow-bak"
fi
git clone -q --local --depth=50 "$GIT_REPO" "$TMP_REPO" 2>/dev/null
if [ "$is_shallow" ]; then
mv "$GIT_REPO/.git/shallow-bak" "$GIT_REPO/.git/shallow"
cp "$GIT_REPO/.git/shallow" "$TMP_REPO/.git/shallow"
fi
# Move over any modified but uncommited files ...
if ! git diff-index --quiet HEAD; then
git stash save -q --keep-index
git stash show -p stash@\{0\} > "$UNCOMMITED_PATCH"
git stash pop -q --index
fi
cd "$TMP_REPO"
# ... and commit them
if [ "$(cat "$UNCOMMITED_PATCH" 2>/dev/null || true)" ]; then
git apply "$UNCOMMITED_PATCH"
git commit -a -m 'Commit changed files'
was_dirty='+'
fi >/dev/null 2>&1
git reset --hard -q HEAD
CURRENT_COMMIT=$(git rev-parse HEAD)
if [ "$ARG1" ]; then
PREVIOUS_COMMIT=$(git merge-base HEAD "$ARG1")
else
PREVIOUS_COMMIT=$(git show --pretty=raw HEAD |
awk '/^parent /{ print $2; exit }')
fi
echo
echo "Comparing commits ${CURRENT_COMMIT:0:10}${was_dirty:-} and ${PREVIOUS_COMMIT:0:10}"
CHANGED_FILES=$(git diff --name-only $CURRENT_COMMIT $PREVIOUS_COMMIT |
grep "\.py$" || true )
[ ! "$(command -v md5sum 2>/dev/null)" ] && md5sum() { md5; } # for OS X
CHANGED_FILES_HASH=$(echo "$CHANGED_FILES" | md5sum | cut -d ' ' -f 1)
if [ ! "$CHANGED_FILES" ]; then
echo "No python files changed. Skipping lint checks."
exit 0
fi
echo
echo "Comparing files"
echo "==============="
echo "$CHANGED_FILES"
echo
# Run pylint on the old and new code, to compare the quality.
# If pylint is run multiple times it will store the previous results and show
# the change in quality with a non-negative number if code was improved or not
# changed, and a negative number if more code issues have been introduced.
checkout ()
{
{ git checkout -q "$1"
git reset --hard -q HEAD
} 2>/dev/null
}
Number_of_issues ()
{
cached="$1"
{ cat "$cached" 2>/dev/null ||
echo "$CHANGED_FILES" |
xargs "$PYLINT" "$PYLINT_ARGS" |
tee "$cached"
} | awk -F'[\\. ]' '/^Your code has been rated at /{ print $7 }' || true
}
Cyclomatic_complexity ()
{
cached="$1"
{ cat "$cached" 2>/dev/null ||
echo "$CHANGED_FILES" |
xargs "$RADON" $RADON_ARGS |
tee "$cached"
} | awk -F'[()]' '/ .+\([0-9]+\)$/ { tot += $2 } END { print tot }' || true
}
Get_diffable ()
{
sed -E "/$diff_block_end/,\$d" |
sort |
sed -E "s/$match_line_num/$replace_line_num/"
}
for check in \
'Pylint,Number_of_issues,^Report$,^([^:]+:)[0-9]+:,\\1,^\\+' \
'radon,Cyclomatic_complexity,^[0-9]+ blocks,^( +[MCF]) [0-9:]+,\\1:,^[+-]'
do
IFS=',' read check \
func \
diff_block_end \
match_line_num \
replace_line_num \
show_diff_lines < <(echo "$check")
# If command not available, skip it
if [ ! "$(eval echo \$$(echo $check | tr '[:lower:]' '[:upper:]') )" ]; then
continue
fi
cached_previous="$CACHE_DIR/previous.$check.$PREVIOUS_COMMIT.$CHANGED_FILES_HASH"
cached_current="$CACHE_DIR/current.$check.$CURRENT_COMMIT.$CHANGED_FILES_HASH"
[ -f "$cached_previous" ] || rm -r "$CACHE_DIR/previous."* 2>/dev/null || true
[ -f "$cached_current" ] || rm -r "$CACHE_DIR/current."* 2>/dev/null || true
[ -f "$cached_previous" ] || checkout $PREVIOUS_COMMIT
RESULT_PARENT=$($func "$cached_previous")
[ -f "$cached_current" ] || checkout $CURRENT_COMMIT
RESULT_CURRENT=$($func "$cached_current")
echo
echo "$check result"
echo "================================================================="
cat "$cached_current"
echo
echo
echo "$check diff"
echo "================================================================="
diff --unified=0 --minimal \
<(Get_diffable < "$cached_previous") \
<(Get_diffable < "$cached_current") |
grep -E "$show_diff_lines" | tail -n +3 || true
echo
echo
echo "$check results"
echo "================================================================="
echo "${func//_/ } on parent commit: $RESULT_PARENT"
echo "${func//_/ } on the pull request: $RESULT_CURRENT ($(printf "%+d" $((RESULT_CURRENT - RESULT_PARENT))))"
echo
if awk "BEGIN { exit ${RESULT_CURRENT:-0} > ${RESULT_PARENT:-0} ? 0 : 1 }"; then
echo "FAIL: ${func//_/ } got worse"
exit 1
fi
done
echo "OK"
|