feat(deploy): always deploy the newest code from origin/main
Build / build (push) Canceled after 0s
Playwright / e2e (push) Canceled after 0s

Three fixes so a plain './deploy.sh' really does ship the latest code:

- Diverged branch: rebase local commits onto origin/main instead of
  dying on 'Fast-forward to origin/main failed'. Conflicts abort the
  rebase and stop the deploy rather than building a half-merged tree.
- Unreachable origin: retry the fetch through the local mihomo proxy
  (HERMES_UI_GIT_PROXY, default http://127.0.0.1:7890).
- Self-kill on restart: when the script runs inside the service's own
  cgroup, 'systemctl restart' SIGTERMs the script too, so the build
  landed but the post-restart health checks never ran. Re-execute under
  a transient systemd scope to escape the cgroup first.
This commit is contained in:
Jack
2026-10-22 15:35:33 +08:00
parent d1bd970540
commit b5b9a62ff8
+64 -1
View File
@@ -24,6 +24,7 @@
set -euo pipefail
APP_DIR="$(cd "$(dirname "$0")" && pwd)"
SCRIPT_PATH="$APP_DIR/$(basename "$0")"
cd "$APP_DIR"
SERVICE="${HERMES_UI_SERVICE:-hermes-ui.service}"
@@ -33,6 +34,10 @@ BACKUP_DIR="$STATE_DIR/backups"
LOCK_HASH_FILE="$STATE_DIR/lockfile.sha256"
KEEP_BACKUPS=3
# gitea is not directly reachable from every host; when a plain fetch fails
# the script retries through the local mihomo proxy.
GIT_PROXY="${HERMES_UI_GIT_PROXY:-http://127.0.0.1:7890}"
HEALTH_URL="http://127.0.0.1:$PORT/health"
AUTH_URL="http://127.0.0.1:$PORT/api/auth/status"
@@ -104,6 +109,31 @@ while [ $# -gt 0 ]; do
shift
done
# --- Escape the service cgroup --------------------------------------------
#
# This script restarts $SERVICE. When it is launched from inside that
# service's cgroup — e.g. from the agent bridge that the web UI itself
# runs — the restart SIGTERMs the whole cgroup, including this script.
# The build succeeds, the service comes back, but the script dies before it
# can run the post-restart health checks.
#
# Re-executing under a transient systemd scope moves the script into its
# own cgroup, so the restart no longer takes it down with it. A script
# started from a normal shell is not affected.
if [ "${HERMES_UI_DEPLOY_SCOPED:-0}" != "1" ] \
&& [ -r /proc/self/cgroup ] \
&& grep -q "$SERVICE" /proc/self/cgroup 2>/dev/null; then
if command -v systemd-run >/dev/null 2>&1; then
scope_unit="hermes-ui-deploy-$(date +%s)-$$"
export HERMES_UI_DEPLOY_SCOPED=1
echo "Running inside the $SERVICE cgroup — re-executing in scope $scope_unit"
exec systemd-run --scope --quiet --unit="$scope_unit" "$SCRIPT_PATH" "$@"
else
warn "systemd-run not found — the restart may kill this script early."
fi
fi
# --- Preflight ------------------------------------------------------------
step "Preflight"
@@ -137,6 +167,30 @@ if [ "$DRY_RUN" -eq 1 ]; then
fi
# --- Pull -----------------------------------------------------------------
#
# The deploy must always end up building the newest code on origin/main.
# Two things get in the way of that:
#
# * The branch diverged — the operator pushed from a dev machine while
# this host also had local commits. A merge commit would be recorded
# for every deploy; a rebase replays the local commits on top of
# origin/main and keeps the history linear.
# * origin is unreachable directly — gitea sits behind the network, so
# the fetch has to go through the local proxy.
#
# If the rebase hits a conflict the script stops instead of deploying a
# half-merged tree.
fetch_origin() {
if git fetch --quiet origin main 2>/dev/null; then
return 0
fi
if [ -n "$GIT_PROXY" ] && git -c "http.proxy=$GIT_PROXY" -c "https.proxy=$GIT_PROXY" \
fetch --quiet origin main 2>/dev/null; then
return 0
fi
return 1
}
if [ "$SKIP_PULL" -eq 1 ]; then
step "Skipping git pull (--skip-pull)"
@@ -149,10 +203,19 @@ else
else
# A failed fetch must not block a deploy: the point of the script is to
# build and restart what is already checked out.
if ! git fetch --quiet origin main; then
if ! fetch_origin; then
warn "Could not reach origin — continuing with the local checkout."
elif git merge --ff-only --quiet origin/main; then
ok "Updated to $(git rev-parse --short HEAD)"
elif [ -n "$(git rev-list --count origin/main..HEAD)" ]; then
# Diverged with local commits on top — rebase them onto origin/main.
step "Local commits found, rebasing onto origin/main"
if git rebase origin/main; then
ok "Rebased onto $(git rev-parse --short origin/main), now at $(git rev-parse --short HEAD)"
else
git rebase --abort || true
die "Rebase onto origin/main hit a conflict. Resolve it by hand and re-run."
fi
else
die "Fast-forward to origin/main failed. Resolve the branch by hand and re-run."
fi