summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaude <noreply@anthropic.com>2026-09-21 19:21:32 +0000
committerClaude <noreply@anthropic.com>2026-09-21 19:21:32 +0000
commit97e9b5e6f18705666b9d622b9eab0b31836321ba (patch)
treebdca9c22b584f1400f266da6c1992e7967ec2e2f
parent95f95acdb551168f2e5a52e000ff62167be266a8 (diff)
publish-apk: fix a SIGPIPE this repo's own README warns about
The zip check used `unzip -l | grep -q`. Under pipefail, grep exits on its first match and SIGPIPEs unzip, so the pipeline fails on a perfectly valid file. It rejected a real 112MB APK as 'not an APK'. That exact trap is in the README's table, written after it broke crash detection in android-launch-check. Knowing about a trap is not the same as not falling into it, which is the argument for these scripts existing at all. Captures the listing first and tests it with `case`, and the new test builds a 300-entry zip so grep matches long before the listing ends -- which is when SIGPIPE actually bites. A single-entry fixture would have passed either way. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
-rwxr-xr-xbin/publish-apk17
-rwxr-xr-xtest/run-tests.sh16
2 files changed, 30 insertions, 3 deletions
diff --git a/bin/publish-apk b/bin/publish-apk
index 91b4705..faf6db8 100755
--- a/bin/publish-apk
+++ b/bin/publish-apk
@@ -39,10 +39,21 @@ ACTUAL_HOST=$(hostname)
|| die "host is '$ACTUAL_HOST', expected '$EXPECT_HOST'. Build and test here, publish there."
[ -f "$APK" ] || die "no such file: $APK"
+
# An .apk is a zip; an HTML error page or a truncated copy is not.
-head -c 2 "$APK" | grep -q "PK" || die "$APK is not a zip archive — not a real APK"
-unzip -l "$APK" 2>/dev/null | grep -q "AndroidManifest.xml" \
- || die "$APK has no AndroidManifest.xml — not an APK"
+#
+# Deliberately NOT `... | grep -q`: under `set -o pipefail` grep exits on its first match and
+# SIGPIPEs the producer, so the pipeline reports failure on a file that is perfectly valid.
+# This script hit exactly that on a real 112MB APK -- the trap is in this repo's own README,
+# and it still caught me. Capture first, test second.
+MAGIC=$(head -c 2 "$APK")
+[ "$MAGIC" = "PK" ] || die "$APK is not a zip archive — not a real APK"
+
+LISTING=$(unzip -l "$APK" 2>/dev/null)
+case "$LISTING" in
+ *AndroidManifest.xml*) ;;
+ *) die "$APK has no AndroidManifest.xml — not an APK" ;;
+esac
[ -d "$DEST_DIR" ] || die "no such directory: $DEST_DIR"
DEST="$DEST_DIR/$NAME"
diff --git a/test/run-tests.sh b/test/run-tests.sh
index a88e024..415ecc3 100755
--- a/test/run-tests.sh
+++ b/test/run-tests.sh
@@ -276,5 +276,21 @@ else
bad "apk validation" "rc=$rc out=$out"
fi
+# 23. A real, large APK must be accepted. The first version of the zip check used
+# `unzip -l | grep -q`, which under pipefail SIGPIPEs unzip and rejects valid files --
+# the very trap this repo's README documents.
+realapk="$tmproot/real.apk"
+mkdir -p "$tmproot/apkbuild/res"
+printf 'manifest' > "$tmproot/apkbuild/AndroidManifest.xml"
+# Enough entries that grep matches well before the listing ends, which is when SIGPIPE bites.
+for i in $(seq 1 300); do printf 'x%.0s' $(seq 1 100) > "$tmproot/apkbuild/res/f$i.bin"; done
+( cd "$tmproot/apkbuild" && zip -q -r "$realapk" . )
+out=$("$BIN/publish-apk" "$realapk" thing.apk --host "$(hostname)" --dir "$tmproot/nonexistent" 2>&1); rc=$?
+if printf '%s' "$out" | grep -qi "not an APK\|not a zip"; then
+ bad "valid APK rejected" "out=$out"
+else
+ ok "accepts a real multi-entry APK (no SIGPIPE from the zip check)"
+fi
+
printf '\n=== summary: %d passed, %d failed ===\n' "$pass" "$fail"
[ "$fail" -eq 0 ]