Skip to content

v2.30.0: Shell-free argv arrays for ready_cmd and health_cmd, and pitchfork wait follows restarts

Choose a tag to compare

@jdx jdx released this 07 Oct 10:41
· 55 commits to main since this release
Immutable release. Only release title and notes can be modified.
60e97b1

ready_cmd and health_cmd now accept argv arrays, so probes can run without a shell. run templates can use variables that only mise knows, and a new restarting status lets pitchfork wait keep following a daemon through restarts. The release also fixes a long list of supervisor lifecycle bugs: shutdown, stop order, settings that persisted after being removed from config, disabled daemons, ad-hoc restarts and cron retries. It also fixes Windows process cleanup and macOS boot crash recovery.

Added

  • argv arrays for ready_cmd and health_cmd (#1012, #1015) by @Marukome0743. Both fields now take the same array as run, either as the shorthand or as run in the object form. The program gets each argument exactly as written, without going through a shell, so spaces, quotes, $, % and & are passed through unchanged. Each argument's templates are rendered separately.

    [daemons.database]
    run = ["postgres", "-D", "data"]
    ready_cmd = ["pg_isready", "-h", "127.0.0.1"]
    health_cmd = { run = ["pg_isready", "-h", "127.0.0.1"], interval = "10s", timeout = "5s" }

    Without a shell, $PORT in an argument stays as the literal text. PORT is still set in the environment, and you can keep using the string form if the port needs to be in an argument. If an array is empty, has an empty program or starts with "exec", the config fails to load. The string form and the --ready-cmd, --cmd and --health-cmd flags are unchanged.

  • run templates can use mise-only variables (#966) by @jdx. Pitchfork may not be able to render a daemon's run, for example when it uses a mise [vars] entry or a mise-only filter. If the daemon runs under mise x (mise = true or general.mise), pitchfork now passes the command through unrendered. It also puts its own template context in PITCHFORK_TEMPLATE_CONTEXT so mise can render the whole command.

    [daemons.api]
    run = "exec node server.js --port {{ daemons.redis.port }} --title {{ vars.title | quote }}"
    mise = true

    This needs a mise release that includes jdx/mise#13894. Older mise versions leave the tags unrendered. Only run is deferred: env, hooks and the ready and health fields still have to use variables pitchfork defines. Without mise, an unknown variable is still an error. If mise is enabled but its binary can't be found, the start fails.

Changed

  • New restarting status, and pitchfork wait follows restarts (#992) by @jdx. A daemon being restarted by a file-watch change, pitchfork restart or start --force now shows restarting until its new process starts. Before, it briefly showed stopped. The status appears in list (filter with --status restarting), the TUI, the web UI, JSON output and the HTTP API. pitchfork wait now follows the daemon to its new process. Pass --exit-on-restart to get the old behavior, where a restart ends the wait with exit 0. A stop issued during a restart cancels the restart.
  • pitchfork wait keeps waiting through retries (#979) by @Marukome0743. If a failed daemon still has retry attempts left, wait keeps waiting. It exits 0 if a retry succeeds, or with the last attempt's exit code once all attempts have failed.
  • pitchfork stop succeeds when no supervisor is running (#991) by @jdx. It prints Supervisor is not running, nothing to stop and exits 0, without starting a supervisor. It also marks the targeted daemons stopped, so the next supervisor won't restart them. If a crashed supervisor left daemon processes running, stop fails, lists them, and tells you to run pitchfork supervisor start first.
  • Minimum supported Rust version is now 1.95 (#960) by @jdx. This only matters if you build from source. The previously declared 1.91 couldn't actually build the locked dependencies.

Fixed

Supervisor lifecycle and shutdown

  • pitchfork supervisor stop no longer kills the supervisor before it has stopped every daemon. The supervisor now works out how long its shutdown can take and reports that to the CLI. It also refuses new starts, cron runs and retries once shutdown has begun. A second signal during shutdown kills the remaining daemons instead of leaving them running without a supervisor (#973) by @Marukome0743.
  • stop --all, group stops and supervisor shutdown now stop dependents before their dependencies, using the dependencies recorded when each daemon started. Before, running the command outside the project could stop them all at once (#986) by @Marukome0743.
  • A setting removed from pitchfork.toml, such as a health check, env, cron, resource limits or stop_signal, no longer stays in effect after the next start (#967) by @Marukome0743. Likewise, removing auto = ["stop"] now stops a daemon from being autostopped after it restarts (#964) by @Marukome0743.
  • Disabled daemons are no longer started by cron, retries or boot_start. After pitchfork enable, a cron schedule resumes at its next scheduled time without catching up on missed runs (#948) by @JamBalaya56562.
  • pitchfork stop no longer panics when a daemon has a very large stop_signal timeout (#938) by @JamBalaya56562.

Ad-hoc daemons and cron

  • After pitchfork stop, an ad-hoc daemon from pitchfork run can be started again with start or restart, using its saved command. Starts that can't proceed now exit 1 and explain why, instead of warning and exiting 0 (#945) by @JamBalaya56562.
  • Restarting an ad-hoc daemon keeps the ready check, expected port, port bumping and retry it was originally run with. Passing any readiness flag replaces the saved ready check (#943) by @JamBalaya56562.
  • Each scheduled cron run now gets its full retry budget. Before, once a run used up its retries, later runs weren't retried at all (#969) by @JamBalaya56562.
  • A cron schedule added to config no longer fires the command of an ad-hoc run that has the same id (#935) by @JamBalaya56562.

Config, hooks and mise

  • A short daemon name now resolves to the current project's daemon before a slug that another project registered (#962) by @Marukome0743.
  • If pitchfork.toml declares a namespace, pitchfork daemons add --local no longer fails when it creates a new pitchfork.local.toml (#984) by @Marukome0743.
  • Configs that pitchfork writes itself no longer trigger warnings about the deprecated expected_port, auto_bump_port and port_bump_attempts fields. The warning now appears only when those fields disagree with port. Configs that only use the deprecated fields get a deprecation notice with a removal date of 2027-10-03 (#970, #976) by @JamBalaya56562 and @jdx.
  • Hooks now fire for daemons whose dir points outside their project. This includes daemons that are adopted or retried from records saved by older versions (#981, #983) by @Marukome0743.
  • mise = true daemons now use their project's general.mise_bin, even when the supervisor was started in another directory. A relative path resolves from the project directory, and PITCHFORK_MISE_BIN takes precedence. A mise_bin in user or system config is no longer treated as the project's (#968, #972) by @Marukome0743.
  • watch now works in project directories whose names contain glob characters such as [, { or * (#974) by @JamBalaya56562.

Logs and proxy

  • pitchfork logs -n N --jq EXPR now shows the last N entries that match the filter. Before, it filtered only the last N entries (#985) by @JamBalaya56562.
  • logfmt values keep their original text unless they are plainly numbers, booleans or null. Quoted values always stay strings. For example, id=007 and code="42" can now be matched with --field and --jq (#978) by @JamBalaya56562.
  • A daemon in a nested project is no longer shown the proxy URL of a root daemon with the same name (#977) by @JamBalaya56562.
  • The proxy now uses a --label registered from a linked git worktree, not just one registered from the primary checkout (#1011) by @jdx.

Platform-specific

  • Windows: pitchfork stop now ends the daemon's whole process tree, including orphaned grandchildren and children that ignore Ctrl+C, by running each daemon in its own job object (#980, #997) by @JamBalaya56562.
  • Windows: with a PowerShell general.windows_shell or general.shell, a run string that starts with a quoted program path, such as "C:\Program Files\app\app.exe" --port 8080, now starts correctly (#1016) by @JamBalaya56562.
  • macOS: launchd now restarts the boot supervisor after a crash, as systemd already does on Linux. A clean pitchfork supervisor stop still leaves it stopped. To upgrade an existing registration, re-run pitchfork boot enable. A KeepAlive policy you set yourself is kept (#1009, #1014) by @jdx and @Marukome0743.
  • Linux musl: pitchfork builds again for musl targets such as x86_64-unknown-linux-musl (#1002) by @Marukome0743.

Full Changelog: v2.29.0...v2.30.0

💚 Sponsor pitchfork

pitchfork is built and maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.

If pitchfork has a place in your development workflow, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep pitchfork fast, free, and independent.