Found while installing #60 on the live host, before it ran there.
Moving the deploy script into the repo put it inside the checkout it git reset --hards. Bash doesn't read a script all at once — it reads as it goes, by byte offset — so replacing the file underneath a running shell makes it stop wherever it had reached.
Silently, and with exit status 0. Demonstrated rather than assumed:
In a deploy that means: build the image, stop before replacing the container, report success. The old container keeps serving while everything says the new one shipped.
It only bites when a deploy carries a change to the deploy script itself — rare enough to be baffling when it happens, and exactly the quiet-failure shape this project keeps paying for. The old host script lived outside the checkout, so it never had this problem; moving it in is what introduced it.
The fix
Re-exec from a copy outside the tree before touching git, and remove the copy on exit. Running from outside the checkout skips the whole thing.
The trap is an if rather than [ -n … ] && trap. The latter rests on errexit ignoring a failed left operand of && — it does (I checked), but a deploy script is a poor place to depend on knowing that.
Testing
case
result
script inside checkout, overwrites itself mid-run
every later line runs; temp copy removed
same without the guard
execution stops at the rewrite, exit 0
script run from outside the checkout
no re-exec, unchanged behaviour
--help from both locations
exits 0
226 web + 75 server tests and typecheck unaffected.
Found while installing #60 on the live host, before it ran there.
Moving the deploy script into the repo put it **inside the checkout it `git reset --hard`s**. Bash doesn't read a script all at once — it reads as it goes, by byte offset — so replacing the file underneath a running shell makes it stop wherever it had reached.
Silently, and with **exit status 0**. Demonstrated rather than assumed:
```bash
echo "line A"
cat > "$0" <<'NEW'
echo "REWRITTEN"
NEW
echo "line B"
```
prints `line A`, never reaches `line B`, and exits `0`.
In a deploy that means: build the image, stop before replacing the container, report success. The old container keeps serving while everything says the new one shipped.
It only bites when a deploy carries a change to the deploy script itself — rare enough to be baffling when it happens, and exactly the quiet-failure shape this project keeps paying for. The old host script lived *outside* the checkout, so it never had this problem; moving it in is what introduced it.
## The fix
Re-exec from a copy outside the tree before touching git, and remove the copy on exit. Running from outside the checkout skips the whole thing.
The trap is an `if` rather than `[ -n … ] && trap`. The latter rests on errexit ignoring a failed left operand of `&&` — it does (I checked), but a deploy script is a poor place to depend on knowing that.
## Testing
| case | result |
|---|---|
| script inside checkout, overwrites itself mid-run | every later line runs; temp copy removed |
| same without the guard | execution stops at the rewrite, exit 0 |
| script run from outside the checkout | no re-exec, unchanged behaviour |
| `--help` from both locations | exits 0 |
226 web + 75 server tests and typecheck unaffected.
**Merged** 2026-08-26 as coffey-labs/ihasmail@fd0fe43ef72d
<sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Found while installing #60 on the live host, before it ran there.
Moving the deploy script into the repo put it inside the checkout it
git reset --hards. Bash doesn't read a script all at once — it reads as it goes, by byte offset — so replacing the file underneath a running shell makes it stop wherever it had reached.Silently, and with exit status 0. Demonstrated rather than assumed:
prints
line A, never reachesline B, and exits0.In a deploy that means: build the image, stop before replacing the container, report success. The old container keeps serving while everything says the new one shipped.
It only bites when a deploy carries a change to the deploy script itself — rare enough to be baffling when it happens, and exactly the quiet-failure shape this project keeps paying for. The old host script lived outside the checkout, so it never had this problem; moving it in is what introduced it.
The fix
Re-exec from a copy outside the tree before touching git, and remove the copy on exit. Running from outside the checkout skips the whole thing.
The trap is an
ifrather than[ -n … ] && trap. The latter rests on errexit ignoring a failed left operand of&&— it does (I checked), but a deploy script is a poor place to depend on knowing that.Testing
--helpfrom both locations226 web + 75 server tests and typecheck unaffected.
Merged 2026-08-26 as coffey-labs/ihasmail@fd0fe43ef7
Rebuilt from: git history, session transcript.