install.sh: fall back to wget and to other zip extractors

RO
2mo ago
+530-100

Fixes #43642

Problem

  • The install script exits with unzip is required to install bun on a host without unzip, and with Failed to download bun on a host without curl. Minimal containers and fresh distro images often lack both (src/runtime/cli/install.sh:56, :149).
  • bun upgrade has 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.sh downloads with curl, or with wget -q -O when curl is missing (BusyBox wget accepts these flags). It probes unzip, busybox, 7z, 7zz, 7za, bsdtar, and python3 in that order and uses the first one it finds. Both probes run before the script creates any directory.
  • bun upgrade probes the same extractors through an UnzipProgram table. Both probes skip a busybox whose --list has no unzip applet. bsdtar runs with --no-same-owner. After extraction the upgrade sets mode 0755 on the binary through the staging directory handle, because python3 -m zipfile does 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) and test/cli/install/bun-upgrade.test.ts (new describe, 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 with bun 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/install serves src/runtime/cli/install.sh directly. The script computes the target name, downloads bun-<target>.zip from GitHub releases, extracts it, and adds the binary to the shell rc file.
  • bun upgrade does the same download in Rust and shells out to unzip so that the extracted binary keeps the attributes needed for codesigning.
  • The tests build a restricted PATH with symlinks to a few programs and serve a fake zip from a local Bun.serve. This is how they make the host look like it lacks curl or unzip.
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