Files
ai-app/app/build-apk.sh
T
irisandClaude Opus 5 99bcc341c1 Cleanup pass: one home for duplicated logic, stale comments out
Nothing behavioral except two status codes; mostly removing places where
the same rule was written down more than once and could drift.

- server/src/private.rs: the owner-only create/write helpers, which
  config.rs, certs.rs, and the session dirs each had their own copy of
  (certs.rs even duplicated the explanatory comment). One module owns the
  modes now, so the "nothing this server writes is readable by anyone
  else" property is checkable in one place.
- server/src/media.rs: the image media-type/extension table, which the
  four places that have to agree on it each spelled out separately --
  storing an upload, serving it back, building a content block, saving a
  produced image. The differing *defaults* stay at the call sites with
  the reasoning, since they genuinely differ by direction.
- routes.rs: a missing file was a 400 and an unreadable one a 400 with a
  hand-rolled log line; they are now 404 and Internal respectively.
  UnknownSession became NotFound, since it was the only 404-with-message.
- main.rs: xdg_dir takes the variable's value instead of reading the
  environment, which drops the unsafe set_var from its test and lets the
  test actually assert the relative-path rule.
- echo.rs had its own 4-byte hex generator beside session::random_hex.
- claude.rs: the two impl Translator blocks were one type's methods.
- Stale comments: phase-2 markers on shipped work, a permission-mode list
  that had drifted from the CLI's, "dev-updater" as the leaf certificate's
  fallback common name, a half-written sentence in build-apk.sh.
- App: the JSONArray walk written out in four fetchers, the four
  near-identical BackHandlers in AppRoot, and SessionScreen's inline
  fully-qualified names where the file otherwise imports.
- server/wg-test.log was committed by accident; *.log is ignored now, and
  the gitignore comments describe where state actually lives.
- PLAN.md's backend layout gains the new modules and drops hosts.rs for
  the ssh.rs that was built instead.

Verified: 35 server tests, clippy clean, app compiles warning-free, and a
scratch server driven over curl -- attachment upload/serve round-trip with
both a known and an unknown content type, the new 404s, transcript and
session-dir deletion, plus a real claude-cli session answering a prompt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017xn8nHw1tw1R6PtiY1eEtw
2026-08-25 16:10:33 -04:00

70 lines
2.9 KiB
Bash
Executable File

#!/bin/sh
# Builds the app's APK, ready to install on a phone through Dev Updater.
#
# ./build-apk.sh
#
# The APK pins the CA on *this* machine ($XDG_CONFIG_HOME/ai-app/certs/ca.pem,
# or AI_APP_CA), so build it on the machine that runs the backend: an app
# built somewhere else trusts a CA that backend can't present, and simply
# won't connect. Start ai-server once first if there are no certificates
# yet -- it generates them; the build stops with that instruction if it
# can't find one.
#
# Unlike ./run-android.sh, this touches no emulator: it only produces the
# file. Installing on a real phone goes through Dev Updater, which serves
# whatever is under this project's build directory.
set -eu
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
cd "$SCRIPT_DIR"
# Prefer an SDK this machine has already configured -- the host and the dev
# VM don't keep it in the same place, and android-env.sh is written for the
# VM's layout (it also installs missing packages, which isn't wanted here).
if [ -n "${ANDROID_HOME:-}" ] && [ -d "${ANDROID_HOME}" ]; then
echo "==> Using ANDROID_HOME=$ANDROID_HOME"
elif [ -n "${ANDROID_SDK_ROOT:-}" ] && [ -d "${ANDROID_SDK_ROOT}" ]; then
ANDROID_HOME="$ANDROID_SDK_ROOT"
export ANDROID_HOME
echo "==> Using ANDROID_SDK_ROOT=$ANDROID_SDK_ROOT"
elif [ -d "$HOME/Android/Sdk" ]; then
ANDROID_HOME="$HOME/Android/Sdk"
ANDROID_SDK_ROOT="$ANDROID_HOME"
export ANDROID_HOME ANDROID_SDK_ROOT
echo "==> Using $ANDROID_HOME"
else
echo "No Android SDK found. Set ANDROID_HOME to it, or install one" >&2
echo "(Android Studio's default location is ~/Android/Sdk)." >&2
exit 1
fi
CA="${AI_APP_CA:-${XDG_CONFIG_HOME:-$HOME/.config}/ai-app/certs/ca.pem}"
if [ -f "$CA" ]; then
# Printed so a wrong or stale certificate is visible here rather than
# as a handshake failure on the phone -- compare it against the CA the
# backend is actually presenting.
FINGERPRINT=$(openssl x509 -in "$CA" -pubkey -noout 2>/dev/null \
| openssl pkey -pubin -outform der 2>/dev/null \
| openssl dgst -sha256 -binary 2>/dev/null \
| openssl base64 2>/dev/null || echo "(openssl unavailable)")
echo "==> Pinning the CA at $CA"
echo " fingerprint: $FINGERPRINT"
else
echo "No CA certificate at $CA -- start ai-server once on this machine" >&2
echo "(it generates them), or set AI_APP_CA. The APK embeds it at build time." >&2
exit 1
fi
echo "==> Building"
./gradlew :androidApp:assembleDebug
APK="$SCRIPT_DIR/androidApp/build/outputs/apk/debug/androidApp-debug.apk"
echo
echo "==> Built $APK"
[ -f "$APK" ] && ls -lh "$APK" | awk '{print " " $5}'
echo
echo "To get it onto the phone: add this project to Dev Updater (or hit"
echo "Update on it if it's already there) and install from there."
echo "Then start the backend and scan the enrollment QR it prints:"
echo " ./server/target/release/ai-server --rotate-token"