Carry a service script, and declare the backend as a component
ai-server is now something Dev Updater can install, start and stop from the phone: the project declares a Server component naming ./server/service, and that script is where knowing about systemd and OpenRC lives. Detection asks rather than looks -- `systemctl --user show-environment` answering, or `rc-service --user` existing -- because a machine can carry both binaries and an OpenRC below 0.60 has rc-service without --user. Neither present is an error with a message, not a guess at a fallback. Two things the script will not do. It never prompts: Dev Updater runs it with stdin closed, so a sudo prompt would hang rather than fail, and anything needing root exits telling you to run it yourself once. And on OpenRC it refuses when XDG_RUNTIME_DIR is unset rather than proceeding -- user services store their state there, and the failure otherwise reads as a broken service instead of a missing variable. The server is declared before the app so it is built and delivered first; a failed APK build then leaves the phone with the APK it already had rather than half of a matched pair. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QQz6R4kBQcWSHBNZMgBnjL
This commit is contained in:
1 parent
0da6a542bd
commit
effefdeb03
2 files changed
+166
-1
No files matched your search
+11
-1
@@ -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",
|
||||
),
|
||||
],
|
||||
]
|
||||
Executable
+155
@@ -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" <<UNIT
|
||||
[Unit]
|
||||
Description=$NAME (installed by $SCRIPT_DIR/service)
|
||||
|
||||
[Service]
|
||||
ExecStart=$BINARY
|
||||
Restart=on-failure
|
||||
WorkingDirectory=$SCRIPT_DIR
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
UNIT
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable "$NAME" >/dev/null
|
||||
;;
|
||||
openrc)
|
||||
mkdir -p "$(dirname "$OPENRC_UNIT")"
|
||||
cat > "$OPENRC_UNIT" <<UNIT
|
||||
#!/sbin/openrc-run
|
||||
name="$NAME"
|
||||
description="$NAME (installed by $SCRIPT_DIR/service)"
|
||||
command="$BINARY"
|
||||
command_background=true
|
||||
directory="$SCRIPT_DIR"
|
||||
pidfile="\${XDG_RUNTIME_DIR}/$NAME.pid"
|
||||
UNIT
|
||||
chmod +x "$OPENRC_UNIT"
|
||||
rc-update --user add "$NAME" >/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
|
||||
Reference in new issue
Block a user