ref:main

anvil runner stop: graceful shutdown + PID file (foreground and service) #22

closed Opened by cole.christensen@gmail.com

Links

No links yet.

Problem

`anvil runner start` has no clean way to be stopped from outside the process. Specifically:

  1. Foreground: `anvil runner start` listens only for SIGINT (Ctrl+C, see `src/runner/loop_runner.rs:50`). If backgrounded (`nohup anvil runner start &`), you have to find the PID via `pgrep` and `kill` manually.
  2. systemd / launchd service: `systemctl stop anvil-runner` sends SIGTERM, not SIGINT. The runner currently ignores SIGTERM, so the service stop is effectively a hard kill — jobs in flight get yanked instead of finishing.
  3. No `anvil runner stop` subcommand exists. `anvil runner service svc-stop` only covers the OS-service path.

Proposal

Three changes, all in one PR (tightly coupled):

  1. Write a PID file on `runner start` at `~/.anvil-runner/runner.pid` (path configurable via `–pid-file`, also stored in config). Removed on clean shutdown.
  2. Handle SIGTERM in addition to SIGINT. Both trigger the same graceful-shutdown path (existing `tokio::sync::Notify`). Jobs-in-flight get a configurable grace window (default 30s) to complete before force-exit.
  3. `anvil runner stop` subcommand: reads the PID file, sends SIGTERM, waits for the file to disappear (signaling clean exit), errors with timeout if not. `–force` falls through to SIGKILL after the timeout.

Side benefits

  • `anvil runner service svc-stop` (systemd/launchd path) becomes truly graceful instead of a kill.
  • The new `anvil runner stop` works regardless of how the runner was started.
  • PID file enables future affordances like `anvil runner status` showing “running (PID 12345)”.

Acceptance criteria

  • `anvil runner start` writes `runner.pid` and removes it on clean exit
  • SIGTERM triggers the same graceful path as SIGINT
  • Jobs in flight finish (or hit grace timeout) before exit
  • `anvil runner stop` finds and signals the running instance
  • `anvil runner stop` errors clearly if no PID file / stale PID
  • `–force` adds SIGKILL fallback
  • Tests: clap parsing for new `stop` subcommand + signal-handling unit test

Origin

User feedback: “we have things for the anvil runner being STARTED but not stopped. we need that both for the systemd style service and the non-[service]”.