Summary
freshEnv() in tests/harness.mjs sets GIT_CONFIG_GLOBAL=/dev/null and
GIT_CONFIG_SYSTEM=/dev/null but does not supply a git identity. Any git invocation
that writes a ref therefore has no reachable user.email, so git falls back to
synthesizing user@host — which calls getaddrinfo() on the machine's hostname.
On a host where the hostname does not resolve promptly, that call blocks. The stub
herdr worktree create runs a real git worktree add, whose internal
git branch --quiet then hangs until runPane's 30s spawnSync timeout fires.
Result on such a host: 21 failures out of 237, all in fanout.test.mjs, and the
suite takes ~880s. With an identity supplied it is 237/237 in ~75s.
The harness already knows about this hazard — tests/harness.mjs:17 says "harness env
has GIT_CONFIG_GLOBAL=/dev/null, so identity must ride in" — and the module-scope
gitIdent correctly rides in the harness's own git() helper. The gap is that
freshEnv(), which is what the stub inherits, never got the same treatment.
Environment
- herdr-swarm
main (v0.3.0)
- Linux, git 2.43.0, Node 24
- Host hostname absent from
/etc/hosts; nsswitch hosts: files mdns4_minimal [NOTFOUND=return] dns
Reproduce
Simulate the resolver stall (or run on any host where getent hosts $(hostname) is slow
or times out), then:
node --test tests/fanout.test.mjs
Every test that reaches fan-out fails with status: null at exactly 30000ms.
Minimal proof the identity — not the plugin — is the variable:
git init -q r && cd r && git -c user.email=t@t -c user.name=t commit -q --allow-empty -m base
# hangs (exit 124)
timeout 10 env GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null \
git branch b1 HEAD
# 0.003s, exit 0
timeout 10 env GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null \
GIT_AUTHOR_NAME=t GIT_AUTHOR_EMAIL=t@t \
GIT_COMMITTER_NAME=t GIT_COMMITTER_EMAIL=t@t \
git branch b2 HEAD
Diagnosis trail
The failure reads exactly like a plugin hang or a herdr-version incompatibility, which
is what makes it worth guarding against. What identifies it:
$ cat /proc/<pid>/wchan
poll_schedule_timeout.constprop.0
$ ls -l /proc/<pid>/fd
3 -> .../.git/logs/refs/heads/<branch> # the reflog being written
4 -> socket:[...] # a socket, during a purely local ref write
$ strace -e trace=network ...
connect(4, {AF_UNIX, "/var/run/nscd/socket"}) = -1 ENOENT x4
socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) = 4 # UDP DNS query, unanswered
A socket opened during a local git branch is the tell.
Suggested fix
Add the identity to freshEnv() alongside the config-nulling it already does:
GIT_CONFIG_GLOBAL: "/dev/null",
GIT_CONFIG_SYSTEM: "/dev/null",
GIT_AUTHOR_NAME: "hs-test", GIT_AUTHOR_EMAIL: "hs-test@example.invalid",
GIT_COMMITTER_NAME: "hs-test", GIT_COMMITTER_EMAIL: "hs-test@example.invalid",
Reusing the exported gitIdent object would keep it DRY. This is a one-line-ish change
that also cut the suite from ~880s to ~75s here, so it may be worth it on CI runners
regardless of whether they exhibit the stall.
Happy to open a PR if useful.
Context: found while evaluating the plugin for adoption. Once the identity was supplied
the suite was fully green, so this is purely a harness portability issue — no defect in
the plugin itself.
Summary
freshEnv()intests/harness.mjssetsGIT_CONFIG_GLOBAL=/dev/nullandGIT_CONFIG_SYSTEM=/dev/nullbut does not supply a git identity. Anygitinvocationthat writes a ref therefore has no reachable
user.email, so git falls back tosynthesizing
user@host— which callsgetaddrinfo()on the machine's hostname.On a host where the hostname does not resolve promptly, that call blocks. The stub
herdr worktree createruns a realgit worktree add, whose internalgit branch --quietthen hangs untilrunPane's 30sspawnSynctimeout fires.Result on such a host: 21 failures out of 237, all in
fanout.test.mjs, and thesuite takes ~880s. With an identity supplied it is 237/237 in ~75s.
The harness already knows about this hazard —
tests/harness.mjs:17says "harness envhas GIT_CONFIG_GLOBAL=/dev/null, so identity must ride in" — and the module-scope
gitIdentcorrectly rides in the harness's owngit()helper. The gap is thatfreshEnv(), which is what the stub inherits, never got the same treatment.Environment
main(v0.3.0)/etc/hosts;nsswitchhosts: files mdns4_minimal [NOTFOUND=return] dnsReproduce
Simulate the resolver stall (or run on any host where
getent hosts $(hostname)is slowor times out), then:
Every test that reaches fan-out fails with
status: nullat exactly 30000ms.Minimal proof the identity — not the plugin — is the variable:
Diagnosis trail
The failure reads exactly like a plugin hang or a herdr-version incompatibility, which
is what makes it worth guarding against. What identifies it:
A socket opened during a local
git branchis the tell.Suggested fix
Add the identity to
freshEnv()alongside the config-nulling it already does:Reusing the exported
gitIdentobject would keep it DRY. This is a one-line-ish changethat also cut the suite from ~880s to ~75s here, so it may be worth it on CI runners
regardless of whether they exhibit the stall.
Happy to open a PR if useful.
Context: found while evaluating the plugin for adoption. Once the identity was supplied
the suite was fully green, so this is purely a harness portability issue — no defect in
the plugin itself.