diff options
Diffstat (limited to 'bin/gradle-report')
| -rwxr-xr-x | bin/gradle-report | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/bin/gradle-report b/bin/gradle-report new file mode 100755 index 0000000..c59945a --- /dev/null +++ b/bin/gradle-report @@ -0,0 +1,110 @@ +#!/usr/bin/env bash +# gradle-report <project-dir> <detekt|lint> -- [gradle args] +# +# Runs a static-analysis task and reports from its XML report, refusing to speak if the report +# is older than the run. +# +# Exists because detekt has a failure mode that looks exactly like findings: an invalid config +# property makes it abort BEFORE analysing anything, leaving the previous run's XML on disk. +# Three times in one session that stale file was read as the current result, and the line +# numbers never moved because the analysis never happened. The only honest signal was the exit +# code, which said "failed" while the report said "here are your 8 findings". +set -uo pipefail + +die() { echo "gradle-report: $1" >&2; exit 2; } + +[ $# -ge 2 ] || die "usage: gradle-report <project-dir> <detekt|lint> [-- <gradle args>]" + +PROJECT_DIR=$(cd "$1" 2>/dev/null && pwd) || die "no such project directory: $1" +KIND="$2" +shift 2 +[ "${1:-}" = "--" ] && shift +EXTRA=("$@") + +case "$KIND" in + detekt) TASK=":app:detekt"; REPORT="$PROJECT_DIR/app/build/reports/detekt/detekt.xml" ;; + lint) TASK=":app:lintDebug"; REPORT="$PROJECT_DIR/app/build/lint-results-debug.xml" ;; + *) die "kind must be 'detekt' or 'lint', got '$KIND'" ;; +esac + +[ -x "$PROJECT_DIR/gradlew" ] || die "no gradlew in $PROJECT_DIR" + +# Marker predates the run, so any report not newer than it is stale. +MARKER=$(mktemp) +trap 'rm -f "$MARKER"' EXIT +sleep 1 + +LOG=$(mktemp) +( cd "$PROJECT_DIR" && ./gradlew "$TASK" "${EXTRA[@]}" ) >"$LOG" 2>&1 +STATUS=$? + +echo "=== gradle-report ($KIND) ===" +echo "project : $PROJECT_DIR" +echo "task : $TASK" +echo "exit : $STATUS" + +# An invalid config aborts detekt before it analyses anything. Name it, because the report on +# disk will be the previous run's and will look perfectly reasonable. +if grep -qiE "is misspelled or does not exist|configuration file is invalid|invalid config" "$LOG"; then + echo + echo "CONFIG INVALID — analysis never ran. Offending properties:" + grep -iE "is misspelled or does not exist|configuration file is invalid|invalid config" "$LOG" | sed 's/^/ /' | sort -u + echo + echo "result : FAIL (config rejected; any report on disk is from an earlier run)" + rm -f "$LOG" + exit 1 +fi + +if [ ! -f "$REPORT" ]; then + echo "report : MISSING ($REPORT)" + echo "result : $([ $STATUS -eq 0 ] && echo 'PASS (no report produced, nothing to read)' || echo 'FAIL')" + rm -f "$LOG" + exit $STATUS +fi + +if [ "$REPORT" -ot "$MARKER" ]; then + echo "report : STALE — $REPORT predates this run" + echo "result : FAIL — refusing to report numbers that describe older code" + rm -f "$LOG" + exit 1 +fi + +FINDINGS=$(python3 - "$REPORT" "$KIND" <<'PY' +import sys, xml.etree.ElementTree as ET +path, kind = sys.argv[1], sys.argv[2] +try: + root = ET.parse(path).getroot() +except Exception as e: + print(f"UNREADABLE {e}") + raise SystemExit +rows = [] +if kind == "detekt": + for f in root.iter("file"): + for err in f.iter("error"): + rows.append(f"{f.get('name','').split('/')[-1]}:{err.get('line')} " + f"[{(err.get('source') or '').split('.')[-1]}] {(err.get('message') or '')[:90]}") +else: + for issue in root.iter("issue"): + if issue.get("severity", "").lower() not in ("error", "fatal"): + continue + loc = next(issue.iter("location"), None) + where = f"{(loc.get('file','') if loc is not None else '').split('/')[-1]}:{loc.get('line') if loc is not None else '?'}" + rows.append(f"{where} [{issue.get('id')}] {(issue.get('message') or '')[:90]}") +print(f"COUNT {len(rows)}") +for r in rows: + print(f" {r}") +PY +) + +echo "report : fresh" +echo "$FINDINGS" | head -1 | sed 's/^COUNT /findings: /' +echo "$FINDINGS" | tail -n +2 + +COUNT=$(echo "$FINDINGS" | head -1 | awk '{print $2}') +if [ "$COUNT" = "0" ] && [ $STATUS -eq 0 ]; then + echo "result : PASS (0 findings, gradle exit 0)" +else + echo "result : FAIL (${COUNT:-?} findings, gradle exit $STATUS)" +fi +rm -f "$LOG" +[ $STATUS -eq 0 ] && [ "$COUNT" = "0" ] |
