diff options
Diffstat (limited to 'bin/publish-apk')
| -rwxr-xr-x | bin/publish-apk | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/bin/publish-apk b/bin/publish-apk new file mode 100755 index 0000000..91b4705 --- /dev/null +++ b/bin/publish-apk @@ -0,0 +1,91 @@ +#!/usr/bin/env bash +# publish-apk <apk> <published-name> [--host tungsten] [--dir /site/static.terst.org/public/files] +# +# Publishes a built APK to the static site, verifying every step that has gone wrong before: +# the host, that the file is really an APK, that what landed is byte-identical to what was +# tested, and that the web server actually serves it. +# +# Exists because publishing is where a silent mistake is most expensive. A stale published APK +# once sat for eight weeks while the repo moved on; installing it over current data threw +# "A migration from 11 to 6 was required but not found" and the app refused to start. The cost +# wasn't the bug, it was that nothing in the publish step had ever checked. +set -uo pipefail + +die() { echo "publish-apk: $1" >&2; exit 2; } + +EXPECT_HOST="tungsten" +DEST_DIR="/site/static.terst.org/public/files" + +[ $# -ge 2 ] || die "usage: publish-apk <apk> <published-name> [--host H] [--dir D]" +APK="$1"; NAME="$2"; shift 2 +while [ $# -gt 0 ]; do + case "$1" in + --host) EXPECT_HOST="$2"; shift 2 ;; + --dir) DEST_DIR="$2"; shift 2 ;; + *) die "unknown option '$1'" ;; + esac +done + +case "$NAME" in + */*) die "published name must be a bare filename, got '$NAME'" ;; + *.apk) ;; + *) die "published name should end in .apk, got '$NAME'" ;; +esac + +# Publishing from a box that merely resembles the deploy host is how a previous session +# "deployed" to a sandbox and had to unwind it. +ACTUAL_HOST=$(hostname) +[ "$ACTUAL_HOST" = "$EXPECT_HOST" ] \ + || 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" + +[ -d "$DEST_DIR" ] || die "no such directory: $DEST_DIR" +DEST="$DEST_DIR/$NAME" + +echo "=== publish-apk ===" +echo "host : $ACTUAL_HOST" +echo "source : $APK ($(stat -c%s "$APK") bytes)" +echo "dest : $DEST" + +if [ -f "$DEST" ]; then + BACKUP="$DEST.$(date +%Y%m%d-%H%M%S).bak" + cp "$DEST" "$BACKUP" || die "could not back up the existing file" + echo "backup : $BACKUP" +else + echo "backup : none needed (new file)" +fi + +cp "$APK" "$DEST" || die "copy failed" +chmod 644 "$DEST" || die "chmod failed" +chown root:root "$DEST" 2>/dev/null || true + +# The whole point: what is served must be what was tested. +if cmp -s "$APK" "$DEST"; then + echo "identity: published copy is byte-identical to the source" +else + die "published copy differs from the source — do not trust it" +fi + +URL="https://static.terst.org/files/$NAME" +REMOTE_LEN=$(curl -sI "$URL" 2>/dev/null | tr -d '\r' | awk 'tolower($1)=="content-length:"{print $2}') +LOCAL_LEN=$(stat -c%s "$DEST") + +if [ -z "$REMOTE_LEN" ]; then + echo "serving : COULD NOT VERIFY (no content-length from $URL)" + echo "result : PARTIAL — the file is in place but the web server did not confirm it" + exit 1 +fi + +echo "url : $URL" +echo "serving : $REMOTE_LEN bytes" +if [ "$REMOTE_LEN" = "$LOCAL_LEN" ]; then + echo "result : PASS — served bytes match the tested build" +else + echo "result : FAIL — server reports $REMOTE_LEN, on disk $LOCAL_LEN" + exit 1 +fi |
