Repository navigation
Run the dev server and Vite with bun run --parallel - #38
Conversation
`bun run dev` started the Rust server and Vite through scripts/dev.ts, a hand-written supervisor: own process groups, forwarded signals, SIGSTOP/SIGCONT for Ctrl+Z. That replaces what the terminal already does: Ctrl+C, Ctrl+Z and hangup go to the whole foreground process group. Bun's built-in `bun run --parallel` runs the scripts side by side with prefixed output and stops the others when one fails. The scripts are now `dev:server` (`cargo run -p windfenster`) and `dev:web` (the web app's `bun run dev`); `dev` creates `.env` from `.env.example` on the first run, prints the banner and runs both. scripts/dev.ts and its process tests go; `bun run test` runs the web app's tests. Checked in a pty with stand-ins for cargo and the web app: Ctrl+C leaves nothing running, Ctrl+Z stops all and the shell takes input, `fg` resumes, a failing script stops the other. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y3rdfbWb1xDUdPRng7TX9Z
TDD-Review: 2 belegte FindingsGeprüft gegen das alte 1. Fehlt
|
The `;` after the .env step dropped the exit code of `cp`: without .env.example (or with a failing copy) the server and Vite started anyway, where scripts/dev.ts stopped with copyFileSync's error. `&&` chains the step into the rest, as `(a || b) && c`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y3rdfbWb1xDUdPRng7TX9Z
--parallel pipes each script's output (for the name prefix) and gives it /dev/null as input, so neither sees a terminal: cargo and Vite (picocolors checks stdout.isTTY or FORCE_COLOR) dropped their colours, which scripts/dev.ts kept by passing the terminal on. In a terminal `dev` now sets FORCE_COLOR and CARGO_TERM_COLOR. Vite's keyboard shortcuts need a terminal on stdin and stay with `bun run dev:web`; the docs say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y3rdfbWb1xDUdPRng7TX9Z
Fixes gepusht
Checks: t1_env_abort.sh#!/bin/sh
# usage: t1_env_abort.sh <package.json> -- .env cannot be created: `bun run dev` must stop before starting anything
set -u
T=$(mktemp -d); mkdir -p "$T/web" "$T/bin"
cp "$1" "$T/package.json" # no .env.example, no .env
echo '{"name":"@windfenster/web","private":true,"scripts":{"dev":"echo web-started"}}' > "$T/web/package.json"
printf '#!/bin/sh\necho server-started\n' > "$T/bin/cargo"; chmod +x "$T/bin/cargo"
out=$(cd "$T" && PATH="$T/bin:$PATH" bun run dev 2>&1); code=$?
echo "$out" | sed 's/^/ | /'
fail=0
[ "$code" -ne 0 ] || { echo "FAIL: exit code $code, expected non-zero"; fail=1; }
echo "$out" | grep -q 'server-started\|web-started' && { echo "FAIL: server/web started although .env could not be created"; fail=1; }
[ $fail -eq 0 ] && echo PASS
exit $failt2_color.py# usage: python3 -I t2_color.py <package.json> -- under a terminal, `bun run dev` must keep colours for cargo and Vite
import os, pty, sys, tempfile, json
pkg = sys.argv[1]
T = tempfile.mkdtemp(); os.makedirs(f"{T}/web"); os.makedirs(f"{T}/bin")
open(f"{T}/package.json", "w").write(open(pkg).read()); open(f"{T}/.env", "w").write("")
probe = f"{T}/probe.ts"
open(probe, "w").write("console.log(`${process.argv[2]} tty=${!!process.stdout.isTTY} FORCE_COLOR=${process.env.FORCE_COLOR} CARGO_TERM_COLOR=${process.env.CARGO_TERM_COLOR}`);\n")
json.dump({"name": "@windfenster/web", "private": True, "scripts": {"dev": f"bun {probe} web"}}, open(f"{T}/web/package.json", "w"))
open(f"{T}/bin/cargo", "w").write(f"#!/bin/sh\nexec bun {probe} server\n"); os.chmod(f"{T}/bin/cargo", 0o755)
pid, fd = pty.fork()
if pid == 0:
os.chdir(T); os.environ["PATH"] = f"{T}/bin:" + os.environ["PATH"]; os.environ.pop("FORCE_COLOR", None); os.environ.pop("CARGO_TERM_COLOR", None)
os.execvp("bun", ["bun", "run", "dev"])
out = b""
while True:
try: d = os.read(fd, 4096)
except OSError: break
if not d: break
out += d
lines = [l for l in out.decode(errors="replace").splitlines() if "tty=" in l]
for l in lines: print(" |", l)
web = next((l for l in lines if " web tty" in l), ""); srv = next((l for l in lines if " server tty" in l), "")
ok = True
if not ("tty=true" in web or "FORCE_COLOR=1" in web): print("FAIL: Vite sees no terminal and no FORCE_COLOR, its output loses its colours"); ok = False
if not ("tty=true" in srv or "CARGO_TERM_COLOR=always" in srv): print("FAIL: cargo sees no terminal and no CARGO_TERM_COLOR, its errors lose their colours"); ok = False
print("PASS" if ok else ""); sys.exit(0 if ok else 1)Generated by Claude Code |
|
TDD-Review abgeschlossen: 2 belegte Findings gefixt ( Generated by Claude Code |
Replaces the hand-written process supervisor
scripts/dev.ts(#37: own process groups, signal forwarding, SIGSTOP/SIGCONT) with Bun's built-inbun run --parallel. The terminal already sends Ctrl+C, Ctrl+Z and hangup to the whole foreground process group.New scripts in
package.json:dev:[ -f .env ] || { cp .env.example .env && echo '.env created from .env.example'; }; printf '\n Windfenster: http://localhost:5173\n\n' && bun run --parallel dev:server dev:webdev:server:cargo run -p windfensterdev:web:bun run --cwd web devtest:bun run --filter @windfenster/web test(no tests left inscripts/)scripts/dev.tsandscripts/dev.test.tsare deleted;docs/development.mdis updated.Verification
Tested in a real pty (interactive bash, Bun 1.4.4), with a stand-in
cargoon PATH and a stand-in webdevscript. Each stand-in is a Bun script that starts its ownsleepchild:.envis created on the first run and the banner is printedTand the shell takes input;fgresumes all of them (S); Ctrl+C then stops them allSignaled: SIGINT). Only thesleepgrandchildren stay behind (the failing script's own, and the other script's own).bun run devexits with 130, not 3.bun run lint,bun run typecheck,bun run testandbun run buildpass. The real server and real Vite were not started, andmkdocs build --strictwas not run locally (mkdocs is not installed).Generated by Claude Code