Files
flamenet-messenger-android/scripts/publish-fdroid.sh
T
Paul HittandClaude Opus 4.8 84b0af1c9a Add a publish script for the F-Droid repository
Our own repo, not a submission to f-droid.org — and the difference is not
cosmetic. f-droid.org rebuilds from source and signs with their key, so an app
already installed under our signature could not be upgraded by it; Android
refuses a differently-signed APK over an existing install. This repo indexes
the APK we already signed, so everybody who sideloaded from the website
upgrades cleanly and then gets updates automatically.

The working directory holds a signing keystore and its password and lives
outside every checkout, because all four repos are public. The script refuses
to run if the keystore is missing rather than re-initialising over a live repo
— replacing the index key makes every client that has added the repository
reject its updates.

Version comes out of the APK via aapt2 rather than being typed, same rule as
the update manifest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-29 16:29:32 -04:00

76 lines
3.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Publish a built release APK into the Flamenet F-Droid repository.
#
# 🔴 **This is OUR OWN repository, not a submission to f-droid.org.** The
# difference is not cosmetic: f-droid.org rebuilds from source and signs with
# THEIR key, so an app already installed under our signature could not be
# upgraded by it — Android refuses a differently-signed APK over an existing
# install. This repo indexes the APK we already signed, so it upgrades cleanly
# for everybody who sideloaded from the website.
#
# 🔴 **The repo working directory holds a signing keystore and its password.**
# It lives OUTSIDE every git checkout on purpose — all four repos are public.
# Never move it in, and never redirect (`>`) over config.yml: that file holds
# the only on-disk copy of the keystore password, and this project has already
# clobbered a credentials file that way once.
#
# ⚠️ The repo index key is permanent. Replacing it makes every client that has
# added this repository reject its updates, silently, until they remove and
# re-add it. Back up ~/flamenet-fdroid off this machine.
#
# Usage: scripts/publish-fdroid.sh [path/to/app-release.apk]
set -euo pipefail
FDROID_DIR="${FDROID_DIR:-$HOME/flamenet-fdroid}"
FDROID_BIN="${FDROID_BIN:-}"
APK="${1:-app/build/outputs/apk/release/app-release.apk}"
REMOTE="${FDROID_REMOTE:-ovh}"
REMOTE_DIR="${FDROID_REMOTE_DIR:-/srv/prod/messenger/fdroid}"
[ -f "$APK" ] || { echo "no APK at $APK — build it first" >&2; exit 1; }
[ -d "$FDROID_DIR" ] || { echo "no repo at $FDROID_DIR" >&2; exit 1; }
[ -f "$FDROID_DIR/keystore.p12" ] || {
echo "no keystore in $FDROID_DIR — refusing to re-init over a live repo" >&2; exit 1; }
if [ -z "$FDROID_BIN" ]; then
echo "set FDROID_BIN to the fdroid executable (a venv with fdroidserver)" >&2
exit 1
fi
export JAVA_HOME="${JAVA_HOME:-/opt/homebrew/opt/openjdk@21}"
export PATH="$JAVA_HOME/bin:$PATH"
export ANDROID_HOME="${ANDROID_HOME:-$HOME/Library/Android/sdk}"
# Name the file by its real version, read out of the APK rather than typed —
# same rule as the update manifest.
aapt2="$(find "$ANDROID_HOME/build-tools" -maxdepth 2 -name aapt2 -type f | sort -V | tail -1)"
badging="$("$aapt2" dump badging "$APK")"
name="$(sed -n "s/.*versionName='\([^']*\)'.*/\1/p" <<<"$badging" | head -1)"
[ -n "$name" ] || { echo "could not read versionName from $APK" >&2; exit 1; }
echo "publishing $APK as version $name"
cp "$APK" "$FDROID_DIR/repo/flamenet-messenger-$name.apk"
cd "$FDROID_DIR"
# `update` re-reads every APK in repo/, rebuilds the index and signs it. Older
# releases move to archive/ per `archive_older` rather than being deleted, so a
# version somebody is pinned to does not vanish.
"$FDROID_BIN" update --pretty
echo "syncing to $REMOTE:$REMOTE_DIR"
rsync -az --delete -e ssh repo/ "$REMOTE:/tmp/fdroid-repo-new/"
[ -d archive ] && rsync -az --delete -e ssh archive/ "$REMOTE:/tmp/fdroid-archive-new/" || true
# shellcheck disable=SC2029
ssh "$REMOTE" "set -e
sudo rm -rf $REMOTE_DIR/repo && sudo mkdir -p $REMOTE_DIR
sudo cp -r /tmp/fdroid-repo-new $REMOTE_DIR/repo && rm -rf /tmp/fdroid-repo-new
if [ -d /tmp/fdroid-archive-new ]; then
sudo rm -rf $REMOTE_DIR/archive
sudo cp -r /tmp/fdroid-archive-new $REMOTE_DIR/archive && rm -rf /tmp/fdroid-archive-new
fi
sudo chown -R root:root $REMOTE_DIR && sudo chmod -R a+rX $REMOTE_DIR"
echo
echo "published. verify it is actually being served:"
echo " curl -sS https://flamenetmessenger.com/fdroid/repo/index-v2.json | head -c 200"