Files
kvitt/infra/pi/install-apk.sh
T
jacobh2 2476667510 M2c-pi: Pi bundle (infra/pi), cert-sync, Drive backup upload, Gitea workflow
The owner copies infra/pi/ to the Kvitt Pi and runs it there (D-100); nothing
on the Mac connects to the Pis. compose stack of SPEC §10.2 with images
${KVITT_REGISTRY}/kvitt-{server,cert-sync,backup}, Caddyfile §10.5.2 verbatim,
.env.example with the OQ-3 values as CHANGE_ME placeholders, install.sh,
deploy.sh (backup before every deploy), bootstrap.sh, backup-now.sh,
install-apk.sh. cert-sync image and sync.sh (§10.5.1, tar stream for both
authorized_keys variants); backup.sh uploads with rclone copy --immutable and
deletes local files once both are confirmed on Drive (D-002); both sidecars
schedule with supercronic because busybox crond runs no jobs as non-root;
backup runs as uid 10001. .gitea/workflows/build-images.yaml from the owner's
example (not yet run); infra/build-images.sh and infra/publish-apk.sh (R8
mapping kept, version per §10.6) on the Mac; README checklist and RESTORE.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
2026-10-18 21:48:35 +02:00

44 lines
2.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# Put the APKs that infra/publish-apk.sh made on the Mac into the server's data volume (SPEC §10.6).
# The owner copies infra/pi/out/apk/ from the Mac to /opt/kvitt/apk/ on the Pi, then runs ON the Pi:
# /opt/kvitt/install-apk.sh
# Copies every kvitt-<version_code>.apk from /opt/kvitt/apk into /data/apk, then latest.json last and atomically
# (temp file, rename), so the server never announces a file it does not have; keeps the newest 5 APKs in /data/apk.
# R8 mapping files (kvitt-<version_code>.mapping.txt) stay in /opt/kvitt/apk; the server never needs them.
set -euo pipefail
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib.sh
. "$here/lib.sh"
src="$KVITT_DIR/apk"
[[ -f "$src/latest.json" ]] || die "no $src/latest.json: copy infra/pi/out/apk/ from the Mac first (README, Publishing an APK)"
file_name=$(sed -n 's/.*"file_name": *"\([^"]*\)".*/\1/p' "$src/latest.json")
[[ "$file_name" =~ ^kvitt-[0-9]+\.apk$ ]] || die "latest.json has no valid file_name"
[[ -f "$src/$file_name" ]] || die "latest.json names $file_name, which is not in $src"
sha=$(sed -n 's/.*"sha256": *"\([0-9a-f]\{64\}\)".*/\1/p' "$src/latest.json")
[[ "$(sha256sum "$src/$file_name" | cut -d' ' -f1)" == "$sha" ]] || die "$file_name does not match the sha256 in latest.json"
# backup runs as uid 10001, the owner of /data, and has a shell; the server image has none.
# shellcheck disable=SC2016 # the script runs inside the backup container
compose run --rm --no-deps -T -v "$src:/incoming:ro" --entrypoint /bin/sh backup -c '
set -eu
mkdir -p /data/apk
for f in /incoming/kvitt-*.apk; do
[ -f "$f" ] || continue
n=$(basename "$f")
[ -f "/data/apk/$n" ] && cmp -s "$f" "/data/apk/$n" && continue
cp "$f" "/data/apk/.$n.tmp" && mv -f "/data/apk/.$n.tmp" "/data/apk/$n"
echo "copied $n"
done
cp /incoming/latest.json /data/apk/.latest.json.tmp
mv -f /data/apk/.latest.json.tmp /data/apk/latest.json
# keep the newest 5 APKs by version code
ls /data/apk | sed -n "s/^kvitt-\([0-9]*\)\.apk$/\1/p" | sort -n -r | tail -n +6 | while read -r v; do
rm -f "/data/apk/kvitt-$v.apk"; echo "removed kvitt-$v.apk"
done
echo "latest.json installed"'
say "GET https://$PUBLIC_HOST:$PUBLIC_PORT/apk/latest (via this Pi):"
curl -fsS --max-time 15 --resolve "$PUBLIC_HOST:$PUBLIC_PORT:127.0.0.1" "https://$PUBLIC_HOST:$PUBLIC_PORT/apk/latest" \
|| die "GET /apk/latest failed"
echo