Repository navigation
v2.28.0: Argv-array run commands and cron timing in status
Daemons can now set run to an argv array so they start without a shell, and pitchfork status shows when a cron daemon last ran and when it runs next. This release also fixes how config changes reach cron daemons that are already running, stops file watching from using 100% CPU on large trees, and fixes several Windows problems with quoting, mise = true, and non-UTF-8 log output.
Added
-
Argv-array
runto start a daemon without a shell (#918) — @JamBalaya56562.runcan now be an array: the program, then its arguments. Pitchfork starts the program directly, so quotes, spaces,$,%and&reach it unchanged, whatever shell is configured and on every platform. The tracked PID is the program itself, including on Windows, where the string form recordscmd.exe.[daemons.api] run = ["node", "my server.js", "--title", "API \"v2\""]
Each argument is rendered as a template separately, and a rendered value stays a single argument. With
mise = truethe program runs asmise x -- <program> <args...>. An empty array or one that starts with"exec"is rejected. Shell features such as&&, pipes and$VARexpansion still need the string form. On Windows, a program named without an extension is only found as an.exe. Batch scripts need their extension (for example["npm.cmd", "run", "server"]) and still run throughcmd.exe.pitchfork daemons --jsonreportsrunexactly as written in the config, as either a string or an array. The string form works the same as before. -
Cron timing in
pitchfork status(#902) — @jdx. For scheduled daemons,statusnow shows the cron expression, the last scheduled start and the next scheduled time, each with a relative time:Cron: 0 0 2 * * * Last run: 2026-09-21 02:00:03 (7h 41m ago) Next run: 2026-09-22 02:00:00 (in 16h 18m)Last runonly counts scheduled starts that actually launched a process. Manual starts and skipped runs don't update it, and runs from before this upgrade aren't recovered. If a scheduled time has already passed, for example because the supervisor was stopped,Next runlabels itoverdue. The TUI detail pane shows the same timing. The CLI JSON and HTTP API entries now includecron_schedule,cron_last_runandcron_next_run(RFC 3339). CLI JSON leaves out fields that have no value, and the HTTP API returns them asnull.
Fixed
- Cron config changes now reach daemons that are already running (#928) — @JamBalaya56562. Before, a started daemon kept its old schedule until it was restarted. Now, if you change the expression or
retrigger, remove thecronline, or delete the daemon, the change takes effect on the next schedule check. If a project's config fails to parse, pitchfork leaves its schedules as they are. Deleting a project's last daemon or its whole config file isn't detected; usepitchfork disablein that case. - Templates and top-level
[env]now apply to boot-started daemons and to cron daemons that were never started by hand (#923) — @JamBalaya56562. Before,{{ ... }}reached these daemons unrendered and the top-level[env]was not applied.boot_startdaemons now also start independsorder, so a template can use the port of a boot daemon it depends on. - File watching no longer spins the supervisor on large trees (#921) — @jdx. A pattern for a single file, like
watch = ["SUMMARY.md"], used to watch the whole project recursively. On Linux this could keep the supervisor at 100% CPU, and on macOS it could causeToo many open files. Patterns now watch only the directories they can match. Only a**component still watches recursively, starting at the directory before it (for examplesrc/**/*.rswatches thesrctree). Also in this change:- Recursive watches no longer re-scan the tree in a loop when events are dropped.
- Removing
watchfrom the config now takes effect. - Directories created or moved into a watched location trigger restarts for the matching files inside them.
- A directory that fails to register, for example after hitting
fs.inotify.max_user_watches, is retried with backoff (starting at 10s, up to 5 minutes) instead of being silently dropped.
pty = truedaemons keep logging after non-UTF-8 output (#929) — @JamBalaya56562. A line that wasn't valid UTF-8 used to stop the log reader. On Linux the daemon could then stall writing to its PTY, and on Windows it could fail. Every line is now logged.- Windows: non-UTF-8 daemon output is decoded with the console code page (#916) — @JamBalaya56562. Messages from console programs, such as a CP932 error from
cmdon a Japanese system, now show up correctly inpitchfork logsinstead of as replacement characters. - Windows: double quotes in
runscripts now survivecmd.exe(#920) — @JamBalaya56562. A quoted program path such as'"C:\Program Files\app\app.exe" --name "a b"'now starts correctly instead of arriving with\"escapes. The same fix coversready_cmd,health_cmd, lifecycle hooks and the log archive hook. Daemons withmise = truedon't get this fix yet. Arunstring that only worked because of the old\"escaping may now behave differently. - Windows:
mise = truenow findsmise.exeonPATH(#922) — @JamBalaya56562. Before,mise = truewas silently ignored unlessgeneral.mise_binwas set. An explicitgeneral.mise_binstill takes priority.
Changed
- Faster startup on Linux (#932) — @jdx. The Linux GNU release binaries (x86_64, aarch64) are now linked as non-PIE, which saves about 0.6 ms on every command, including the shell-activation
cdhook. The tradeoff: ASLR no longer covers pitchfork's own code and data, though the heap, stack and shared libraries are still randomized. musl, macOS, Windows andcargo installbuilds are unchanged.
Full Changelog: v2.27.0...v2.28.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.