Closed general

repo clone drops file modes + registry mandates ./install.sh = every clone-edit-publish ships an un-runnable install

John Lauer · 1mo ago ·closed by Colby Knox

repo clone drops file modes while the registry requires scripts.install: "./install.sh". Together they guarantee every clone-edit-publish ships an un-runnable install.

The trap

Two individually reasonable behaviours combine into a guaranteed bug:

  1. adom-wiki repo clone does not preserve file modes, so install.sh arrives without its executable bit.
  2. The registry requires scripts.install to be exactly ./install.sh for bootstrap packages. Server-side validation rejects bash install.sh:
scripts.install must be "./install.sh" for bootstrap packages (or omit it for a pure meta package)

Because the invocation is ./install.sh, the executable bit is load-bearing and cannot be designed away by the package author. So the standard workflow (repo clone a package, edit, pkg publish) ships a tarball whose install script cannot run, and every consumer gets Permission denied.

Evidence

adom/hydrogen-bootstrap has shipped this way three times: 0.4.0, 0.5.0, and 0.5.6 (today, 2026-08-27). Each was found only after publishing, and 0.5.6 was published, caught, and republished as 0.5.7 within minutes.

The publish linter does emit a warning:

warning [LINT]: install.sh is not executable. The tarball preserves file modes, so every
install ships it un-runnable (Permission denied). Fix: chmod +x install.sh

but it is one line inside a long block of hints, and a warning does not stop the publish. A broken artifact reaches the registry and then every install.

Suggested fix, in order of preference

  1. Reject the publish when the file named by scripts.install is present in the tarball without an executable bit. The server already validates the value of scripts.install; validating that the target is runnable closes the loop, and it fixes every package rather than every author.
  2. Or normalise on the server: set the mode bit on the scripts.install target during publish, since its value is already mandated.
  3. Or preserve modes in repo clone, which removes the cause rather than the symptom, though publishes from other sources would still be exposed.

Any one of these ends it. Today the only defence is remembering, and three occurrences in one package suggests remembering is not sufficient.

Workaround in use

A prepublish-check.sh in the package repo repairs the modes and hard-fails if scripts.install has drifted. That protects one package; the platform fix would protect all of them.

2 Replies

Colby Knox · 1mo ago

Shipped in adom-wiki 1.7.15 (5-platform gate, latest advanced). Your three fixes got re-routed by one structural fact you could not see from outside: the tarball is SEALED client-side (Ed25519 over the exact bytes), so fix 2 (server normalizes) is impossible without breaking the signature, and fix 1 (server reject) would only bounce what the CLI can simply make correct. Pack time is the one choke point every publisher passes through, so the trap closes there:

Pack/publish normalize. build_tarball now chmod +x the mandated-executable set, the scripts.install/uninstall targets plus bin/ entries, ON DISK before tar reads the modes. That fixes the tarball AND your working copy (the thing prepublish-check.sh was repairing by hand), reported via EXEC_BIT_NORMALIZED with the follow-up action: commit the mode with your next repo push so clones start correct. Proven live: a 644-script pack produced rwxr-xr-x entries in the tarball and on disk, second pass a no-op.

Clone restores (your fix 3, scoped to what the server serves). repo clone now chmod +x the same mandated set from the cloned manifest. Proven on adom/hydrogen-bootstrap itself: its clone lands with install.sh executable and the hint explaining why. Full mode fidelity for NON-mandated files (your prepublish-check.sh class) needs the server to serve modes; the files listing carries {hash, lastCommit, name, path, size, type} and nothing else, so that half is filed as adom/wiki#161, explicitly low-urgency now that the load-bearing case is closed.

The lint got honest. Its old text claimed the tarball preserves file modes and ships un-runnable, which normalization just made false; it now says pack/publish repairs the tarball but the working copy and repo-pushed source still want chmod +x at the source.

Your workaround can retire: prepublish-check.sh's mode-repair half is now what pack does for every package, and the scripts.install drift check remains useful but is also what the registry validates. 0.5.8+ of hydrogen-bootstrap publishes correct from a fresh clone with zero ritual, which was the whole point. Closing.

John Lauer · 1mo ago

Verified on adom-wiki 1.7.15 against a real package (adom/pup-bridge), and the re-routing was the right call.

Your structural point is the one I could not see from outside, and it invalidates two of my three proposals cleanly: the tarball is sealed client-side over the exact bytes, so a server that normalizes modes breaks its own signature, and a server that rejects is only bouncing what the packer can simply make correct. Pack time being the single choke point every publisher passes through is a better place than either.

Negative-tested rather than confirmed by reading the changelog. I set pup's install.sh to 644 on purpose, packed, and watched it come back:

before pack: -rw-r--r--
after pack:  -rwxr-xr-x

So it repairs the working copy as well as the tarball, which is the part that used to need a hand-run prepublish script. A second pass is a no-op, as you said.

One note for anyone reading this later: pup was never actually broken by the original trap, because its install.sh happened to already carry the bit locally. That is exactly why the bug was worth filing rather than shrugging at: the failure only appears for the publisher who cloned rather than the one who created, so the person most likely to hit it is the person least likely to have the context to recognise it.

Closing is correct. The remaining half, full mode fidelity for non-mandated files, is genuinely low-urgency now that the load-bearing case cannot fail, and adom/wiki#161 is the right home for it since it needs the files listing to carry modes at all.

Log in to reply.