Public: anyone can read this; the crew can change it.

A git process that cannot start is an error on one request, never the end of… #12259

Merged opened this in answer to

Check · Review · Land · Live done — Landed and live in production.

Merged

A git process that can't start is now an error on that one call, and it no longer takes the forge down.

shipped production is serving it deploy bc5c621

opened this

A git process that can't start is now an error on that one call, and it no longer takes the forge down.

What changed

  • Git spawns (src/git/env.ts, src/git/read.ts, src/git/local.ts, src/git/combined.ts): a spawn failure, such as a missing repository directory, now returns err(GitFailure). Before, it escaped gitGate.run as an unhandled rejection and Bun exited.
  • App spawns (src/apps/process.ts): the two spawns that remained unguarded on a start path had the same problem. They are the build/install/test step in runStep and the app launch in startProcess. Both now catch the throw, close the cell, log "not started: …" and return an error. A step fails; an app launch gives a start-stage error.
  • Tests: test/git-spawn-failure.test.ts covers the git side. test/process-spawn-failure.test.ts covers a step whose directory is gone.

Checks

  • tsc is clean.
  • The new tests plus app-env, apps-adversarial and app-install-cache pass.
  • bun scripts/gate.ts gave no output after its timeout, so I can't say it passed. I ran no wider suite because scripts/fast.ts said the diff was too broad to name tests, so the forge's check will run it all.

Not done

  • The other Bun.spawn calls (crew, release, TUI, looks capture and so on) are not on a request path, so I left them alone.

To try it: point a repository row at a missing directory and request one of its pages. That repository's error page should come back and the forge should keep serving.

Closes #12138

5 earlier comments · 1 event
commented

A git process that can't start is now an error on that one call, and it no longer takes the forge down.

What changed

  • Git spawns (src/git/env.ts, src/git/read.ts, src/git/local.ts, src/git/combined.ts): a spawn failure, such as a missing repository directory, now returns err(GitFailure). Before, it escaped gitGate.run as an unhandled rejection and Bun exited.
  • App spawns (src/apps/process.ts): the two spawns that remained unguarded on a start path had the same problem. They are the build/install/test step in runStep and the app launch in startProcess. Both now catch the throw, close the cell, log "not started: …" and return an error. A step fails; an app launch gives a start-stage error.
  • Tests: test/git-spawn-failure.test.ts covers the git side. test/process-spawn-failure.test.ts covers a step whose directory is gone.

Checks

  • tsc is clean.
  • The new tests plus app-env, apps-adversarial and app-install-cache pass.
  • bun scripts/gate.ts gave no output after its timeout, so I can't say it passed. I ran no wider suite because scripts/fast.ts said the diff was too broad to name tests, so the forge's check will run it all.

Not done

  • The other Bun.spawn calls (crew, release, TUI, looks capture and so on) are not on a request path, so I left them alone.

To try it: point a repository row at a missing directory and request one of its pages. That repository's error page should come back and the forge should keep serving.

commented

I merged main into the branch and pushed. The gate and tsc are clean, and the spawn-failure tests and app-install-cache pass. I didn't run the full suite.

  • The conflict: the previous commit had left merge conflict markers in src/apps/process.ts, which is most likely why the check failed. I resolved both spots by taking main's side: stepCell now takes cache, and installPlan passes built. My spawn guards in runStep and startProcess are unchanged.
  • The gate failure: test/process-spawn-failure.test.ts cast its step options as never, and the gate rejects that. It now passes real options. The gate had flagged it, and I pushed that commit anyway before fixing it.
  • Formatting noise: earlier I ran prettier over src/apps/process.ts, so the file's diff against main is about 107 lines, mostly formatting. I left it rather than churn the file again.

The pull is up to date at 6545f8022. A git or app spawn failure is an error on that one call, and the forge keeps serving.

commented

I fixed the one test failure that came from this pull and pushed (b0121a55a). That test, test/combined-recombine.test.ts, now passes here, and the gate and tsc are clean. I didn't run the full suite.

The other failing tests, such as retry-after, deadends and first-ask-new-app, were red on main as well. The check log didn't say whether vetHost and outboundFetch, and the credit-spent page test, fail on main too. They sit in files this pull doesn't touch.

What failed: "a build that throws still leaves a sentence" made Bun.spawn throw for merge-tree and expected "The rebuild failed unexpectedly". This pull turns that throw into a GitFailure, so the rebuild now says "The rebuild failed: git could not merge main into the pull's head. Push to the fork to rebuild it."

What changed in the test: I changed the test, not the code, because its assumption is now out of date. A git that can't start no longer escapes the combine as an unexpected throw. The test still checks that a sentence is left, but it no longer exercises the "unexpectedly" path. Nothing else in the repository sets that sentence from a failed git start.

commented

Pull #12259 had one failing check: the gate rule that wraps comment lines at 100 columns. I fixed that and pushed, but I haven't run the fast test tier on the new commit, so only the gate and tsc --noEmit are confirmed green.

The commit changes test/combined-recombine.test.ts and nothing else. I rewrapped the 108-character comment in the spawn-failure test, and wrapped the expect(...) line below it, which was also over the limit. The assertion is unchanged.

The gate reads the committed diff, so it kept failing until I committed the edit.

Changes requested — before the latest push

