android: the icon gate answered fail / pass / fail to the same APK
Round 44's verify-icon.sh had never run inside deploy.sh — only by hand — and it does two things there that it cannot do standalone. deploy.sh exports MSYS_NO_PATHCONV=1 for its adb calls, and that switch is global, so the APK path reached aapt2 as /f/Coding/... Windows binaries cannot open an MSYS path, and aapt2's answer is "failed opening zip: I/O error", which reads exactly like a corrupt APK rather than like a path. The four aapt2 calls now get cygpath -w explicitly; unzip and cmp keep the MSYS form, because they want that one. The second is worse, because it is intermittent. Every membership test was `echo "$VAR" | grep -q PATTERN`, under the `set -o pipefail` at the top. grep -q exits the moment it matches; echo is still pushing 70 KB of unzip listing into a pipe that just closed, takes SIGPIPE, exits 141 — and pipefail promotes the writer's status to the pipeline, so a check that FOUND what it was looking for reports failure. Whether it fires depends on where the match falls in the stream, so three runs against one unchanged APK gave fail, pass, fail, each naming a different missing file. Six of those, now herestrings: no writer process, nothing to kill. Eight runs green afterwards. Worth saying plainly: the APK was correct the entire time. The gate was wrong, and a gate that is green at random is worse than no gate, because the failure it invents points at the build.
This commit is contained in:
1 parent
da4abeb8c9
commit
c36ec439ce
1 file changed
+27
-10
@@ -37,6 +37,15 @@
|
||||
# If anyone ever sets isCrunchPngs = true on the debug build type, the debug
|
||||
# path fails on byte-equality with the file named; --crunched is the answer.
|
||||
set -uo pipefail
|
||||
# Every membership test below reads its haystack from a HERESTRING, never from
|
||||
# `echo "$VAR" | grep -q`. Measured 2026-09-15: that pipeline plus the
|
||||
# `pipefail` above is a race. `grep -q` exits the instant it matches, `echo` is
|
||||
# still pushing 70 KB of `unzip -l` down a closed pipe, takes SIGPIPE and exits
|
||||
# 141 — and pipefail promotes the dead writer's status to the whole pipeline,
|
||||
# so a check that FOUND what it wanted reports failure. Whether it fires
|
||||
# depends on where the match falls in the stream, so three runs against one
|
||||
# unchanged APK gave fail / pass / fail. A herestring has no writer process and
|
||||
# nothing to kill.
|
||||
APK="${1:-}"
|
||||
test -n "$APK" || { echo "usage: verify-icon.sh <apk> [--crunched]"; exit 2; }
|
||||
# resolved before the cd, so a relative path typed by hand still means what it
|
||||
@@ -102,6 +111,14 @@ SDKU=""
|
||||
[ -n "${ANDROID_HOME:-}" ] && SDKU="$(cygpath -u "$ANDROID_HOME" 2>/dev/null || echo "")"
|
||||
[ -z "$SDKU" ] && [ -n "${LOCALAPPDATA:-}" ] && SDKU="$(cygpath -u "$LOCALAPPDATA" 2>/dev/null || echo "")/Android/Sdk"
|
||||
A2="$(ls -1 "$SDKU"/build-tools/*/aapt2.exe 2>/dev/null | sort -V | tail -1 || true)"
|
||||
# aapt2 is a WINDOWS binary and $APK is an MSYS path. Normally MSYS rewrites
|
||||
# one into the other on the way across, but deploy.sh exports
|
||||
# MSYS_NO_PATHCONV=1 for the adb calls, and that switch is global — so inside
|
||||
# a deploy the path arrives as /f/Coding/... and aapt2 answers "failed opening
|
||||
# zip: I/O error", which reads exactly like a corrupt APK. Hand aapt2 the
|
||||
# Windows form explicitly and keep $APK for unzip and cmp, which want the
|
||||
# MSYS one. (Checked standalone this never fires; it only bites under deploy.)
|
||||
APKW="$(cygpath -w "$APK" 2>/dev/null || echo "$APK")"
|
||||
|
||||
# 2. the ground, in BOTH build types. Not "is the file in the APK" — the
|
||||
# adaptive icon's <background> is a REFERENCE, so what matters is what it
|
||||
@@ -113,7 +130,7 @@ A2="$(ls -1 "$SDKU"/build-tools/*/aapt2.exe 2>/dev/null | sort -V | tail -1 || t
|
||||
# of a path: a release APK has had res/mipmap-anydpi-v26/ic_launcher.xml
|
||||
# renamed to res/BW.xml, and the table is the only thing that still knows.
|
||||
TBL=""
|
||||
[ -n "$A2" ] && { TBL="$("$A2" dump resources "$APK" 2>/dev/null)" || bad "aapt2 could not read the resource table of $APK"; }
|
||||
[ -n "$A2" ] && { TBL="$("$A2" dump resources "$APKW" 2>/dev/null)" || bad "aapt2 could not read the resource table of $APK"; }
|
||||
# name [config] -> the file that resource points at. The config is not
|
||||
# optional detail: mipmap/ic_launcher has SIX entries — the anydpi adaptive
|
||||
# XML and five legacy PNGs — so a name-only lookup hands back
|
||||
@@ -130,12 +147,12 @@ name_of() { echo "$TBL" | awk -v id="$1" '$1 == "resource" && $2 == id { print $
|
||||
if [ -z "$TBL" ]; then
|
||||
echo "== ground: NOT CHECKED — no aapt2 under ${SDKU:-(no SDK path)}/build-tools."
|
||||
echo " What <background> points at is a resource id; only the table reads it."
|
||||
elif echo "$TBL" | grep -q "0_resource_name_obfuscated"; then
|
||||
elif grep -q "0_resource_name_obfuscated" <<< "$TBL"; then
|
||||
echo "== ground: NOT CHECKED — this APK's resource names are collapsed."
|
||||
else
|
||||
ADAPT="$(path_of mipmap/ic_launcher anydpi)"
|
||||
test -n "$ADAPT" || bad "mipmap/ic_launcher has no anydpi entry in this APK — the adaptive icon is not in it, so $RES never reached src-tauri/gen/android/app/src/main/res"
|
||||
BGID="$("$A2" dump xmltree "$APK" --file "$ADAPT" 2>/dev/null \
|
||||
BGID="$("$A2" dump xmltree "$APKW" --file "$ADAPT" 2>/dev/null \
|
||||
| awk '/E: background/ { f = 1 } f && /android:drawable\(/ { sub(/.*=@?/, ""); print; exit }')"
|
||||
test -n "$BGID" || bad "the adaptive icon in this APK ($ADAPT) declares no <background>"
|
||||
BGNAME="$(name_of "$BGID")"
|
||||
@@ -147,7 +164,7 @@ else
|
||||
# the $name__N filenames aapt2 happens to give them this version.
|
||||
GPATH="$(path_of drawable/ic_launcher_ground)"
|
||||
test -n "$GPATH" || bad "drawable/ic_launcher_ground resolves to no file in this APK"
|
||||
GTREE="$("$A2" dump xmltree "$APK" --file "$GPATH" 2>/dev/null)" || bad "aapt2 cannot read $GPATH out of this APK"
|
||||
GTREE="$("$A2" dump xmltree "$APKW" --file "$GPATH" 2>/dev/null)" || bad "aapt2 cannot read $GPATH out of this APK"
|
||||
IDS="$(echo "$GTREE" | grep -oE 'fillColor\(0x[0-9a-f]+\)=@0x[0-9a-f]+' | grep -oE '0x[0-9a-f]+$')"
|
||||
NFILL="$(echo "$IDS" | grep -c . || true)"
|
||||
[ "$NFILL" = 2 ] || bad "the ground in this APK has $NFILL gradient fills, not 2 — concept 04 is a surface and the glow over it"
|
||||
@@ -155,10 +172,10 @@ else
|
||||
for id in $IDS; do
|
||||
g="$(path_of "$(name_of "$id")")"
|
||||
test -n "$g" || bad "the ground's gradient $id has no file in the resource table"
|
||||
STOPS="$STOPS $("$A2" dump xmltree "$APK" --file "$g" | grep -oE 'color\(0x[0-9a-f]+\)=#[0-9a-f]{8}' | grep -oE '[0-9a-f]{8}$' | tr '\n' ' ')"
|
||||
STOPS="$STOPS $("$A2" dump xmltree "$APKW" --file "$g" | grep -oE 'color\(0x[0-9a-f]+\)=#[0-9a-f]{8}' | grep -oE '[0-9a-f]{8}$' | tr '\n' ' ')"
|
||||
done
|
||||
for c in ff0a2330 ff07141f ff040a10 2e31c0cb 0d1f8fc9 0007141f; do
|
||||
echo "$STOPS" | grep -qw "$c" || bad "the ground in this APK has lost concept 04's stop #$c"
|
||||
grep -qw "$c" <<< "$STOPS" || bad "the ground in this APK has lost concept 04's stop #$c"
|
||||
done
|
||||
echo "== ground: <background> = $BGNAME, concept 04's six stops, read back out of the APK"
|
||||
fi
|
||||
@@ -172,16 +189,16 @@ if [ "$CRUNCH" = 0 ]; then
|
||||
# Nothing but the overlay makes a mipmap-anydpi-v26 file, so its absence
|
||||
# is exactly the failure this script exists for.
|
||||
for x in ic_launcher ic_launcher_round; do
|
||||
echo "$LIST" | grep -q " res/mipmap-anydpi-v26/$x\.xml$" \
|
||||
grep -q " res/mipmap-anydpi-v26/$x\.xml$" <<< "$LIST" \
|
||||
|| bad "res/mipmap-anydpi-v26/$x.xml is not in the APK — $RES never reached src-tauri/gen/android/app/src/main/res"
|
||||
done
|
||||
# and the ground by name as well. In release the paths are shortened
|
||||
# away and only the table above can find it; here it is still a file.
|
||||
echo "$LIST" | grep -q " res/drawable/ic_launcher_ground\.xml$" \
|
||||
grep -q " res/drawable/ic_launcher_ground\.xml$" <<< "$LIST" \
|
||||
|| bad "res/drawable/ic_launcher_ground.xml is not in the APK — the adaptive background never reached src-tauri/gen/android/app/src/main/res"
|
||||
for d in $DPI; do for n in $NAMES; do
|
||||
got="res/mipmap-$d-v4/$n.png"
|
||||
echo "$LIST" | grep -q " $got$" || bad "$got is not in the APK"
|
||||
grep -q " $got$" <<< "$LIST" || bad "$got is not in the APK"
|
||||
unzip -p "$APK" "$got" | cmp -s - "$RES/mipmap-$d/$n.png" \
|
||||
|| bad "$got in the APK is not $RES/mipmap-$d/$n.png — a stale res in gen/, the overlay lost a file, or this build crunched its PNGs (then: --crunched)"
|
||||
done; done
|
||||
@@ -207,7 +224,7 @@ MAP="$(echo "$TBL" | awk '
|
||||
# the other half of the same optimization pass: with resource shrinking on,
|
||||
# aapt2 also collapses the NAMES, and then even the table cannot say which
|
||||
# entry is the launcher icon
|
||||
if echo "$MAP" | grep -q "0_resource_name_obfuscated"; then
|
||||
if grep -q "0_resource_name_obfuscated" <<< "$MAP"; then
|
||||
echo "== icon: NOT CHECKED — this APK's resource names are collapsed"
|
||||
echo " (aapt2 optimize --collapse-resource-names, which comes with resource"
|
||||
echo " shrinking). Nothing left to match on. Unverified here, not missing."
|
||||
|
||||
Reference in new issue
Block a user