#!/usr/bin/env bash # Pull the latest LLeMbas into the deployment and restart the service. # # Run this after pushing. It fetches, hard-resets the deployment checkout to the # remote branch, reinstalls dependencies if they changed, and restarts. Nothing # is ever edited in place under the deployment prefix, so a hard reset is safe # and avoids merge conflicts from a dirty tree. set -euo pipefail SERVICE_USER="${SERVICE_USER:-lembas}" PREFIX="${PREFIX:-/srv/lembas}" BRANCH="${LEMBAS_BRANCH:-main}" APP="$PREFIX/app" VENV="$PREFIX/venv" if [[ ! -d "$APP/.git" ]]; then echo "No deployment at $APP. Run deploy/install.sh first." >&2 exit 1 fi git_as() { sudo -u "$SERVICE_USER" git -C "$APP" "$@"; } before=$(git_as rev-parse HEAD) echo "== fetching ==" git_as fetch --quiet origin "$BRANCH" git_as reset --hard --quiet "origin/$BRANCH" after=$(git_as rev-parse HEAD) if [[ "$before" == "$after" ]]; then echo " already at ${after:0:7}, nothing to pull" else echo " ${before:0:7} -> ${after:0:7}" git_as --no-pager log --oneline "$before..$after" | sed 's/^/ /' fi # Cheap and idempotent; catches a dependency added since the last deploy. # The extras a deployment gets. `search` because DuckDuckGo is the default web # search provider and is meant to need no setup; `ssh` because agent chats reach # their machine over it and a deployment without it offers the feature with an # install hint instead. Listed here AND in install.sh -- an extra added to only # one of them means existing deployments silently miss it. LEMBAS_EXTRAS="${LEMBAS_EXTRAS:-search,ssh}" echo "== dependencies ==" sudo -u "$SERVICE_USER" "$VENV/bin/pip" install --quiet -e "$APP[$LEMBAS_EXTRAS]" # The unit is NOT reinstalled automatically. An installed unit usually carries # host-specific lines the template cannot know about -- an ordering dependency # on whatever serves the models, a note about how the prefix is mounted -- and # overwriting those on every update would be a worse surprise than drifting. # # So this compares the *template* against the one last applied here, not the # template against the installed file. Comparing the files would warn forever # about the local lines, and a warning that always fires is one nobody reads. # # The drift is worth catching: a change in the unit can be what makes a release # work at all. Dropping ProtectKernelTunables is why an agent chat can start a # sandbox, and a host that pulled the code without it would run the new version # under the old confinement and fail confusingly. STAMP="$PREFIX/.unit-applied" current=$(sha256sum "$APP/deploy/lembas.service" | cut -d' ' -f1) if [[ -f "$STAMP" && "$(cat "$STAMP")" != "$current" ]]; then echo "== systemd unit ==" >&2 echo " deploy/lembas.service has changed since it was last applied here." >&2 echo " Review it and merge by hand, keeping this host's own lines:" >&2 echo " diff /etc/systemd/system/lembas.service <(sed \\" >&2 echo " -e 's|__PREFIX__|$PREFIX|g' -e 's|__SERVICE_USER__|$SERVICE_USER|g' \\" >&2 echo " $APP/deploy/lembas.service)" >&2 echo " Then: sudo systemctl daemon-reload && sudo systemctl restart lembas" >&2 echo " And record it as applied: echo $current | sudo tee $STAMP" >&2 elif [[ ! -f "$STAMP" ]]; then # First run after this check was added. Assume what is installed is current; # there is nothing to compare against and crying wolf on every host once is # not worth it. echo "$current" | sudo tee "$STAMP" >/dev/null fi echo "== restart ==" sudo systemctl restart lembas sleep 2 if systemctl is-active --quiet lembas; then echo " lembas is running" else echo " lembas FAILED to start:" >&2 sudo journalctl -u lembas -n 30 --no-pager >&2 exit 1 fi