61 lines
2.9 KiB
Bash
61 lines
2.9 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
#
|
||
|
|
# CloserApp — painterResource(non-vector XML) crash scanner.
|
||
|
|
#
|
||
|
|
# WHY THIS EXISTS: Compose's `painterResource(id)` picks its loader by file extension — any `.xml`
|
||
|
|
# drawable is routed through the VectorDrawable loader, which throws at runtime:
|
||
|
|
# java.lang.IllegalArgumentException: Only VectorDrawables and rasterized asset types are supported
|
||
|
|
# the instant such a composable is rendered, if the XML's root is NOT <vector> (e.g. <bitmap>,
|
||
|
|
# <layer-list>, <adaptive-icon>, <shape>, <selector>, <animated-vector>, ...). It compiles fine and is
|
||
|
|
# invisible to unit tests — it only blows up on screen. This shipped once as O-ONBOARD-001: the app-icon
|
||
|
|
# redesign (commit 334cb07) turned ic_launcher_foreground.xml from <vector> into a <bitmap>, crashing
|
||
|
|
# every fresh install on the final onboarding slide + the login screen (both used
|
||
|
|
# painterResource(R.drawable.ic_launcher_foreground)). Recurring QA missed it (logged-in emulators skip
|
||
|
|
# onboarding/auth). Fix: point painterResource at the underlying raster, or use ImageVector/ImageBitmap.
|
||
|
|
#
|
||
|
|
# This scan is a cheap deterministic gate against the whole class. Exit 1 on any finding.
|
||
|
|
#
|
||
|
|
# Usage: ./scripts/painter-xml-scan.sh
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
|
|
SRC="$ROOT/app/src/main/java"
|
||
|
|
RES_GLOB="$ROOT/app/src/main/res/drawable"*
|
||
|
|
|
||
|
|
fail=0
|
||
|
|
found=0
|
||
|
|
|
||
|
|
# Collect every painterResource(R.drawable.NAME) reference in Kotlin source.
|
||
|
|
while IFS= read -r line; do
|
||
|
|
file="${line%%:*}"
|
||
|
|
rest="${line#*:}"
|
||
|
|
lineno="${rest%%:*}"
|
||
|
|
name="$(printf '%s\n' "$rest" | grep -oE 'painterResource\(R\.drawable\.[A-Za-z0-9_]+' | head -1 | sed -E 's/.*R\.drawable\.//')"
|
||
|
|
[ -z "$name" ] && continue
|
||
|
|
|
||
|
|
# Is there an XML drawable resource of that name in any drawable* bucket?
|
||
|
|
xml=""
|
||
|
|
for d in $RES_GLOB; do
|
||
|
|
[ -f "$d/$name.xml" ] && xml="$d/$name.xml" && break
|
||
|
|
done
|
||
|
|
[ -z "$xml" ] && continue # not an XML drawable (raster) → painterResource-safe
|
||
|
|
|
||
|
|
# Root element of the XML drawable.
|
||
|
|
root="$(grep -m1 -oE '<(vector|bitmap|layer-list|shape|selector|adaptive-icon|animated-vector|ripple|inset|clip|rotate|scale|transition|level-list|nine-patch|drawable)' "$xml" | head -1 | tr -d '<')"
|
||
|
|
if [ "$root" != "vector" ]; then
|
||
|
|
found=$((found+1)); fail=1
|
||
|
|
rel="${file#"$ROOT"/}"
|
||
|
|
echo "🔴 CRASH RISK $rel:$lineno painterResource(R.drawable.$name) → <$root> ($name.xml)"
|
||
|
|
echo " painterResource only loads <vector> XML or raster files; a <$root> root throws at render."
|
||
|
|
echo " Fix: reference the underlying raster, or use ImageVector.vectorResource / ImageBitmap.imageResource."
|
||
|
|
fi
|
||
|
|
done < <(grep -rREn 'painterResource\(R\.drawable\.[A-Za-z0-9_]+' "$SRC" 2>/dev/null || true)
|
||
|
|
|
||
|
|
if [ "$fail" -eq 0 ]; then
|
||
|
|
echo "✅ painter-xml-scan: 0 painterResource() calls target a non-<vector> XML drawable."
|
||
|
|
else
|
||
|
|
echo ""
|
||
|
|
echo "❌ painter-xml-scan: $found painterResource(non-vector XML) call(s) — each is a guaranteed runtime crash on render."
|
||
|
|
fi
|
||
|
|
exit $fail
|