summaryrefslogtreecommitdiff
path: root/bin/gradle-report
diff options
context:
space:
mode:
Diffstat (limited to 'bin/gradle-report')
-rwxr-xr-xbin/gradle-report110
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" ]