diff --git a/app/.dev-updater.ron b/app/.dev-updater.ron index 618dd2f..9ef6078 100644 --- a/app/.dev-updater.ron +++ b/app/.dev-updater.ron @@ -3,9 +3,19 @@ label: "AI Sessions", +// The server is built and delivered before the APK: if the APK build then +// fails, the phone keeps being offered the one it already had rather than +// half of a matched pair. components: [ + Server( + name: "backend", + build: "cargo build --release --manifest-path ../server/Cargo.toml", + // Installs into whichever user-service manager is here, and is how + // this server is started, stopped and asked about. See the script. + service: "../server/service", + ), Apk( name: "app", build: "./build-apk.sh", ), -], +] diff --git a/server/service b/server/service new file mode 100755 index 0000000..6ff5831 --- /dev/null +++ b/server/service @@ -0,0 +1,155 @@ +#!/bin/sh +# Installs and controls ai-server as a user service, for Dev Updater to +# drive from the phone -- and for you to drive by hand, which is the same +# thing. +# +# ./service install | uninstall | start | stop | restart | status +# +# `status` prints exactly one of `running`, `stopped` or `not-installed` +# and exits 0. Anything else it prints, or any non-zero exit, means it +# could not tell -- which the card shows as "couldn't check" rather than +# as a service that is down. +# +# Nothing here ever prompts. Dev Updater runs this with stdin closed and no +# terminal, so a `sudo` password prompt would not fail, it would hang until +# the timeout with the card stuck mid-action. Anything needing root exits +# with a message telling you to run it yourself once instead. +# +# User services on purpose: a system unit needs root to install, and a +# build machine's own account is where a dev server belongs. +set -eu + +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +NAME=ai-server +BINARY="$SCRIPT_DIR/target/release/$NAME" + +# Which init system is here, decided by asking rather than by looking for a +# binary: a machine can carry both, and an OpenRC older than 0.60 has +# rc-service but no --user at all. +detect() { + if command -v systemctl >/dev/null 2>&1 && + systemctl --user show-environment >/dev/null 2>&1; then + echo systemd + elif command -v rc-service >/dev/null 2>&1 && + rc-service --user --help >/dev/null 2>&1; then + echo openrc + else + echo none + fi +} + +MANAGER=$(detect) +if [ "$MANAGER" = none ]; then + echo "No user-service manager here: this needs systemd with a user bus," >&2 + echo "or OpenRC 0.60+ (older ones have no --user). Run $NAME by hand." >&2 + exit 1 +fi + +# OpenRC keeps user-service state under XDG_RUNTIME_DIR and refuses without +# it. Worth saying plainly: from a server started outside a login session +# it can be unset, and the failure otherwise reads as a broken service +# rather than a missing variable. +if [ "$MANAGER" = openrc ] && [ -z "${XDG_RUNTIME_DIR:-}" ]; then + echo "XDG_RUNTIME_DIR is unset, and OpenRC stores user-service state in it." >&2 + echo "Set it at login (elogind or pam_xdg) and try again." >&2 + exit 1 +fi + +SYSTEMD_UNIT="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/$NAME.service" +OPENRC_UNIT="${XDG_CONFIG_HOME:-$HOME/.config}/rc/init.d/$NAME" + +installed() { + case "$MANAGER" in + systemd) [ -f "$SYSTEMD_UNIT" ] ;; + openrc) [ -f "$OPENRC_UNIT" ] ;; + esac +} + +require_binary() { + [ -x "$BINARY" ] && return 0 + echo "No built server at $BINARY -- build it first." >&2 + exit 1 +} + +do_install() { + require_binary + case "$MANAGER" in + systemd) + mkdir -p "$(dirname "$SYSTEMD_UNIT")" + cat > "$SYSTEMD_UNIT" </dev/null + ;; + openrc) + mkdir -p "$(dirname "$OPENRC_UNIT")" + cat > "$OPENRC_UNIT" </dev/null + ;; + esac +} + +do_uninstall() { + installed || return 0 + case "$MANAGER" in + systemd) + systemctl --user disable --now "$NAME" >/dev/null 2>&1 || true + rm -f "$SYSTEMD_UNIT" + systemctl --user daemon-reload + ;; + openrc) + rc-service --user "$NAME" stop >/dev/null 2>&1 || true + rc-update --user del "$NAME" >/dev/null 2>&1 || true + rm -f "$OPENRC_UNIT" + ;; + esac +} + +control() { + installed || { echo "$NAME is not installed" >&2; exit 1; } + case "$MANAGER" in + systemd) systemctl --user "$1" "$NAME" ;; + openrc) rc-service --user "$NAME" "$1" ;; + esac +} + +case "${1:-}" in + install) do_install ;; + uninstall) do_uninstall ;; + start | stop | restart) control "$1" ;; + status) + if ! installed; then + echo not-installed + elif case "$MANAGER" in + systemd) systemctl --user --quiet is-active "$NAME" ;; + openrc) rc-service --user "$NAME" status >/dev/null 2>&1 ;; + esac then + echo running + else + echo stopped + fi + ;; + *) + echo "usage: $0 install|uninstall|start|stop|restart|status" >&2 + exit 2 + ;; +esac