Shell 1.12.1: a catalog install keeps the tarball's file modes #44

Open
michael wants to merge 1 commits from claude/shell-tar-exec-bits into main
Owner

The bug (AB, 2026-10-04, 1.10.2 rollout)

parseTar discarded each tar entry's mode, so installModule wrote every downloaded file with fs.writeFileSync's default 0644 — including the prebuilt helpers modules ship under bin/ (Timers 1.7.0's ltc-capture-*, SPL 0.5.0's spl-capture-*). spawn() then failed with EACCES: SPL showed its error and LTC showed a misleading "No signal" (its own bug, fixed separately in the Timers 1.7.1 PR). AB was hand-fixed with chmod +x; NA's pending 1.10.2 rollout would hit the same thing, so this should land and ship first.

carryOverExecutables (1.9.2) never applied here: it only carries executables the download does not include, and these downloads include the helpers.

The fix

  • parseTar keeps the header's mode field (octal at offset 100) — permission bits only, defaulting to 0644; setuid/sticky from an archive are deliberately dropped.
  • installModule's staging writes and the archive-based shell update both pass the mode to writeFileSync.

Version

main was 1.12.0 (the PR #34 1.9.3 bump and PR #39's 1.10.1 are both already merged underneath it) → 1.12.1.

Testing

tools/unbundled.test.js's mock repo now ships demo 2.0.0 with an executable bin/demo-capture (0755), and a new step ④ installs the update from the catalog and asserts the execute bit survived — alongside the existing 1.9.2 carry-over check (a per-machine executable the download lacks is still kept). Verified the new assertion fails against the unfixed installModule. All four shell tests pass: unbundled, update-check, layout-roundtrip, alerts.

🤖 Generated with Claude Code

## The bug (AB, 2026-10-04, 1.10.2 rollout) `parseTar` discarded each tar entry's mode, so `installModule` wrote every downloaded file with `fs.writeFileSync`'s default 0644 — including the prebuilt helpers modules ship under `bin/` (Timers 1.7.0's `ltc-capture-*`, SPL 0.5.0's `spl-capture-*`). `spawn()` then failed with EACCES: SPL showed its error and LTC showed a misleading "No signal" (its own bug, fixed separately in the Timers 1.7.1 PR). AB was hand-fixed with `chmod +x`; NA's pending 1.10.2 rollout would hit the same thing, so this should land and ship first. `carryOverExecutables` (1.9.2) never applied here: it only carries executables the download does **not** include, and these downloads include the helpers. ## The fix - `parseTar` keeps the header's mode field (octal at offset 100) — permission bits only, defaulting to 0644; setuid/sticky from an archive are deliberately dropped. - `installModule`'s staging writes and the archive-based shell update both pass the mode to `writeFileSync`. ## Version main was 1.12.0 (the PR #34 1.9.3 bump and PR #39's 1.10.1 are both already merged underneath it) → **1.12.1**. ## Testing `tools/unbundled.test.js`'s mock repo now ships `demo` 2.0.0 with an executable `bin/demo-capture` (0755), and a new step ④ installs the update from the catalog and asserts the execute bit survived — alongside the existing 1.9.2 carry-over check (a per-machine executable the download lacks is still kept). Verified the new assertion fails against the unfixed `installModule`. All four shell tests pass: `unbundled`, `update-check`, `layout-roundtrip`, `alerts`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
michael added 1 commit 2026-10-07 11:46:42 -04:00
parseTar threw away each entry's mode, so installModule wrote every
downloaded file 0644 — including the prebuilt helpers modules ship under
bin/. spawn() then failed with EACCES: on the 1.10.2 rollout (AB,
2026-10-04) that silently killed both LTC capture (Timers 1.7.0) and SPL
metering (0.5.0) until the binaries were chmod +x'd by hand.
carryOverExecutables (1.9.2) never applied: it only carries files the
download does NOT include, and these downloads include the helpers.

parseTar now keeps the header's mode field (permission bits only,
defaulting to 0644) and both writers pass it on: installModule's staging
and the archive-based shell update. The unbundled-shell test's mock repo
now ships an executable bin/ helper and asserts it still spawns-worthy
after a catalog install, alongside the existing carry-over check.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This pull request has changes conflicting with the target branch.
  • package.json
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin claude/shell-tar-exec-bits:claude/shell-tar-exec-bits
git checkout claude/shell-tar-exec-bits
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: michael/ProdDash#44