Skip to content

fix(dev-scripts): kill-backend no longer self-terminates dev:single (EVO-1609)#45

Merged
dpaes merged 1 commit into
developfrom
danilocarneiro/evo-1609-evo-flow-npm-run-devsingle-aborts-before-boot-kill-backend
Jun 10, 2026
Merged

fix(dev-scripts): kill-backend no longer self-terminates dev:single (EVO-1609)#45
dpaes merged 1 commit into
developfrom
danilocarneiro/evo-1609-evo-flow-npm-run-devsingle-aborts-before-boot-kill-backend

Conversation

@daniloleonecarneiro

Copy link
Copy Markdown

Summary

This is the fix on the real target named by EVO-1609 (evo-flow/package.json). The buggy kill-backend was still live on develop and main here. A prior PR — evolution-foundation/evo-campaign#9 — fixed the same latent bug in the evo-campaign scaffold (a distinct standalone repo sharing the backoffice@0.0.1 base); that one stands on its own merit, but the card's title, Module, Suspected Area and all 3 ACs name evo-flow, so this PR is what closes them.

npm run dev:single (npm run kill-backend && RUN_MODE=single npm run dev) aborted with Terminated before boot. kill-backend ran pkill -f 'node.*backend.*dist/main'; since pkill -f matches each process's full command line, the pattern text in pkill's own sh -c argv self-matched and killed the parent npm process before the && could chain into the Nest boot. (The old pattern also never matched a real backend — the prod cmdline is node dist/main.js, no backend token.)

Changes

  • package.json — harden kill-backend to pkill -u "$(id -u)" -f '[n]ode .*dist/(src/)?main(\.js)?( |$)':
    • [n] class matches a real node … but not the literal [n]ode in pkill's own argv → self-exclusion, no $$/$PPID bookkeeping;
    • evo-flow's nest build emits dist/main.js (tsc strips the src/ root — verified against the repo's dist/); the optional (src/)? keeps the script portable to a dist/src/main.js layout and matches both;
    • main(\.js)?( |$) rejects dist/maintenance.js / main-old.js;
    • -u "$(id -u)" scopes the blast radius to the current user.
  • scripts/kill-backend-pattern.test.sh + npm run test:kill-backend — deterministic, no-kill regression guard: extracts the ERE from package.json and grep-asserts match (prod/Docker/dev-watch + alt layout) and no-match (lookalikes + self-exclusion). Passes 10/10.
  • .github/workflows/dev-scripts-smoke.yml — runs the guard in CI on changes to package.json / the test script / the workflow.
  • README.md — documents dev:single, the [n] self-exclusion (immediate-shell only), -u scoping, the (src/)? rationale, the read-only pgrep preview, and the EADDRINUSE + Kafka caveats.

Test plan

  • npm run test:kill-backend → PASS (10/10) locally.
  • Verified the real build path: this repo's dist/ contains dist/main.js (no dist/src/), so the hardened pattern matches the actual runtime cmdline.
  • CI dev-scripts-smoke green on this PR.
  • Manual (WSL2/Linux, optional): pgrep -af '[n]ode .*dist/(src/)?main(\.js)?( |$)' previews targets without killing; npm run dev:single now advances past kill-backend (may still stop downstream at the out-of-scope Kafka journey_trigger_kafka_queue topic — EVO-1571 / EVO-1200).

Notes

  • Out of scope (per the card): the RUN_MODE=single Kafka-topic crash (EVO-1571 / EVO-1200) and general README/env sync (EVO-1229).

…EVO-1609)

`npm run dev:single` aborted with `Terminated` before boot: `kill-backend` ran
`pkill -f 'node.*backend.*dist/main'`, and because `pkill -f` matches each
process's FULL command line, the pattern text in pkill's own invoking `sh -c`
argv self-matched and killed the parent npm process before the `&&` could chain
into the Nest boot. (The old pattern also never matched a real backend — the
prod cmdline is `node dist/main.js`, with no "backend" token.)

Harden the pattern to `[n]ode .*dist/(src/)?main(\.js)?( |$)`:
- the `[n]` class matches a real `node …` process but not the literal `[n]ode`
  in pkill's own argv, so it self-excludes without `$$`/`$PPID` bookkeeping;
- evo-flow's `nest build` emits `dist/main.js` (tsc strips the src/ root); the
  optional `(src/)?` branch keeps the script portable to a `dist/src/main.js`
  layout (sibling evo-campaign scaffold / future monorepo) and matches both;
- `main(\.js)?( |$)` blocks lookalikes like `dist/maintenance.js`/`main-old.js`;
- `-u "$(id -u)"` scopes the blast radius to the current user's processes.

Add a deterministic regression guard (`scripts/kill-backend-pattern.test.sh`,
`npm run test:kill-backend`) that extracts the ERE from package.json and
grep-asserts match/no-match including self-exclusion, wired into CI via
`.github/workflows/dev-scripts-smoke.yml`. Document the step and its caveats in
the README Run Modes section.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @daniloleonecarneiro, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@dpaes dpaes left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Approved — EVO-1609

Correct-repo twin fix — this addresses the earlier reject where the patch landed on evo-campaign instead of the evo-flow target named by the card.

AC coverage — all 3 met:

  • AC1/AC2: the new kill-backend pattern pkill -u "$(id -u)" -f '[n]ode .*dist/(src/)?main(\.js)?( |$)' self-excludes via the [n] char-class trick while still matching the real start:prod signature (node dist/main), docker, dumb-init, watch, and the alt dist/src/main.js layout. Lookalikes (maintenance.js, main-old.js, mainx, main.json) are correctly excluded by the main(\.js)?( |$) boundary anchor. I independently reproduced all 10 match/no-match cases plus the simulated sh -c argv (old pattern self-matched → root cause; new pattern does not).
  • AC3: exceeds the either/or — scripts/kill-backend-pattern.test.sh guard + a dedicated path-filtered .github/workflows/dev-scripts-smoke.yml + a README dev:single repro note.

Non-blocking (no action required):

  • The guard is a deterministic grep-proxy (you can't safely spawn real pkill in CI) — the correct choice, documented as such.
  • [n] self-exclusion protects the immediate shell, not echoed copies (set -x, some CI log shells) — flagged honestly in the README.

Merging to develop.

@dpaes dpaes merged commit 20b0e9f into develop Jun 10, 2026
5 checks passed
@dpaes dpaes deleted the danilocarneiro/evo-1609-evo-flow-npm-run-devsingle-aborts-before-boot-kill-backend branch June 10, 2026 11:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants