From 5f7043d43d77d4298dbc7968acd35262d9b65c40 Mon Sep 17 00:00:00 2001 From: Cardin Lee <553816+cardin@users.noreply.github.com> Date: Thu, 15 Oct 2026 14:53:56 +0800 Subject: [PATCH 1/2] feat(release): add static x86_64 linux musl binary --- .github/workflows/release.yml | 11 +++++++++++ README.md | 7 +++++++ scripts/smoke-musl.sh | 28 ++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 scripts/smoke-musl.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e885c04..518a3bf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,6 +26,7 @@ jobs: matrix: include: - { target: x86_64-unknown-linux-gnu, os: ubuntu-22.04 } + - { target: x86_64-unknown-linux-musl, os: ubuntu-22.04 } - { target: aarch64-unknown-linux-gnu, os: ubuntu-22.04-arm } - { target: x86_64-apple-darwin, os: macos-15-intel } - { target: aarch64-apple-darwin, os: macos-15 } @@ -41,7 +42,17 @@ jobs: - uses: Swatinem/rust-cache@v2 with: key: ${{ matrix.target }} + - name: install musl linker + if: matrix.target == 'x86_64-unknown-linux-musl' + run: sudo apt-get update && sudo apt-get install -y musl-tools - run: cargo build --release -p plannotator-tui --target ${{ matrix.target }} + - name: smoke-test musl on older glibc and Alpine + if: matrix.target == 'x86_64-unknown-linux-musl' + run: | + for image in debian:bullseye-slim alpine:3.24; do + docker run --rm -v "$PWD:/work:ro" -w /work "$image" \ + sh scripts/smoke-musl.sh "target/${{ matrix.target }}/release/plannotator-tui" + done - name: name the asset by target shell: bash run: | diff --git a/README.md b/README.md index 9656b4a..019c666 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,13 @@ Homebrew 6 asks you to trust a third-party tap once before installing from it. Prebuilt binaries for macOS, Linux and Windows are on the [releases page](https://github.com/plannotator/plannotator-tui/releases). +On x86_64 Linux, choose `plannotator-tui-x86_64-unknown-linux-musl` if the GNU +binary reports a missing `GLIBC_*` version, or if you use Alpine. This static +musl build does not require system glibc or a local Rust toolchain. Existing +GNU/Linux binaries remain available. Verify your download against `SHA256SUMS` +from the same release, make it executable, and put it on your `PATH` as +`plannotator-tui`. + ## Use ```sh diff --git a/scripts/smoke-musl.sh b/scripts/smoke-musl.sh new file mode 100644 index 0000000..607cdee --- /dev/null +++ b/scripts/smoke-musl.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# Run against a musl release binary on the host or inside a minimal Linux container. +# No Rust toolchain, compiler, or interactive terminal is required. +set -eu +binary="${1:?usage: sh scripts/smoke-musl.sh }" +case "$binary" in +/*) ;; +*) binary="$PWD/$binary" ;; +esac +version="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1)" +test "$("$binary" --version)" = "plannotator-tui $version" + +tmp="$(mktemp -d)" +trap 'rm -rf "$tmp"' EXIT +export PLANNOTATOR_DATA_DIR="$tmp/data" +export PLANNOTATOR_FEEDBACK_HISTORY=0 +document="$tmp/smoke.md" +printf '# Musl compatibility test\n\nThis binary runs without glibc.\n' >"$document" + +test "$("$binary" --export "$document")" = "No annotations." +"$binary" --annotate "$document" 'without glibc' 'Musl smoke test feedback' +"$binary" --export "$document" >"$tmp/feedback.md" +grep -Fq '# Annotations on smoke.md' "$tmp/feedback.md" +grep -Fq 'Comment on: "without glibc"' "$tmp/feedback.md" +grep -Fq '> Musl smoke test feedback' "$tmp/feedback.md" +"$binary" --snapshot "$document" 80 24 0 >"$tmp/snapshot.txt" +grep -Fq 'Musl compatibility test' "$tmp/snapshot.txt" +echo "musl smoke test passed" From 686e9206c4cc08c87a95c1adb17df653f6e1fc6a Mon Sep 17 00:00:00 2001 From: Michael Ramos Date: Fri, 23 Oct 2026 14:15:56 -0400 Subject: [PATCH 2/2] ci: build and smoke-test the musl binary on every push --- .github/workflows/ci.yml | 20 ++++++++++++++++++-- .github/workflows/release.yml | 6 +----- scripts/smoke-musl-containers.sh | 10 ++++++++++ 3 files changed, 29 insertions(+), 7 deletions(-) create mode 100755 scripts/smoke-musl-containers.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bca9b32..599fc77 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,6 @@ -# Pull-request gate: the full suite on Ubuntu and Windows. Both jobs run in parallel and are -# cancelled when the PR is updated. +# Pull-request gate: the full suite on Ubuntu and Windows, plus the static musl release binary +# smoke-tested on old glibc and Alpine. The jobs run in parallel and are cancelled when the PR +# is updated. name: ci on: @@ -32,6 +33,21 @@ jobs: - run: cmp target/release/plannotator-tui herdr/bin/plannotator-tui.exe - run: ./herdr/bin/plannotator-tui.exe --version + # Same build and smoke test as the musl entry in release.yml, so a break shows up here + # rather than on release day. + musl: + runs-on: ubuntu-22.04 + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + targets: x86_64-unknown-linux-musl + - uses: Swatinem/rust-cache@v2 + - run: sudo apt-get update && sudo apt-get install -y musl-tools + - run: cargo build --release -p plannotator-tui --target x86_64-unknown-linux-musl + - run: sh scripts/smoke-musl-containers.sh target/x86_64-unknown-linux-musl/release/plannotator-tui + check-windows: runs-on: windows-latest timeout-minutes: 30 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 518a3bf..741806f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,11 +48,7 @@ jobs: - run: cargo build --release -p plannotator-tui --target ${{ matrix.target }} - name: smoke-test musl on older glibc and Alpine if: matrix.target == 'x86_64-unknown-linux-musl' - run: | - for image in debian:bullseye-slim alpine:3.24; do - docker run --rm -v "$PWD:/work:ro" -w /work "$image" \ - sh scripts/smoke-musl.sh "target/${{ matrix.target }}/release/plannotator-tui" - done + run: sh scripts/smoke-musl-containers.sh "target/${{ matrix.target }}/release/plannotator-tui" - name: name the asset by target shell: bash run: | diff --git a/scripts/smoke-musl-containers.sh b/scripts/smoke-musl-containers.sh new file mode 100755 index 0000000..d11e8f1 --- /dev/null +++ b/scripts/smoke-musl-containers.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# Run scripts/smoke-musl.sh against a musl binary in minimal containers: Debian Bullseye +# (glibc 2.31, older than the GNU binary needs) and Alpine (no glibc). Shared by the ci and +# release workflows so both test the same images. The binary path is relative to the repo root. +set -eu +binary="${1:?usage: sh scripts/smoke-musl-containers.sh }" +for image in debian:bullseye-slim alpine:3.24; do + echo "== $image" + docker run --rm -v "$PWD:/work:ro" -w /work "$image" sh scripts/smoke-musl.sh "$binary" +done