install.sh: fall back to wget and to other zip extractors
Fixes #43642
Problem
- The install script exits with
unzip is required to install bunon a host withoutunzip, and withFailed to download bunon a host withoutcurl. Minimal containers and fresh distro images often lack both (src/runtime/cli/install.sh:56,:149). bun upgradehas the same hard dependency:Failed to locate "unzip" in PATH. bun upgrade needs "unzip" to work.(src/runtime/cli/upgrade_command.rs:820).
Fix
install.shdownloads withcurl, or withwget -q -Owhencurlis missing (BusyBox wget accepts these flags). It probesunzip,busybox,7z,7zz,7za,bsdtar, andpython3in that order and uses the first one it finds. Both probes run before the script creates any directory.bun upgradeprobes the same extractors through anUnzipProgramtable. Both probes skip abusyboxwhose--listhas nounzipapplet.bsdtarruns with--no-same-owner. After extraction the upgrade sets mode0755on the binary through the staging directory handle, becausepython3 -m zipfiledoes not restore the file mode. Its spawn and exit errors name the extractor that ran.- The release assets stay
.zip. Jarred said on #1740 that zip is needed for macOS signing. Tar.gz assets are tracked in #10211. - Verified:
test/cli/install/install-sh.test.ts(new) andtest/cli/install/bun-upgrade.test.ts(newdescribe, and a case for the failure message). Both run one case per extractor that exists on the host, plus the wget and the two error cases. The python3 and wget cases fail on main. Both files pass withbun bd test. A source lint,test/internal/source-lints/install-extractors.test.ts, checks that the two extractor lists and error texts stay equal. - Self-reviewed: 4 concerns raised, 3 addressed (the lint, the stale xattr comment, the docs sentence). Rejected: dropping
python3. It is the only fallback that CI can exercise, and it is the most common one on minimal Debian, Ubuntu, and Fedora images.
Background
bun.sh/installservessrc/runtime/cli/install.shdirectly. The script computes the target name, downloadsbun-<target>.zipfrom GitHub releases, extracts it, and adds the binary to the shell rc file.bun upgradedoes the same download in Rust and shells out tounzipso that the extracted binary keeps the attributes needed for codesigning.- The tests build a restricted
PATHwith symlinks to a few programs and serve a fake zip from a localBun.serve. This is how they make the host look like it lackscurlorunzip.
Notes
The issue also asked for an nvm-style PROFILE variable to skip rc-file edits. That is out of scope here. The script also spawns bun completions, which writes to the rc file on its own, so a PROFILE=/dev/null knob would not deliver what it promises. The rc-file opt-out is tracked in #1272 and PR #24968 (BUN_SKIP_SHELL_CONFIG).
Prior work: three older PRs proposed these fallbacks first. #16881 by erg has the extractor list that this PR uses (busybox, 7z, 7zz, 7za, bsdtar) for install.sh and for bun upgrade. A review there asked for an error text that names the alternatives. The extractor name in the bun upgrade failure messages also comes from #16881. #14111 by sequencerr has the busybox unzip fallback. #21785 by bnjmnjrk has the wget fallback. It passes --show-progress, which BusyBox wget rejects (unrecognized option '--show-progress'), so this PR uses wget -q -O. None of the three applies to the tree now: the script moved to src/runtime/cli/, and #16881 changes the Zig version of bun upgrade. This PR covers all three.
The old comment in upgrade_command.rs said that bun shells out to unzip to keep xattrs for codesigning. The actual reason is that the vendored libarchive only compiles in its tar reader since #39484, so there is no in-process zip reader. The comment now says that.
makeZipStored moved from bun-upgrade.test.ts into test/cli/install/fake-release.ts so both test files can use it.
Extractor coverage: every extractor ran for both entry points on a Debian 13 host. unzip and python3 come from the host. The others are built from source: BusyBox 1.36.1, 7-Zip 24.09 (7zz, also linked as 7z and 7za), and bsdtar 3.8.9 from vendor/libarchive. The wget case also passes with BusyBox wget. The fall-through for a busybox without the unzip applet ran against a stub busybox.
Suites run: test/cli/install/bun-upgrade.test.ts, test/cli/install/install-sh.test.ts.
no test proof · iteration 0 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/cli/install/bun-upgrade.test.ts