From dafb9a08fd9d08a5abf112d80afe290b0c49ba27 Mon Sep 17 00:00:00 2001 From: Microck Date: Fri, 9 Oct 2026 13:27:38 +0200 Subject: [PATCH 1/2] ci: publish static Linux musl release binaries --- .github/workflows/release.yml | 39 ++++++++++++++++++++ CHANGELOG.md | 4 ++ docs/content/docs/guides/installation.mdx | 16 ++++---- docs/content/docs/guides/troubleshooting.mdx | 5 ++- docs/release-runbook.md | 3 ++ 5 files changed, 58 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 46c7902..2cd7be5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -60,6 +60,13 @@ jobs: - os: ubuntu-latest target: aarch64-unknown-linux-gnu archive_ext: tar.gz + - os: ubuntu-latest + target: x86_64-unknown-linux-musl + archive_ext: tar.gz + # Build ARM64 musl natively so musl-gcc also compiles ring correctly. + - os: ubuntu-24.04-arm + target: aarch64-unknown-linux-musl + archive_ext: tar.gz - os: macos-15-intel target: x86_64-apple-darwin archive_ext: tar.gz @@ -91,14 +98,46 @@ jobs: sudo apt-get update sudo apt-get install -y gcc-aarch64-linux-gnu + - name: Install musl toolchain + if: endsWith(matrix.target, '-linux-musl') + run: | + sudo apt-get update + sudo apt-get install -y musl-tools binutils + - name: Cache cargo artifacts uses: Swatinem/rust-cache@v2 - name: Build release binary + if: "!endsWith(matrix.target, '-linux-musl')" env: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: ${{ matrix.target == 'aarch64-unknown-linux-gnu' && 'aarch64-linux-gnu-gcc' || '' }} run: cargo build --locked --release --target ${{ matrix.target }} + - name: Build static musl release binary + if: endsWith(matrix.target, '-linux-musl') + env: + CC: musl-gcc + CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER: musl-gcc + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER: musl-gcc + RUSTFLAGS: -C target-feature=+crt-static + run: cargo build --locked --release --target ${{ matrix.target }} + + - name: Verify static musl release binary + if: endsWith(matrix.target, '-linux-musl') + env: + TARGET: ${{ matrix.target }} + run: | + set -euo pipefail + binary="target/$TARGET/release/kagi" + readelf --program-headers "$binary" > program-headers.txt + readelf --dynamic "$binary" > dynamic-section.txt + if grep -q 'INTERP' program-headers.txt || grep -q '(NEEDED)' dynamic-section.txt; then + echo "Expected a static musl binary without a dynamic loader or shared libraries" >&2 + exit 1 + fi + "$binary" --version + "$binary" --help + - name: Package release archive (unix) if: runner.os != 'Windows' env: diff --git a/CHANGELOG.md b/CHANGELOG.md index 08e4f30..384ea4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## [Unreleased] +### Added + +- Static Linux release binaries for `x86_64-unknown-linux-musl` and `aarch64-unknown-linux-musl`, with archives, bare binaries, and SHA-256 checksums (#200). + ## [0.21.1] ### Fixed diff --git a/docs/content/docs/guides/installation.mdx b/docs/content/docs/guides/installation.mdx index 734622b..0c5e682 100644 --- a/docs/content/docs/guides/installation.mdx +++ b/docs/content/docs/guides/installation.mdx @@ -221,11 +221,13 @@ For users who want full control over the installation process or need to install 1. Visit the [GitHub Releases page](https://github.com/Microck/kagi-cli/releases) 2. Choose the latest release (or a specific version) 3. Download the appropriate asset for your platform: - - macOS Intel: `kagi-x86_64-apple-darwin.tar.gz` - - macOS Apple Silicon: `kagi-aarch64-apple-darwin.tar.gz` - - Linux x86_64: `kagi-x86_64-unknown-linux-gnu.tar.gz` - - Linux ARM64: `kagi-aarch64-unknown-linux-gnu.tar.gz` - - Windows x64: `kagi-x86_64-pc-windows-msvc.zip` + - macOS Intel: `kagi-vX.Y.Z-x86_64-apple-darwin.tar.gz` + - macOS Apple Silicon: `kagi-vX.Y.Z-aarch64-apple-darwin.tar.gz` + - Linux x86_64: `kagi-vX.Y.Z-x86_64-unknown-linux-gnu.tar.gz` + - Linux ARM64: `kagi-vX.Y.Z-aarch64-unknown-linux-gnu.tar.gz` + - Linux x86_64 (static, including Alpine/NixOS): `kagi-vX.Y.Z-x86_64-unknown-linux-musl.tar.gz` + - Linux ARM64 (static, including Alpine/NixOS): `kagi-vX.Y.Z-aarch64-unknown-linux-musl.tar.gz` + - Windows x64: `kagi-vX.Y.Z-x86_64-pc-windows-msvc.zip` ### Step 2: Extract the Binary @@ -382,11 +384,11 @@ xattr -d com.apple.quarantine $(which kagi) - Fedora 32+ - CentOS/RHEL 8+ - Arch Linux -- Other distros (e.g. Alpine): no prebuilt musl binary is published yet, so build from source (see below) +- Alpine and NixOS: choose the static `*-unknown-linux-musl` release asset for your architecture **Dependencies:** -The binary is statically linked and has no runtime dependencies beyond the Linux kernel. +The `*-unknown-linux-musl` binaries are statically linked and do not require glibc or a dynamic loader. The `*-unknown-linux-gnu` binaries require glibc. **Shell Completion:** diff --git a/docs/content/docs/guides/troubleshooting.mdx b/docs/content/docs/guides/troubleshooting.mdx index 41adab6..176b04e 100644 --- a/docs/content/docs/guides/troubleshooting.mdx +++ b/docs/content/docs/guides/troubleshooting.mdx @@ -500,8 +500,9 @@ ldd $(which kagi) **2. Use musl build (static):** ```bash -# Download musl version instead of gnu -wget https://github.com/Microck/kagi-cli/releases/download/.../kagi-x86_64-unknown-linux-musl.tar.gz +# Replace vX.Y.Z with a release tag that includes musl assets. +# Use aarch64-unknown-linux-musl for ARM64. +wget https://github.com/Microck/kagi-cli/releases/download/vX.Y.Z/kagi-vX.Y.Z-x86_64-unknown-linux-musl.tar.gz ``` **3. Install missing libraries:** diff --git a/docs/release-runbook.md b/docs/release-runbook.md index b5140b5..f768f04 100644 --- a/docs/release-runbook.md +++ b/docs/release-runbook.md @@ -62,9 +62,12 @@ git push origin vX.Y.Z - builds release artifacts for: - `x86_64-unknown-linux-gnu` - `aarch64-unknown-linux-gnu` + - `x86_64-unknown-linux-musl` (static) + - `aarch64-unknown-linux-musl` (static, built on a native ARM64 runner) - `x86_64-apple-darwin` - `aarch64-apple-darwin` - `x86_64-pc-windows-msvc` +- checks musl binaries for dynamic loaders/shared-library dependencies and smoke-tests `--version` and `--help` - uploads archives plus raw binaries - generates `kagi-vX.Y.Z-checksums.txt` - extracts release notes from `CHANGELOG.md` From 51c7e250ee8fde0c61e24795d0dad490ca23a957 Mon Sep 17 00:00:00 2001 From: Microck Date: Fri, 9 Oct 2026 13:30:08 +0200 Subject: [PATCH 2/2] ci: verify both static musl builds before merge --- .github/workflows/musl.yml | 56 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 .github/workflows/musl.yml diff --git a/.github/workflows/musl.yml b/.github/workflows/musl.yml new file mode 100644 index 0000000..66add4f --- /dev/null +++ b/.github/workflows/musl.yml @@ -0,0 +1,56 @@ +name: Static Linux builds +'on': + push: + branches: + - '**' + pull_request: null +permissions: + contents: read +concurrency: + group: musl-${{ github.ref }} + cancel-in-progress: true +jobs: + musl: + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + target: x86_64-unknown-linux-musl + - os: ubuntu-24.04-arm + target: aarch64-unknown-linux-musl + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + steps: + - name: Checkout + uses: actions/checkout@v7.0.1 + with: + persist-credentials: false + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + - name: Install musl toolchain + run: 'sudo apt-get update + + sudo apt-get install -y musl-tools binutils + + ' + - name: Cache cargo artifacts + uses: Swatinem/rust-cache@v2 + - name: Build static musl release binary + env: + CC: musl-gcc + CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER: musl-gcc + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER: musl-gcc + RUSTFLAGS: -C target-feature=+crt-static + run: cargo build --locked --release --target ${{ matrix.target }} + - name: Verify static musl release binary + env: + TARGET: ${{ matrix.target }} + run: "set -euo pipefail\nbinary=\"target/$TARGET/release/kagi\"\nreadelf --program-headers\ + \ \"$binary\" > program-headers.txt\nreadelf --dynamic \"$binary\" > dynamic-section.txt\n\ + if grep -q 'INTERP' program-headers.txt || grep -q '(NEEDED)' dynamic-section.txt;\ + \ then\n echo \"Expected a static musl binary without a dynamic loader or\ + \ shared libraries\" >&2\n exit 1\nfi\n\"$binary\" --version\n\"$binary\"\ + \ --help\n"