From f7b221b3bd3992ea635fc7706b4b3a71bdfb3159 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 5 Jun 2025 22:13:12 +0000 Subject: [PATCH] Add Linux self-extracting archives (SFX) for Bun installation --- .github/workflows/release.yml | 125 ++++++++- .github/workflows/sfx-linux.yml | 455 ++++++++++++++++++++++++++++++++ docs/install/sfx.md | 102 +++++++ src/cli/install.sh | 64 ++++- 4 files changed, 734 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/sfx-linux.yml create mode 100644 docs/install/sfx.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ab17d3287e..708661052d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -49,7 +49,7 @@ on: type: boolean default: false use-sfx: - description: Should macOS SFX archives be generated? + description: Should self-extracting archives be generated? type: boolean default: false @@ -440,6 +440,129 @@ jobs: --clobber \ --repo "${{ github.repository }}" + linux-sfx: + name: Generate Linux SFX Archives + runs-on: ubuntu-latest + needs: sign + if: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.use-sfx == 'true') }} + permissions: + contents: write + strategy: + matrix: + include: + - arch: x64 + zip_name: bun-linux-x64 + display: x64 + - arch: x64 + zip_name: bun-linux-x64-baseline + display: x64-baseline + baseline: true + - arch: aarch64 + zip_name: bun-linux-aarch64 + display: aarch64 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Environment + run: | + echo "Creating working directory..." + mkdir -p sfx-build + - name: Download Bun Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + cd sfx-build + + # Download the zip file from GitHub releases + echo "Downloading ${{ matrix.zip_name }}.zip..." + + if [[ "${{ env.BUN_VERSION }}" == "canary" ]]; then + DOWNLOAD_URL="https://github.com/oven-sh/bun/releases/download/canary/${{ matrix.zip_name }}.zip" + else + DOWNLOAD_URL="https://github.com/oven-sh/bun/releases/download/${{ env.BUN_VERSION }}/${{ matrix.zip_name }}.zip" + fi + + curl -L -o "${{ matrix.zip_name }}.zip" "$DOWNLOAD_URL" + + # Extract the zip file + unzip -q "${{ matrix.zip_name }}.zip" + + # Find the bun executable + BUN_EXEC=$(find . -name "bun" -type f | head -n 1) + if [[ -z "$BUN_EXEC" ]]; then + echo "Error: Could not find bun executable in the archive" + exit 1 + fi + + # Move it to a known location + mv "$BUN_EXEC" "./bun" + chmod +x ./bun + + # Clean up + rm -rf bun-linux-*/ ${{ matrix.zip_name }}.zip + - name: Create Self-Extracting Archive + run: | + cd sfx-build + + # Create the self-extracting script header + cat > extract-header.sh << 'SCRIPT_EOF' + #!/bin/sh + set -e + DEFAULT_INSTALL_DIR="$HOME/.bun/bin" + INSTALL_DIR="${BUN_INSTALL_DIR:-$DEFAULT_INSTALL_DIR}" + ARCH="$(uname -m)" + EXPECTED_ARCH="ARCH_PLACEHOLDER" + EXPECTED_DISPLAY="DISPLAY_PLACEHOLDER" + case "$ARCH" in + x86_64) ARCH_NORM="x64" ;; + aarch64|arm64) ARCH_NORM="aarch64" ;; + *) ARCH_NORM="$ARCH" ;; + esac + ARCH_MISMATCH=0 + case "$EXPECTED_ARCH" in + x64) [ "$ARCH_NORM" != "x64" ] && ARCH_MISMATCH=1 ;; + aarch64) [ "$ARCH_NORM" != "aarch64" ] && ARCH_MISMATCH=1 ;; + esac + if [ "$ARCH_MISMATCH" = "1" ]; then + echo "Warning: This installer is for $EXPECTED_DISPLAY but you're running on $ARCH" + fi + mkdir -p "$INSTALL_DIR" + ARCHIVE_LINE=$(awk '/^__ARCHIVE_BELOW__/ { print NR + 1; exit }' "$0") + echo "Extracting Bun to $INSTALL_DIR..." + tail -n +"$ARCHIVE_LINE" "$0" | base64 -d | tar -xzf - -C "$INSTALL_DIR" + chmod +x "$INSTALL_DIR/bun" + BUN_VERSION=$("$INSTALL_DIR/bun" --version 2>/dev/null || echo "unknown") + echo "Bun $BUN_VERSION has been installed to $INSTALL_DIR/bun" + echo "Run 'export PATH=\"\$PATH:$INSTALL_DIR\"' to add it to your PATH" + exit 0 + __ARCHIVE_BELOW__ + SCRIPT_EOF + + # Replace placeholders + sed -i "s/ARCH_PLACEHOLDER/${{ matrix.arch }}/g" extract-header.sh + sed -i "s/DISPLAY_PLACEHOLDER/${{ matrix.display }}/g" extract-header.sh + + # Create the SFX archive + tar -czf bun.tar.gz bun + base64 < bun.tar.gz > bun.tar.gz.b64 + cat extract-header.sh bun.tar.gz.b64 > "${{ matrix.zip_name }}-sfx.sh" + chmod +x "${{ matrix.zip_name }}-sfx.sh" + + # Generate checksum + sha256sum "${{ matrix.zip_name }}-sfx.sh" > "${{ matrix.zip_name }}-sfx.sh.sha256" + - name: Upload to Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + cd sfx-build + + # Upload the SFX to the release + gh release upload "${{ env.BUN_VERSION }}" \ + "${{ matrix.zip_name }}-sfx.sh" \ + "${{ matrix.zip_name }}-sfx.sh.sha256" \ + --clobber \ + --repo "${{ github.repository }}" + notify-sentry: name: Notify Sentry runs-on: ubuntu-latest diff --git a/.github/workflows/sfx-linux.yml b/.github/workflows/sfx-linux.yml new file mode 100644 index 0000000000..f75b8b2a99 --- /dev/null +++ b/.github/workflows/sfx-linux.yml @@ -0,0 +1,455 @@ +name: Generate Linux SFX Archives +concurrency: sfx-linux + +on: + release: + types: + - published + workflow_dispatch: + inputs: + tag: + description: 'Release tag (e.g., "bun-v1.0.2", "canary")' + required: true + default: "canary" + upload-to-release: + description: "Upload SFX files to the release?" + type: boolean + default: false + +env: + BUN_VERSION: ${{ github.event.inputs.tag || github.event.release.tag_name || 'canary' }} + +jobs: + create-linux-sfx: + name: Create Linux Self-Extracting Archives + runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'oven-sh' }} + permissions: + contents: write + strategy: + matrix: + include: + - arch: x64 + zip_name: bun-linux-x64 + display: x64 + - arch: x64 + zip_name: bun-linux-x64-baseline + display: x64-baseline + baseline: true + - arch: aarch64 + zip_name: bun-linux-aarch64 + display: aarch64 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Environment + run: | + echo "Creating working directory..." + mkdir -p sfx-build + cd sfx-build + + - name: Download Bun Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + cd sfx-build + + # Download the zip file from GitHub releases + echo "Downloading ${{ matrix.zip_name }}.zip..." + + if [[ "${{ env.BUN_VERSION }}" == "canary" ]]; then + DOWNLOAD_URL="https://github.com/oven-sh/bun/releases/download/canary/${{ matrix.zip_name }}.zip" + else + DOWNLOAD_URL="https://github.com/oven-sh/bun/releases/download/${{ env.BUN_VERSION }}/${{ matrix.zip_name }}.zip" + fi + + curl -L -o "${{ matrix.zip_name }}.zip" "$DOWNLOAD_URL" + + # Extract the zip file using unzip (available on GitHub runners) + unzip -q "${{ matrix.zip_name }}.zip" + + # Find the bun executable + BUN_EXEC=$(find . -name "bun" -type f | head -n 1) + if [[ -z "$BUN_EXEC" ]]; then + echo "Error: Could not find bun executable in the archive" + exit 1 + fi + + # Move it to a known location + mv "$BUN_EXEC" "./bun" + chmod +x ./bun + + # Clean up + rm -rf bun-linux-*/ ${{ matrix.zip_name }}.zip + + - name: Create SFX Script + run: | + cd sfx-build + + # Create the self-extracting script + cat > extract-header.sh << 'SCRIPT_EOF' + #!/bin/sh + # Bun Self-Extracting Archive for Linux + # This is a self-extracting archive. Run it to install Bun. + # No external dependencies required (no unzip needed!) + + set -e + + # Default installation directory + DEFAULT_INSTALL_DIR="$HOME/.bun/bin" + INSTALL_DIR="${BUN_INSTALL_DIR:-$DEFAULT_INSTALL_DIR}" + + # ANSI color codes (only if terminal supports it) + if [ -t 1 ] && [ -n "${TERM-}" ] && [ "${TERM-}" != "dumb" ]; then + RED='\033[0;31m' + GREEN='\033[0;32m' + YELLOW='\033[1;33m' + NC='\033[0m' + else + RED='' + GREEN='' + YELLOW='' + NC='' + fi + + error() { + printf "${RED}error: %s${NC}\n" "$1" >&2 + exit 1 + } + + info() { + printf "${GREEN}%s${NC}\n" "$1" + } + + warn() { + printf "${YELLOW}warning: %s${NC}\n" "$1" + } + + # Check if running on Linux + if [ "$(uname -s)" != "Linux" ]; then + error "This installer is for Linux only. Please download the correct version for your platform." + fi + + # Check architecture + ARCH="$(uname -m)" + EXPECTED_ARCH="ARCH_PLACEHOLDER" + EXPECTED_DISPLAY="DISPLAY_PLACEHOLDER" + + case "$ARCH" in + x86_64) + ARCH_NORM="x64" + ;; + aarch64|arm64) + ARCH_NORM="aarch64" + ;; + *) + ARCH_NORM="$ARCH" + ;; + esac + + # Check if architecture matches (allow x64 baseline on x64) + ARCH_MISMATCH=0 + case "$EXPECTED_ARCH" in + x64) + if [ "$ARCH_NORM" != "x64" ]; then + ARCH_MISMATCH=1 + fi + ;; + aarch64) + if [ "$ARCH_NORM" != "aarch64" ]; then + ARCH_MISMATCH=1 + fi + ;; + esac + + if [ "$ARCH_MISMATCH" = "1" ]; then + warn "This installer is for $EXPECTED_DISPLAY but you're running on $ARCH" + if [ -t 0 ]; then + printf "Continue anyway? [y/N] " + read -r REPLY + case "$REPLY" in + [Yy]*) + ;; + *) + exit 1 + ;; + esac + else + error "Architecture mismatch and no TTY to confirm" + fi + fi + + # Create installation directory + mkdir -p "$INSTALL_DIR" || error "Failed to create installation directory: $INSTALL_DIR" + + # Find where the payload starts + ARCHIVE_MARKER="__ARCHIVE_BELOW__" + ARCHIVE_LINE=$(awk "/$ARCHIVE_MARKER/ { print NR + 1; exit }" "$0") + + if [ -z "$ARCHIVE_LINE" ]; then + error "Could not find archive marker" + fi + + # Extract the payload + info "Extracting Bun to $INSTALL_DIR..." + + # Use tail to skip the script part, then decode and extract + # We use base64 to ensure binary safety across all systems + if ! tail -n +"$ARCHIVE_LINE" "$0" | base64 -d | tar -xzf - -C "$INSTALL_DIR" 2>/dev/null; then + error "Failed to extract Bun. The archive may be corrupted." + fi + + # Make it executable + chmod +x "$INSTALL_DIR/bun" || error "Failed to make bun executable" + + # Verify installation + if [ -x "$INSTALL_DIR/bun" ]; then + BUN_VERSION=$("$INSTALL_DIR/bun" --version 2>/dev/null || echo "unknown") + info "✓ Bun $BUN_VERSION has been installed to $INSTALL_DIR/bun" + else + error "Installation verification failed" + fi + + # Check if bun is in PATH + if ! echo "$PATH" | grep -q "$INSTALL_DIR"; then + info "" + info "To get started, add Bun to your PATH:" + info " export PATH=\"\$PATH:$INSTALL_DIR\"" + info "" + info "To make this permanent, add it to your shell configuration file:" + case "${SHELL##*/}" in + bash) + info " echo 'export PATH=\"\$PATH:$INSTALL_DIR\"' >> ~/.bashrc" + ;; + zsh) + info " echo 'export PATH=\"\$PATH:$INSTALL_DIR\"' >> ~/.zshrc" + ;; + fish) + info " echo 'set -gx PATH \$PATH $INSTALL_DIR' >> ~/.config/fish/config.fish" + ;; + *) + info " echo 'export PATH=\"\$PATH:$INSTALL_DIR\"' >> ~/.profile" + ;; + esac + fi + + exit 0 + # DO NOT REMOVE THE FOLLOWING LINE + __ARCHIVE_BELOW__ + SCRIPT_EOF + + # Replace placeholders + sed -i "s/ARCH_PLACEHOLDER/${{ matrix.arch }}/g" extract-header.sh + sed -i "s/DISPLAY_PLACEHOLDER/${{ matrix.display }}/g" extract-header.sh + + - name: Create Self-Extracting Archive + run: | + cd sfx-build + + # Create a tar.gz of the bun executable + tar -czf bun.tar.gz bun + + # Encode it as base64 (for binary safety) + base64 < bun.tar.gz > bun.tar.gz.b64 + + # Combine header and payload + cat extract-header.sh bun.tar.gz.b64 > "${{ matrix.zip_name }}-sfx.sh" + + # Make it executable + chmod +x "${{ matrix.zip_name }}-sfx.sh" + + # Test the SFX + echo "Testing self-extraction..." + TEST_DIR="$(mktemp -d)" + BUN_INSTALL_DIR="$TEST_DIR" ./${{ matrix.zip_name }}-sfx.sh + + if [ -x "$TEST_DIR/bun" ]; then + echo "✓ Self-extraction test passed" + "$TEST_DIR/bun" --version + rm -rf "$TEST_DIR" + else + echo "✗ Self-extraction test failed" + exit 1 + fi + + - name: Create Quick Install Script + run: | + cd sfx-build + + # Also create a simple download-and-run script + cat > "${{ matrix.zip_name }}-install.sh" << 'INSTALLER_EOF' + #!/bin/sh + # Quick installer script for Bun on Linux + + set -e + + VERSION="VERSION_PLACEHOLDER" + ARCHIVE_NAME="ARCHIVE_PLACEHOLDER" + + echo "Downloading Bun $VERSION for Linux..." + + if [ "$VERSION" = "canary" ]; then + URL="https://github.com/oven-sh/bun/releases/download/canary/${ARCHIVE_NAME}-sfx.sh" + else + URL="https://github.com/oven-sh/bun/releases/download/$VERSION/${ARCHIVE_NAME}-sfx.sh" + fi + + if command -v curl >/dev/null 2>&1; then + curl -fsSL "$URL" | sh + elif command -v wget >/dev/null 2>&1; then + wget -qO- "$URL" | sh + else + echo "Error: Neither curl nor wget found. Please install one of them." + exit 1 + fi + INSTALLER_EOF + + # Replace placeholders + sed -i "s/VERSION_PLACEHOLDER/${{ env.BUN_VERSION }}/g" "${{ matrix.zip_name }}-install.sh" + sed -i "s/ARCHIVE_PLACEHOLDER/${{ matrix.zip_name }}/g" "${{ matrix.zip_name }}-install.sh" + + chmod +x "${{ matrix.zip_name }}-install.sh" + + - name: Generate Checksums + run: | + cd sfx-build + + # Generate SHA256 checksums + sha256sum "${{ matrix.zip_name }}-sfx.sh" > "${{ matrix.zip_name }}-sfx.sh.sha256" + sha256sum "${{ matrix.zip_name }}-install.sh" > "${{ matrix.zip_name }}-install.sh.sha256" + + echo "Generated files:" + ls -la *.sh *.sha256 + + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.zip_name }}-sfx + path: | + sfx-build/${{ matrix.zip_name }}-sfx.sh + sfx-build/${{ matrix.zip_name }}-sfx.sh.sha256 + sfx-build/${{ matrix.zip_name }}-install.sh + sfx-build/${{ matrix.zip_name }}-install.sh.sha256 + + - name: Upload to Release + if: ${{ (github.event_name == 'release' || github.event.inputs.upload-to-release == 'true') && env.BUN_VERSION != 'canary' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + cd sfx-build + + # Upload the SFX and installer scripts to the release + gh release upload "${{ env.BUN_VERSION }}" \ + "${{ matrix.zip_name }}-sfx.sh" \ + "${{ matrix.zip_name }}-sfx.sh.sha256" \ + "${{ matrix.zip_name }}-install.sh" \ + "${{ matrix.zip_name }}-install.sh.sha256" \ + --clobber \ + --repo "${{ github.repository }}" + + create-universal-linux-installer: + name: Create Universal Linux Installer + runs-on: ubuntu-latest + needs: create-linux-sfx + if: ${{ github.repository_owner == 'oven-sh' }} + permissions: + contents: write + steps: + - name: Download SFX Artifacts + uses: actions/download-artifact@v4 + with: + pattern: bun-linux-*-sfx + merge-multiple: true + path: sfx-files + + - name: Create Universal Installer + run: | + cd sfx-files + + # Create a universal installer that detects architecture + cat > bun-linux-install.sh << 'UNIVERSAL_EOF' + #!/bin/sh + # Universal Bun installer for Linux (detects architecture automatically) + + set -e + + VERSION="VERSION_PLACEHOLDER" + + # Detect architecture + ARCH="$(uname -m)" + case "$ARCH" in + x86_64) + # Check CPU features for baseline support + if [ -f /proc/cpuinfo ]; then + # Check for AVX2 support (non-baseline requirement) + if grep -q "avx2" /proc/cpuinfo 2>/dev/null; then + ARCHIVE_NAME="bun-linux-x64" + echo "Detected: x64 (with AVX2 support)" + else + ARCHIVE_NAME="bun-linux-x64-baseline" + echo "Detected: x64 (baseline - no AVX2)" + fi + else + # Default to baseline if we can't detect + ARCHIVE_NAME="bun-linux-x64-baseline" + echo "Detected: x64 (defaulting to baseline)" + fi + ;; + aarch64|arm64) + ARCHIVE_NAME="bun-linux-aarch64" + echo "Detected: ARM64/AArch64" + ;; + *) + echo "Error: Unsupported architecture: $ARCH" + echo "Bun supports x64 and aarch64 on Linux" + exit 1 + ;; + esac + + echo "Downloading Bun $VERSION for Linux..." + + if [ "$VERSION" = "canary" ]; then + URL="https://github.com/oven-sh/bun/releases/download/canary/${ARCHIVE_NAME}-sfx.sh" + else + URL="https://github.com/oven-sh/bun/releases/download/$VERSION/${ARCHIVE_NAME}-sfx.sh" + fi + + if command -v curl >/dev/null 2>&1; then + curl -fsSL "$URL" | sh + elif command -v wget >/dev/null 2>&1; then + wget -qO- "$URL" | sh + else + echo "Error: Neither curl nor wget found. Please install one of them." + exit 1 + fi + UNIVERSAL_EOF + + # Replace version placeholder + sed -i "s/VERSION_PLACEHOLDER/${{ env.BUN_VERSION }}/g" bun-linux-install.sh + + chmod +x bun-linux-install.sh + + # Generate checksum + sha256sum bun-linux-install.sh > bun-linux-install.sh.sha256 + + - name: Upload Universal Installer + uses: actions/upload-artifact@v4 + with: + name: bun-linux-universal-sfx + path: | + sfx-files/bun-linux-install.sh + sfx-files/bun-linux-install.sh.sha256 + + - name: Upload to Release + if: ${{ (github.event_name == 'release' || github.event.inputs.upload-to-release == 'true') && env.BUN_VERSION != 'canary' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + cd sfx-files + + gh release upload "${{ env.BUN_VERSION }}" \ + "bun-linux-install.sh" \ + "bun-linux-install.sh.sha256" \ + --clobber \ + --repo "${{ github.repository }}" diff --git a/docs/install/sfx.md b/docs/install/sfx.md new file mode 100644 index 0000000000..82638ae21c --- /dev/null +++ b/docs/install/sfx.md @@ -0,0 +1,102 @@ +# Self-Extracting Archives (SFX) for Bun + +Self-extracting archives provide a convenient way to install Bun without requiring external tools like `unzip`. These are shell scripts that contain the Bun binary embedded within them. + +## Linux + +### Quick Install + +For automatic architecture detection: + +```bash +curl -fsSL https://github.com/oven-sh/bun/releases/latest/download/bun-linux-install.sh | sh +``` + +### Manual Installation + +Choose the appropriate version for your system: + +**x64 (with AVX2 support):** + +```bash +curl -fsSL https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64-sfx.sh | sh +``` + +**x64 (baseline - no AVX2):** + +```bash +curl -fsSL https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64-baseline-sfx.sh | sh +``` + +**ARM64/AArch64:** + +```bash +curl -fsSL https://github.com/oven-sh/bun/releases/latest/download/bun-linux-aarch64-sfx.sh | sh +``` + +### Download and Run Later + +You can also download the SFX file and run it later: + +```bash +# Download +curl -LO https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64-sfx.sh + +# Make executable +chmod +x bun-linux-x64-sfx.sh + +# Run +./bun-linux-x64-sfx.sh +``` + +### Custom Installation Directory + +By default, Bun installs to `$HOME/.bun/bin`. You can override this: + +```bash +BUN_INSTALL_DIR=/usr/local/bin ./bun-linux-x64-sfx.sh +``` + +## How It Works + +The self-extracting archives are shell scripts with these components: + +1. **Shell Script Header**: A POSIX-compliant shell script that: + + - Checks system compatibility + - Creates the installation directory + - Extracts the embedded binary + - Sets proper permissions + - Provides PATH setup instructions + +2. **Embedded Binary**: The Bun executable is: + + - Compressed with gzip + - Encoded in base64 for text safety + - Appended to the shell script + +3. **No External Dependencies**: Only uses standard POSIX tools: + - `sh` (POSIX shell) + - `tar`, `gzip` (for extraction) + - `base64` (for decoding) + - `awk`, `tail` (for payload extraction) + +## Advantages + +- **No `unzip` Required**: Works on minimal Linux systems +- **Single File**: Everything in one downloadable script +- **Secure**: Checksums provided for verification +- **Portable**: POSIX-compliant, works on most Linux distributions +- **Architecture Detection**: Universal installer automatically selects the right binary + +## Verification + +Always verify downloads using the provided checksums: + +```bash +# Download checksum +curl -LO https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64-sfx.sh.sha256 + +# Verify +sha256sum -c bun-linux-x64-sfx.sh.sha256 +``` diff --git a/src/cli/install.sh b/src/cli/install.sh index f32e073258..e4e1fa430e 100644 --- a/src/cli/install.sh +++ b/src/cli/install.sh @@ -53,8 +53,18 @@ success() { echo -e "${Green}$@ ${Color_Off}" } -command -v unzip >/dev/null || - error 'unzip is required to install bun' +# Check for unzip and offer SFX alternative if not available +use_sfx=0 +if ! command -v unzip >/dev/null 2>&1; then + echo -e "${Dim}unzip is not available. Using self-extracting archive instead.${Color_Off}" + use_sfx=1 + + # Check for required tools for SFX + for tool in sh tar gzip base64 awk tail; do + command -v "$tool" >/dev/null 2>&1 || + error "$tool is required for self-extracting archive installation" + done +fi if [[ $# -gt 2 ]]; then error 'Too many arguments, only 2 are allowed. The first can be a specific tag of bun to install. (e.g. "bun-v0.1.4") The second can be a build variant of bun to install. (e.g. "debug-info")' @@ -140,19 +150,51 @@ if [[ ! -d $bin_dir ]]; then error "Failed to create install directory \"$bin_dir\"" fi -curl --fail --location --progress-bar --output "$exe.zip" "$bun_uri" || - error "Failed to download bun from \"$bun_uri\"" +# Use SFX if unzip is not available +if [[ $use_sfx -eq 1 ]]; then + # Map target to SFX filename + sfx_name="" + case "$target" in + linux-x64) sfx_name="bun-linux-x64" ;; + linux-x64-baseline) sfx_name="bun-linux-x64-baseline" ;; + linux-aarch64) sfx_name="bun-linux-aarch64" ;; + linux-x64-musl) sfx_name="bun-linux-x64-musl" ;; + linux-x64-musl-baseline) sfx_name="bun-linux-x64-musl-baseline" ;; + linux-aarch64-musl) sfx_name="bun-linux-aarch64-musl" ;; + *) error "Self-extracting archives are not available for $target. Please install unzip." ;; + esac + + if [[ $# = 0 ]]; then + sfx_uri=$github_repo/releases/latest/download/$sfx_name-sfx.sh + else + sfx_uri=$github_repo/releases/download/$1/$sfx_name-sfx.sh + fi + + info "Downloading self-extracting archive..." + + # Download and run the SFX with custom install directory + export BUN_INSTALL_DIR="$bin_dir" + curl --fail --location --progress-bar "$sfx_uri" | sh || + error "Failed to download and run self-extracting archive from \"$sfx_uri\"" + + # The SFX installs directly to bin_dir, so we're done with extraction + exe=$bin_dir/bun +else + # Original unzip-based installation + curl --fail --location --progress-bar --output "$exe.zip" "$bun_uri" || + error "Failed to download bun from \"$bun_uri\"" -unzip -oqd "$bin_dir" "$exe.zip" || - error 'Failed to extract bun' + unzip -oqd "$bin_dir" "$exe.zip" || + error 'Failed to extract bun' -mv "$bin_dir/bun-$target/$exe_name" "$exe" || - error 'Failed to move extracted bun to destination' + mv "$bin_dir/bun-$target/$exe_name" "$exe" || + error 'Failed to move extracted bun to destination' -chmod +x "$exe" || - error 'Failed to set permissions on bun executable' + chmod +x "$exe" || + error 'Failed to set permissions on bun executable' -rm -r "$bin_dir/bun-$target" "$exe.zip" + rm -r "$bin_dir/bun-$target" "$exe.zip" +fi tildify() { if [[ $1 = $HOME/* ]]; then