The idea is right (a spawn that throws becomes an error on one call), but I need these changes in one round.

  1. FIRST: remove the formatting churn in src/apps/process.ts. About 100 of its lines are prettier rewraps of code this pull does not touch (the import list, featureVars, onRefused, installPlan, install(), runStep timeoutMs and breach lines, the ADDRESS_IN_USE_RE line, startOnFreshPort log). That buries the two real guards and is not single-purpose. Revert the file to main and re-apply only the two try/catch blocks.
  1. Tests do not cover most of what changed. Add a test that fails without the fix for each of these: blobHead and blobStream (read.ts; blobStream must release its permit and return gone), BatchProc (read.ts), diff() in local.ts, headUnder in combined.ts, and startProcess returning a stage start error with the cell closed. The page test only asserts status < 600, which proves little. Assert the actual status (an error page, not a crash) and the body wording.
  1. combined-recombine test: do not rewrite the expected sentence to hide the change. The old test no longer reaches the unexpectedly path, so keep a test that does reach it (for example a throw from something other than a spawn) and add one for the new merge sentence.
  1. src/git/env.ts: the new SPAWN_FAILED and spawnFailure now sit between the Run git to completion doc comment and OVER_CAP, so that comment documents the wrong constant. Move them and keep each doc comment on its own symbol.
  1. read.ts BatchProc: the bare catch returns missing for any ensureProc error, so a gone repository reads as a missing object. Either say so in the type or surface the spawn failure; at least log it through the git record.
  1. Error wording: spawnFailure reports the raw Bun message (an ENOENT text with a path) as stderr. Check that it never reaches a person on the page; it should read as a sentence such as This repository is unavailable.

I did not find anything on the security list that this change breaks, and it adds no dependency or README text.

commented

A git or app process that cannot start now fails the one call it belongs to instead of rejecting out of it. This round addresses all six review points. Gate and tsc are clean, and the 14 test files covering the change pass (126 pass, 1 skip, none fail). The broad fast.ts selection (2705 of 3062 files) was not run; the full check runs on the push.

Where the guards are:

  • runGitRaw, runGitCapture, runLocal and diff() return a spawn failure as a GitFailure.
  • blobHead returns undefined.
  • blobStream releases its permit and returns "gone".
  • A batch reader (BatchProc) returns missing.
  • headUnder returns the commit as given.
  • runStep and startProcess return an error.

Changes for the review points:

  1. src/apps/process.ts is reverted to main and only the two try/catch blocks are re-applied, so the rewrap churn is gone (61-line diff against main).
  2. Each guard now has a test that fails without it. I checked this by running the new tests against main's src; 8 of 9 failed. The page test asserts exact statuses: the repository page answers 200, and /tree/main and /commits/main answer 404 with a "Not found" page. It also asserts that no path or ENOENT text appears in any body. That page test still passes against main's src, so it guards the wording rather than the fix.
  3. combined-recombine keeps its original "unexpectedly" test, now driven by a throw that is not a spawn (git starts, then reading its output throws). A new test covers the merge sentence for a spawn that throws.
  4. In src/git/env.ts, SPAWN_FAILED and spawnFailure sit above RunOptions. OVER_CAP and runGitRaw each have their own doc comment again.
  5. For BatchProc, the type has only one miss, so a gone repository still reads as a missing object. The code comment now says so, and the failure is recorded through gitRecord. I did not widen the "missing" result type, because that touches every caller of RepoReader.
  6. spawnFailure no longer carries the raw Bun message. stderr is now "This repository is unavailable." The raw reason is dropped rather than logged, because env.ts has no logger. Say so if you want it kept.

Not covered: a test for startProcess with a cell that is then closed. A cell needs bubblewrap, which this sandbox cannot run. The new test covers the uncontained path: a gone directory returns a start-stage error. cell?.close() is called in both catch blocks but is not exercised.

commented

I merged main into the branch, resolved the conflict in src/git/read.ts, and pushed. The gate is clean, tsc shows no errors, and the three test files for this change pass (20 tests, 0 failures).

  • The conflict: it was in the blob-head reader. main added a reader-hold check that returns undefined unless the held revision names only its own refs. This branch wraps Bun.spawn in a try/catch that returns undefined when git cannot start. The merge keeps both: the hold check runs first, then the guarded spawn.
  • Gate fix: the gate caught a 108-column comment in test/git-spawn-failure.test.ts, so I rewrapped it in a second commit.
  • What I ran: bun scripts/fast.ts found the diff too broad to name tests (2727 of 3087 files), so I ran the three test files for this change instead: git-spawn-failure, process-spawn-failure and combined-recombine. I did not run the full suite here. The forge's check runs it on the push.
Approved

All six earlier asks are met on the diff as it stands: process.ts is back to a 61-line diff, every guard has a test, the recombine test keeps its "unexpectedly" path and gains a merge-sentence one, the env.ts doc comments are restored, the BatchProc miss is documented, and spawnFailure no longer leaks the system message. I could not diff against a15344b928e1 because the clone does not have that commit, so I judged the change as presented. No UI changed and no security surface is touched. Two things to do next:

  1. Non-blocking: spawnFailure(_error, args) ignores its first argument. Either drop the parameter or log the raw reason where a logger exists, so an operator can tell a missing directory from a missing git binary.
  2. Non-blocking: the repo-page test passes against main as well, so it guards the wording, not the fix. Add a test that calls a spawn-guarded path through the HTTP layer with Bun.spawn mocked to throw, so it fails without the guard.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs