summaryrefslogtreecommitdiff
path: root/bin/publish-apk
diff options
context:
space:
mode:
authorClaude <noreply@anthropic.com>2026-09-25 07:36:19 +0000
committerClaude <noreply@anthropic.com>2026-09-25 07:36:19 +0000
commit0d04aefa0c5e7956cb2dbc80596910cbe53136bf (patch)
tree9580972c3b17dc6ed55282f6227999de8007ecc0 /bin/publish-apk
parentc89250698116180acfe9a98c24e2e5291e050240 (diff)
publish-apk: allow publishing from another machine over sshHEADmain
The host gate stopped the Fedora laptop publishing at all, which left a build made there with nowhere to go. It now takes PUBLISH_SSH and sends the file to the real host instead. The gate itself is unchanged in spirit: without PUBLISH_SSH a build from elsewhere is still refused, because the failure it guards against is writing to a local path that merely looks like the deploy host's. Two tests cover that -- the error names the way out, and a failed remote publish never falls back to writing locally. Transfers land on a temporary name and move into place only once complete, and the remote copy is hash-compared against the source before the served bytes are checked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
Diffstat (limited to 'bin/publish-apk')
-rwxr-xr-xbin/publish-apk62
1 files changed, 58 insertions, 4 deletions
diff --git a/bin/publish-apk b/bin/publish-apk
index 520eeae..9faf36a 100755
--- a/bin/publish-apk
+++ b/bin/publish-apk
@@ -1,6 +1,8 @@
#!/usr/bin/env bash
# publish-apk <apk> <published-name> [--host H] [--dir D] [--keep N]
#
+# From another machine: PUBLISH_SSH=user@tungsten publish-apk <apk> <name>
+#
# 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.
@@ -39,8 +41,20 @@ 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."
+REMOTE=0
+if [ "$ACTUAL_HOST" != "$EXPECT_HOST" ]; then
+ # Publishing from elsewhere is allowed, but only by sending the file to the real host --
+ # never by writing to a local path that merely looks like it. A previous session
+ # "deployed" to a sandbox exactly that way and had to unwind it.
+ if [ -n "${PUBLISH_SSH:-}" ]; then
+ REMOTE=1
+ else
+ die "host is '$ACTUAL_HOST', expected '$EXPECT_HOST'.
+ To publish from here, send it over ssh instead:
+ PUBLISH_SSH=<user@$EXPECT_HOST> publish-apk <apk> <name>
+ That copies to the real host rather than writing to a local path of the same name."
+ fi
+fi
[ -f "$APK" ] || die "no such file: $APK"
@@ -59,14 +73,54 @@ case "$LISTING" in
*) die "$APK has no AndroidManifest.xml — not an APK" ;;
esac
-[ -d "$DEST_DIR" ] || die "no such directory: $DEST_DIR"
+if [ $REMOTE -eq 0 ]; then
+ [ -d "$DEST_DIR" ] || die "no such directory: $DEST_DIR"
+fi
DEST="$DEST_DIR/$NAME"
echo "=== publish-apk ==="
-echo "host : $ACTUAL_HOST"
+echo "host : $ACTUAL_HOST$([ $REMOTE -eq 1 ] && echo " (publishing to $PUBLISH_SSH over ssh)")"
echo "source : $APK ($(stat -c%s "$APK") bytes)"
echo "dest : $DEST"
+if [ $REMOTE -eq 1 ]; then
+ echo "source : $APK ($(stat -c%s "$APK") bytes)"
+ LOCAL_SHA=$(sha256sum "$APK" | awk '{print $1}')
+
+ # Copy to a temp name first, so a half-transferred file is never the published one.
+ scp -q "$APK" "$PUBLISH_SSH:/tmp/$NAME.incoming" || die "scp failed"
+
+ ssh "$PUBLISH_SSH" "bash -s" <<REMOTE_SCRIPT || die "remote publish failed"
+set -euo pipefail
+dest="$DEST_DIR/$NAME"
+[ -d "$DEST_DIR" ] || { echo "no such directory: $DEST_DIR" >&2; exit 1; }
+[ -f "\$dest" ] && cp "\$dest" "\$dest.\$(date +%Y%m%d-%H%M%S).bak"
+ls -t "\$dest".*.bak 2>/dev/null | tail -n +$((KEEP_BACKUPS + 1)) | while read -r old; do rm -f "\$old"; done
+mv "/tmp/$NAME.incoming" "\$dest"
+chmod 644 "\$dest"
+chown root:root "\$dest" 2>/dev/null || true
+sha256sum "\$dest" | awk '{print \$1}'
+REMOTE_SCRIPT
+
+ REMOTE_SHA=$(ssh "$PUBLISH_SSH" "sha256sum '$DEST_DIR/$NAME'" 2>/dev/null | awk '{print $1}')
+ if [ "$LOCAL_SHA" = "$REMOTE_SHA" ]; then
+ echo "identity: published copy is byte-identical to the source"
+ else
+ die "published copy differs from the source (local $LOCAL_SHA, remote $REMOTE_SHA)"
+ 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 "$APK")
+ echo "url : $URL"
+ if [ "$REMOTE_LEN" = "$LOCAL_LEN" ]; then
+ echo "result : PASS -- served bytes match the tested build"
+ exit 0
+ fi
+ echo "result : FAIL -- server reports ${REMOTE_LEN:-nothing}, built $LOCAL_LEN"
+ exit 1
+fi
+
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"