Skip to content

fix(install): build before replacing the working install - #118

Merged
ralyodio merged 1 commit into
masterfrom
fix/installer-atomic-build
Jul 31, 2026
Merged

fix(install): build before replacing the working install#118
ralyodio merged 1 commit into
masterfrom
fix/installer-atomic-build

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

A failed install left the machine with no CLI at all.

What happened

do_install destroyed the existing install before building:

rm -rf "$SRC_DIR"; mv "$tmp" "$SRC_DIR"          # working install gone
npm install ... >/dev/null 2>&1 || fail
npm run build:cli >/dev/null 2>&1 || fail        # if this fails, nothing left

If the build failed — or the run was interrupted, or the machine slept — what remained was an unbuilt tree, a wrapper still pointing at the dist/ that was never produced, and the previous run's install.json still claiming success (it lives beside src/, so rm -rf "$SRC_DIR" doesn't clear it).

Every later invocation died with no explanation:

Error: Cannot find module '.../packages/cli/dist/index.js'
code: 'MODULE_NOT_FOUND'

That's exactly what occurred on this machine: install.json dated 01:57, src/ replaced at 02:59 by a second run that never finished.

Fix

Download, npm install and build all happen in a staging directory. $SRC_DIR is only touched once packages/cli/dist/index.js exists — the file the wrapper execs, so its absence is precisely the failure the user would otherwise discover on their next command.

  • Staging lives inside $LOGICSRC_HOME, so the swap is a rename on one filesystem rather than a cross-device copy of node_modules.
  • The previous tree is kept until the swap succeeds, so a failed mv is undone.
  • INT/TERM/HUP clean up staging.

Build output is no longer discarded. "build failed" with no reason isn't a diagnosis — the last 25 lines are printed and the full log is left on disk.

The commit id from the GitHub API is validated before being recorded. Anything that isn't 40 hex characters is dropped rather than written into install.json, which logicsrc update compares against. (An error page or proxy HTML was previously stored verbatim as the commit.)

Verification

Stubbed npm/curl, all three paths:

scenario result
build fails existing install still runs; real npm error shown; staging cleaned
npm succeeds but emits no artifact caught by the dist/index.js check; install survives
clean install swaps in, correct wrapper + manifest, no leftovers

sh -n and dash -n both clean.

🤖 Generated with Claude Code

A failed install left the machine with no CLI at all. do_install ran
`rm -rf "$SRC_DIR"` and only then built; if the build failed, or the run
was interrupted, what remained was an unbuilt tree, a wrapper still
pointing at the dist/ that was never produced, and the previous run's
install.json still claiming success. Every later `logicsrc` invocation
died with MODULE_NOT_FOUND, and nothing said why.

That is what happened here: install.json dated 01:57, src/ replaced at
02:59 by a second run that did not finish.

Now the download, npm install and build all happen in a staging
directory, and $SRC_DIR is only touched once packages/cli/dist/index.js
actually exists -- the file the wrapper execs, so its absence is exactly
the failure the user would otherwise hit on their next command. Staging
sits inside $LOGICSRC_HOME so the swap is a rename on one filesystem
rather than a cross-device copy of node_modules, and the previous tree
is kept until the swap succeeds so a failed move can be undone.

Build output was going to /dev/null, so "build failed" carried no reason
at all. It is captured now, with the last 25 lines printed on failure and
the full log left on disk.

Also validates the commit id from the GitHub API before recording it:
anything that is not 40 hex characters is dropped rather than written
into install.json, which `logicsrc update` compares against.

Verified against a stubbed npm/curl in all three paths: a failing build
leaves the existing install running, a build that produces no artifact is
caught, and a clean install still swaps in and writes a correct manifest.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@ralyodio
ralyodio merged commit 87266bb into master Jul 31, 2026
5 checks passed
@ralyodio
ralyodio deleted the fix/installer-atomic-build branch July 31, 2026 03:18
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.

1 participant