mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-10-26 22:37:03 +00:00
dbbbefda75d4fc4aaeac7bdaecd65eab772f00a9
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9e6791d4f1 |
fix(scripts): install-refresh asks git in the caller's checkout, not the server's cwd (BUG-3403) (#1790)
* fix(scripts): install-refresh asks git in the caller's checkout, not the server's cwd (BUG-3403) Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS * fix(scripts): anchor the web/ pathspec at the repo root (codex r1 on BUG-3403) Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS |
||
|
|
d4ec587de9 |
fix(cli): a CLI call during an install-refresh bounce waits for the refresh instead of auto-starting a server over it (BUG-3215) (#1544)
install-refresh.sh stops the server, installs, and restarts it with the
captured argv. Any pad CLI call in between found nothing answering and
auto-started a server of its own: the caller's binary, cwd and default
flags. That server held the port against the refresh's restart (day 79:
127.0.0.1:7777 went dark and the script exited 1).
- The script writes <DataDir>/refresh.lock {pid, port, deadline} by rename
before the stop and removes it in its EXIT trap, only while it is still
its own. The deadline is 10s + PAD_PROBE_TIMEOUT (30s by default).
- EnsureServer defers to a live marker for its own port (writer pid alive
AND deadline not passed): it polls health and never spawns. If the
writer is still alive past the deadline it errors naming the pid. A dead
writer, a passed deadline, another port or an unparseable file means
the marker is ignored.
- The script names a holder that took the port before the restart spawns
(and does not spawn it), and refuses success when the listener after
the probe is not the restart it spawned. A health probe answers for
whoever holds the address.
- The script-test dependency guard now reads every install_refresh*_test.go.
Claude-Session: https://claude.ai/code/session_01A3rXV7GDLAhBe9mL4V6z2m
|
||
|
|
b2bee3242f |
fix(scripts): install-refresh restores the server's own cwd and matches the running server by identity (BUG-3196) (#1512)
* fix(scripts): install-refresh restores the server's own cwd with its argv, refuses a deleted one, and matches the running server by identity rather than the built binary's name (BUG-3196) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N * fix(scripts): one existence check refuses a deleted server cwd, naming the path without the kernel's suffix (BUG-3196) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N * test(scripts): the no-/proc refresh test declares lsof, which now reads the server's cwd there (BUG-3196) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N * test(scripts): the lsof dependency sits on the requireScriptDeps line the guard reads (BUG-3196) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N * docs(make): the install target's guarantees include the restored cwd (BUG-3196) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N * fix(scripts): the server's cwd is re-checked immediately before the stop, and the capture's limits are stated (BUG-3196) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N * fix(scripts): a failed restart cd says what state it leaves, not that no server runs (BUG-3196) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N * fix(scripts): an unreadable server cwd is refused naming what was read and its error, and the case is tested (BUG-3196) Claude-Session: https://claude.ai/code/session_01RYiqbqXmxQMoKHsGm8B77N |
||
|
|
c3d64451ed |
test(buildtools): the install-refresh pre-server proves it is the listener the script probes (TASK-3146) (#1443)
* test(buildtools): the install-refresh pre-server proves it is the listener the script probes (TASK-3146) Seven tests pre-start a "running server" on a port chosen by freePort, which releases the port before the stub binds it, and then waited only until SOMETHING answered on it. A stub that lost the port in that window exits on the bind error and the dial succeeds against the foreign listener, so the script's outcome was judged against someone else's process. The port cannot be 0 there, as it is in BUG-3144's two-server test, because the script restarts that server and reads the port from PAD_PORT or the captured argv. startPreServer therefore proves two things instead: the stub's own STUB_ANNOUNCE line, with the port it bound equal to the chosen one, and its pid at the address the script will probe (a new /stub/pid route), which covers a wildcard bind beside a held specific address on a stack that permits it. A collision is now a named fixture failure. Tests force each case: a foreign listener answering health ok on the chosen port, a probe address that reaches someone else, and a stub that bound a different port, plus a positive control. Claude-Session: https://claude.ai/code/session_01Vf8uZmYdsrQqJtbAmjUFSX * test(buildtools): hold the chosen port in the port-proof case so the stub's port 0 cannot land on it (TASK-3146, codex round 1) Claude-Session: https://claude.ai/code/session_01Vf8uZmYdsrQqJtbAmjUFSX |
||
|
|
94381d09a5 |
test(buildtools): start the two-server fixture on port 0 and wait on each stub's own announcement (BUG-3144) (#1434)
* test(buildtools): start the two-server fixture on port 0 and wait on each stub's own announcement (BUG-3144) TestInstallRefresh_RefusesWhenSeveralServersAreRunning failed once on CI's Go (PostgreSQL) job (run 35738655417): "script proceeded with two servers running". The script was right. The fixture had one server. The script counts server PROCESSES (pgrep -x plus the `server start` argv). The test picked two "free" ports with freePort, which releases the port before the stub binds it, and proved readiness by dialing each port. A stub whose port was taken in between (by the other stub, or by anything else on a busy runner) exits on the bind error, the dial succeeds against the other listener, and only ONE stub process exists. The script correctly proceeds, and the test blames it. The stub now binds via net.Listen and, with STUB_ANNOUNCE=1, prints the port it actually got. Otherwise it behaves as before (a failed bind exits). The test starts both stubs on port 0 and waits on each process's own "LISTENING <port>" line, so readiness is tied to that process and two stubs cannot share a port. A stub that exits is now a fixture failure naming itself, not an assertion about the script. Evidence, pre-committed on BUG-3144 before running: - unfixed, forced portB = portA: 3/3 fail with the CI message - unfixed, a foreign listener pre-bound on portB: 3/3 fail, same message - unfixed, unmutated, -count=30, all CPUs busy + a port-churning load: 2/30 fail (one exactly the CI message; one the same mechanism with a failed restart) - fixed, the second stub forced onto the first's port: 3/3 fail as "stub exited without announcing a port" (a fixture failure, not a wrong verdict about the script) - fixed, same contention + churn, -count=50: 50/50 pass Not changed: eight other tests pre-start a server on a chosen port the script must restart on, so they cannot use port 0. They share the pick-then-bind window. Claude-Session: https://claude.ai/code/session_01ByMjAhvdWgbAWWeGE9jwWd * test(buildtools): reap announced stubs in t.Cleanup and keep draining their stdout (BUG-3144) Codex round 1, both valid: startAnnouncedStub started each stub without ever calling Wait, and its reader stopped after the first line, so a stub writing more could block on a full pipe. Each stub is now killed and reaped in t.Cleanup, which runs even when an assertion fails. Wait closes the pipe, ending the reader. The reader drains stdout after the announcement. Hygiene, not measured: with the old code the defer's pkill plus the test binary's exit leave nothing observable afterwards, so no counterfactual discriminates this change. Stated rather than claimed. Claude-Session: https://claude.ai/code/session_01ByMjAhvdWgbAWWeGE9jwWd |
||
|
|
110045578c |
fix(build): make install proves what it installed and what it restarted (BUG-2897, TASK-2787) (#1272)
`make install` made three claims it did not check.
1. It brought the server back BY SIDE EFFECT -- `pad auth whoami` triggers
an auto-start, which does not know the killed process's argv. A server
running `--host 0.0.0.0` came back bound to the default host alone:
curl 127.0.0.1:7777 -> 000 while the LAN address -> 200, with the
process count and the version both reading correct (BUG-2897).
2. `cp` copied whatever was at the repo path, not what the invocation
built. Two sessions sharing the checkout interleave and the loser's
build is installed by the winner, every exit code green (TASK-2787).
3. "Server restarted." was printed after a command ending in `|| true`,
with no probe of any kind. Not "the wrong address went unverified" --
nothing was verified. Found reading the recipe; neither filing names it,
and it is what made the other two invisible.
The logic moves to scripts/install-refresh.sh for one reason above
readability: a script can be TESTED. internal/buildtools drives it against
a compiled stub `pad`, including the branches where a check must FAIL.
Recipe-inline logic is only exercisable by running `make install`, which
stops the developer's server -- a test nobody runs twice, which is how this
target accumulated three unverified claims.
The script checks OUTCOMES rather than steps: what got installed, and what
is answering afterwards. Two commit checks, deliberately, answering
different questions -- the ARTIFACT before the kill, so a wrong build costs
an error instead of an outage, and the DESTINATION after the copy, which is
the shared-path race TASK-2787 names. The restart uses the argv read from
/proc before the kill, and nothing is printed about a restart until the
server answers on BOTH 127.0.0.1 and the configured host (`--host 0.0.0.0`
resolving to loopback plus the primary LAN address, since 0.0.0.0 is a bind
spec, not something to curl).
Three defects were found in the fix itself, by its own tests and by the
first real run:
- The restart redirected to $HOME/.pad/server.log with nothing creating
that directory. On a fresh HOME the redirect fails, the server never
starts, and the probe reports "did not answer" -- true, and three steps
downstream. Invisible to every hand-run this script could have had,
because a developer's box has ~/.pad by luck of history.
- The post-copy check SURVIVED its mutant: both fixtures reported the same
version from source and destination, so the guard and its absence were
indistinguishable. Fixed by making the stub's version depend on its path.
- Comparing commits by equality rejected a healthy build. `git rev-parse
--short` returns the shortest UNAMBIGUOUS prefix, so its width grows with
the object database: the binary embedded `a3a1d58` and the Makefile
produced `a3a1d586` minutes later. Now a prefix comparison in either
direction, with a negative leg pinning that a genuinely different commit
of the same width is still refused.
Five mutants, each verified to compile, each detected by its own leg.
Verified end to end on the real box: captured `--host 0.0.0.0`, installed
|