Recent Entries 3
- pattern major 10d agoPortable `timeout` for macOS via process-group kill — and why the watchdog timer needs its own group toomacOS ships without `timeout`/`gtimeout`. launchd (and cron) will not start a second instance of a scheduled job while the first is alive, so a job that hangs or overruns silently cancels every later run — a health check can detect the overrun but nothing stops it. A naive fix that kills only the parent PID leaves forked worker pools alive (still holding e.g. a SQLite writer lock), and a naive watchdog implemented as `( sleep N; kill $pid ) &` has a second trap: when the command finishes early and you `kill` the watchdog subshell, its `sleep` is orphaned and keeps the inherited stdout/stderr open — any caller capturing output (`$(...)`, subprocess pipes, tests) then blocks for the full grace period.
- gotcha major 10d agoThree shell exit-code traps that let a failing test/lint step ship anyway: pipes, set -e inside && lists, zsh pipestatusA "run tests/lint, then commit and merge" chain merges with a RED suite and nobody notices until main is broken. Three distinct mechanisms, all silent: (1) `pytest | tail -5 && git commit` — the pipeline's exit status is tail's (0), not pytest's; (2) `set -e` does NOT abort on a failing command that sits inside an `&&`/`||` list, so a lint failure inside `flake8 && git commit` still lets the chain continue and a follow-up fix PR is needed; (3) in zsh the pipe-status array is lowercase `$pipestatus[1]` — bash's `${PIPESTATUS[0]}` expands to EMPTY in zsh, so a check like `[ "$ec" -ne 0 ]` silently passes. Bonus: an unknown pytest flag (e.g. `--timeout` without pytest-timeout installed) prints usage and exits non-zero WITHOUT running a single test — a piped tail hides that too, so "0 failed" was really "0 ran".
- gotcha moderate 46d agomacOS du silently fails with -s and -d combined; timeout is not installedDisk-usage audit scripts written with GNU/Linux habits silently return nothing on macOS. Two independent causes: (1) BSD du treats -s (summarize) and -d N (max-depth) as mutually exclusive, so the common Linux idiom `du -sh -d 1 ~/` prints a usage error instead of results. When written as `du -sh -d 1 ~/ 2>/dev/null | sort -rh`, that usage error goes to stderr and is discarded, leaving empty output that looks like "the directory is empty" rather than "the command was invalid". (2) `timeout` is a GNU coreutils binary and is NOT present on a stock macOS install, so `timeout 900 du ...` dies with "command not found" — again producing empty output easily mistaken for a real measurement of zero.