mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-09 22:48:57 +02:00
fix(upgrade): /gstack-upgrade stops a stale daemon — deferring to a busy one (#2551)
A browse daemon started before an upgrade keeps serving the OLD binary's code after `git reset --hard` + `./setup` — the running process holds the old executable, so users on the "new" version kept getting pre-upgrade behavior (and config-mismatch refusals against the new CLI) until they happened to stop it by hand. New unconditional Step 4.8 in gstack-upgrade/SKILL.md.tmpl (+ regen, same commit): compare the running daemon's recorded binaryVersion (the readVersionHash git-SHA the server stamps into its state file) against the freshly built browse/dist/.version. - Stale + responsive → `browse stop` (graceful), telling the user old→new hash; the next command boots a daemon on the new binary. - Stale + BUSY → DEFER (decision 10): never kill a busy daemon during upgrade. Print the old→new hash and the escape hatch — `browse stop` when it finishes, or `browse --force-restart stop` now. - Dead pid / matching hash / no state → silent no-op. Tests: skill-validation + gen-skill-docs 731 pass after regen. Fixes #2551. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
5a6dd6bf88
commit
908c5a6a69
@@ -220,6 +220,50 @@ Migrations are idempotent bash scripts in `gstack-upgrade/migrations/`. Each is
|
||||
`v{VERSION}.sh` and runs only when upgrading from an older version. See CONTRIBUTING.md
|
||||
for how to add new migrations.
|
||||
|
||||
### Step 4.8: Stop any stale daemon (unconditional)
|
||||
|
||||
A browse daemon started before the upgrade keeps serving the OLD binary's code
|
||||
until it is stopped — it survives `git reset --hard` and `./setup` because the
|
||||
running process holds the old executable (#2551). Always run this step, using
|
||||
the install directory detected in Step 2.
|
||||
|
||||
```bash
|
||||
INSTALL_DIR_PLACEHOLDER="<install dir from Step 2>"
|
||||
NEW_HASH=$(cat "$INSTALL_DIR_PLACEHOLDER/browse/dist/.version" 2>/dev/null || echo "")
|
||||
_STATE_FILE="${BROWSE_STATE_FILE:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.gstack/browse.json}"
|
||||
if [ -z "$NEW_HASH" ] || [ ! -f "$_STATE_FILE" ]; then
|
||||
echo "DAEMON_CHECK=none (no state file or no fresh build hash)"
|
||||
else
|
||||
DAEMON_PID=$(sed -n 's/.*"pid"[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p' "$_STATE_FILE" | head -1)
|
||||
DAEMON_PORT=$(sed -n 's/.*"port"[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p' "$_STATE_FILE" | head -1)
|
||||
OLD_HASH=$(sed -n 's/.*"binaryVersion"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$_STATE_FILE" | head -1)
|
||||
if [ -z "$DAEMON_PID" ] || ! kill -0 "$DAEMON_PID" 2>/dev/null; then
|
||||
echo "DAEMON_CHECK=dead (no live daemon to stop)"
|
||||
elif [ "$OLD_HASH" = "$NEW_HASH" ]; then
|
||||
echo "DAEMON_CHECK=current (daemon already runs the new binary)"
|
||||
elif curl -fsS --max-time 2 "http://127.0.0.1:$DAEMON_PORT/health" 2>/dev/null | grep -q '"status":"healthy"'; then
|
||||
echo "DAEMON_CHECK=stale-responsive pid=$DAEMON_PID hash=${OLD_HASH:-unknown} -> $NEW_HASH"
|
||||
"$INSTALL_DIR_PLACEHOLDER/browse/dist/browse" stop && echo "DAEMON_STOPPED=yes"
|
||||
else
|
||||
echo "DAEMON_CHECK=stale-busy pid=$DAEMON_PID hash=${OLD_HASH:-unknown} -> $NEW_HASH"
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
Replace `<install dir from Step 2>` with the actual install directory before
|
||||
running. Interpret the `DAEMON_CHECK` result:
|
||||
|
||||
1. **`stale-responsive` + `DAEMON_STOPPED=yes`:** Tell the user "Stopped the
|
||||
old browse daemon (binary {OLD_HASH} → {NEW_HASH}). The next browse command
|
||||
starts a fresh daemon on the new binary."
|
||||
2. **`stale-busy`:** The daemon runs the old binary but is mid-work — DEFER to
|
||||
it, never kill a busy daemon during upgrade. Tell the user: "A browse daemon
|
||||
is still running the pre-upgrade binary ({OLD_HASH} → {NEW_HASH}) but is
|
||||
busy right now. When it finishes, stop it with `browse stop` — or force it
|
||||
immediately with `browse --force-restart stop` (loses that session's
|
||||
tabs/cookies/logins)."
|
||||
3. **`none` / `dead` / `current`:** Nothing to do — say nothing.
|
||||
|
||||
### Step 5: Write marker + clear cache
|
||||
|
||||
```bash
|
||||
|
||||
@@ -217,6 +217,50 @@ Migrations are idempotent bash scripts in `gstack-upgrade/migrations/`. Each is
|
||||
`v{VERSION}.sh` and runs only when upgrading from an older version. See CONTRIBUTING.md
|
||||
for how to add new migrations.
|
||||
|
||||
### Step 4.8: Stop any stale daemon (unconditional)
|
||||
|
||||
A browse daemon started before the upgrade keeps serving the OLD binary's code
|
||||
until it is stopped — it survives `git reset --hard` and `./setup` because the
|
||||
running process holds the old executable (#2551). Always run this step, using
|
||||
the install directory detected in Step 2.
|
||||
|
||||
```bash
|
||||
INSTALL_DIR_PLACEHOLDER="<install dir from Step 2>"
|
||||
NEW_HASH=$(cat "$INSTALL_DIR_PLACEHOLDER/browse/dist/.version" 2>/dev/null || echo "")
|
||||
_STATE_FILE="${BROWSE_STATE_FILE:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.gstack/browse.json}"
|
||||
if [ -z "$NEW_HASH" ] || [ ! -f "$_STATE_FILE" ]; then
|
||||
echo "DAEMON_CHECK=none (no state file or no fresh build hash)"
|
||||
else
|
||||
DAEMON_PID=$(sed -n 's/.*"pid"[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p' "$_STATE_FILE" | head -1)
|
||||
DAEMON_PORT=$(sed -n 's/.*"port"[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p' "$_STATE_FILE" | head -1)
|
||||
OLD_HASH=$(sed -n 's/.*"binaryVersion"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$_STATE_FILE" | head -1)
|
||||
if [ -z "$DAEMON_PID" ] || ! kill -0 "$DAEMON_PID" 2>/dev/null; then
|
||||
echo "DAEMON_CHECK=dead (no live daemon to stop)"
|
||||
elif [ "$OLD_HASH" = "$NEW_HASH" ]; then
|
||||
echo "DAEMON_CHECK=current (daemon already runs the new binary)"
|
||||
elif curl -fsS --max-time 2 "http://127.0.0.1:$DAEMON_PORT/health" 2>/dev/null | grep -q '"status":"healthy"'; then
|
||||
echo "DAEMON_CHECK=stale-responsive pid=$DAEMON_PID hash=${OLD_HASH:-unknown} -> $NEW_HASH"
|
||||
"$INSTALL_DIR_PLACEHOLDER/browse/dist/browse" stop && echo "DAEMON_STOPPED=yes"
|
||||
else
|
||||
echo "DAEMON_CHECK=stale-busy pid=$DAEMON_PID hash=${OLD_HASH:-unknown} -> $NEW_HASH"
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
Replace `<install dir from Step 2>` with the actual install directory before
|
||||
running. Interpret the `DAEMON_CHECK` result:
|
||||
|
||||
1. **`stale-responsive` + `DAEMON_STOPPED=yes`:** Tell the user "Stopped the
|
||||
old browse daemon (binary {OLD_HASH} → {NEW_HASH}). The next browse command
|
||||
starts a fresh daemon on the new binary."
|
||||
2. **`stale-busy`:** The daemon runs the old binary but is mid-work — DEFER to
|
||||
it, never kill a busy daemon during upgrade. Tell the user: "A browse daemon
|
||||
is still running the pre-upgrade binary ({OLD_HASH} → {NEW_HASH}) but is
|
||||
busy right now. When it finishes, stop it with `browse stop` — or force it
|
||||
immediately with `browse --force-restart stop` (loses that session's
|
||||
tabs/cookies/logins)."
|
||||
3. **`none` / `dead` / `current`:** Nothing to do — say nothing.
|
||||
|
||||
### Step 5: Write marker + clear cache
|
||||
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user