Repository navigation
fix(e2e/live): start-backend.sh records the server's pid, not a subshell's (objectui#12676) - #12737
Merged
Merged
Conversation
…ell's Only the `nohup …` command is backgrounded now, after the `cd`, so `$!` is the `objectstack dev` process itself. Backgrounding `cd … && nohup …` as one list recorded the forked subshell's pid: stop-backend.sh killed the subshell and left the server on its port, and the `already running` guard never matched, because the subshell's cmdline is the script's own. Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
Contributor
Author
席位核对 · ACCEPT · objectui#12737 (draft, head
|
This was referenced Oct 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #12676
Clause-②: no
e2e/live/ci/start-backend.shnow writes the PID of theobjectstack devprocess itself tobackend.pid. Soe2e/live/ci/stop-backend.shstops the server it started, and the port goes quiet. Before, the script backgrounded the wholecd … && nohup …list, and$!was the PID of a forked bash subshell that waited on the server. The stop script killed that subshell, and the server, reparented to PID 1, kept the port.The fix
One hunk in
start(). Thecd "$APP_DIR"now runs first inside the parenthesised subshell, and only thenohup …command is backgrounded. A backgrounded simple command forks once and execs.nohupand the bin'senv nodeshebang both exec in place, so$!is the server'snodeprocess.Everything else is unchanged:
OS_CLOUD_URL=off), the arguments and the log redirection;kill -0on the recorded PID at each poll;readyline and the exit codes;stop-backend.sh, which still kills only the recorded PID. Nothing kills by port or by process name.A comment above the hunk says why the
cdmust stay out of the backgrounded list.Reproduction (before: the base script at
96fa4b9bc)Setup for every run in this PR:
@objectstack/cli@18.0.0-next.2;LIVE_BACKEND_DIRin the session scratchpad;The base-script runs used
git show 96fa4b9bc:e2e/live/ci/start-backend.shbyte for byte (blob547ec18ab).ready: seeded sign-in answered on :4776 (pid 1092). The process tree from the recorded PID:bash …/start-backend.sh. This is the subshell.node ./node_modules/.bin/objectstack dev --seed-admin --fresh -p 4776.node …/objectstack serve --dev --port 4776. It holds the port.esbuild --service) and 1309 (node ./scripts/mcp-fixture.mjs): children of 1243.stop-backend.shprintedstopped pid 1092and exited 0.GET :4776/api/v1/healthstill answered 200. 1092 was gone. 1093, 1243, 1285 and 1309 were all alive, and 1093's PPID was now 1.starting objectstack dev on :4776, notalready running. The guard greps the recorded PID's cmdline forobjectstack, and the subshell's cmdline is the script's own.Port 4776 is in use — serving on 4777 instead.and bound 4777.ready … on :4776 (pid 1552)after 3 s. The FIRST server answered that check.Each leftover server was then stopped by the PID read above:
kill 1093, thenkill 1434 1553. Each tree was gone within seconds, and both ports then refused connections.Proof (after: this branch at
3a0843dcf)The same
LIVE_BACKEND_DIR(its stamp was reused) and the same port.ready: seeded sign-in answered on :4776 (pid 2201)./proc/2201/cmdlineisnode ./node_modules/.bin/objectstack dev --seed-admin --fresh -p 4776. The tree: 2201 (PPID 1), then 2221 (objectstack serve --dev --port 4776, the listener), then 2241 (esbuild) and 2252 (mcp-fixture.mjs).[live-backend] already running (pid 2201). The PID file still read 2201, and only 2221 listened on 4776.GET :4776/api/v1/healthanswered 200.stop-backend.shprintedstopped pid 2201and exited 0. Straight after, health got curl exit 7 (connection refused), and the same 3 s later. 2201, 2221, 2241 and 2252 were all gone. No process listened on any 4xxx port. The SIGTERM to thedevprocess takes itsservechild and the grandchildren down with it, so the recorded PID is enough.LIVE_BACKEND_PORT=70000): the CLI refused withInvalid port: --port "70000"and exited. The script printedbackend process died — last 100 log lines:and exited 1 after 7 s. The base script on the same input gave the same line, exit 1, in 7 s. The readiness loop checks the recorded PID withkill -0. Before, that PID was a subshell, which lived exactly as long as its one child, the server. Now it is the server. So the fail-fast path answers the same on both sides.CI is unchanged
.github/workflows/live-e2e.ymlcalls onlystart-backend.sh.stop-backend.shappears there only in the commentEphemeral runner — no teardown needed.start-backend.shexits the same way and prints the samereadyline on both sides: exit 0, and[live-backend] ready: seeded sign-in answered on :PORT (pid N). Only N changes, from the subshell's PID to the server's.bashwrapper and thennode(read on the objectui#12649 run). It should now list only thenodeprocesses. This was not measured on a runner. This PR's ownLive E2E (informational)run will show it.Gates (local, at
3a0843dcf, each exit captured before any pipe)bash -n e2e/live/ci/start-backend.sh: exit 0.node scripts/check-bash32-floor.mjs: exit 0,13 tracked shell file(s) … name no bash 4+ construct.node scripts/check-control-bytes.mjs: exit 0. Agrep -naPself-scan of the changed file found no control byte.node scripts/check-new-cross-file-line-citations.mjs: exit 0,0 new citation(s).node scripts/check-governed-queue-guard.mjs --test e2e/live/ci/start-backend.sh: exit 0,NOT GOVERNED.node scripts/check-changeset-presence.mjs: exit 0,no changeset is owed. No.changesetfile was added.pnpm exec vitest run scripts/__tests__/bash32-floor-wiring.test.ts scripts/__tests__/ci-cd-pipeline-doc.test.ts:Test Files 2 passed (2),Tests 97 passed (97), lockVERDICT command-exit 0. These are the only test files that namestart-backend.shore2e/live/ci(git grep -l).content/docs/guide/ci-cd-pipeline.mddoes not describe stopping the backend.Acceptance notes
18.0.0-next.2, which nothing in the tree re-derives (the comment says so too). No ablation, because no test pins this behaviour.stop-backend.shfalls back tokill -9after 10 s, and that hits only the recordeddevprocess. Ifdevever ignored SIGTERM, itsservechild would be orphaned. This was not observed: in every run here, SIGTERM ended the whole tree.AGENTS.md's per-agent test-stack section tells you to stop only by your own recorded PID, but the command it gives in the same parenthesis iskill $(lsof -ti tcp:PORT), which kills by port. Its service-discipline section says never to kill by port. Carrier: none.kill -0. Liveness above was read frompsstate, not fromkill -0.Generated by Claude Code