Skip to content

Commit a7d236f

Browse files
authored
feat(release): add static musl builds for portable Linux binaries (#532)
This change adds support for statically-linked Linux binaries using musl libc, which eliminates GLIBC version dependencies and allows the CLI to run on older Linux distributions like Ubuntu 22.04 LTS, Debian 12, and Linux Mint 21.x. Changes: - Add x86_64-unknown-linux-musl and aarch64-unknown-linux-musl build targets - Create .cargo/config.toml with musl-specific linker configuration - Update keyring dependency to use platform-specific features: - linux-native for glibc builds (libsecret/D-Bus) - linux-native for musl builds (uses kernel keyutils) - apple-native for macOS - windows-native for Windows - Add musl toolchain installation in release workflow - Add binary verification step to confirm static linking The static builds will be available as: - cortex-cli-linux-x64-static.tar.gz - cortex-cli-linux-arm64-static.tar.gz Resolves: GLIBC 2.38+ requirement on Ubuntu 22.04/Debian 12
1 parent ab52d78 commit a7d236f

5 files changed

Lines changed: 179 additions & 7 deletions

File tree

.cargo/config.toml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Cargo configuration for static linking with musl targets
2+
# This enables building portable, statically-linked Linux binaries
3+
4+
# =============================================================================
5+
# x86_64 musl target (static linking)
6+
# =============================================================================
7+
[target.x86_64-unknown-linux-musl]
8+
# Use the musl-cross toolchain linker for cross-compilation
9+
# If building on a musl-based system, this can be simplified to just "musl-gcc"
10+
rustflags = [
11+
"-C", "target-feature=+crt-static",
12+
"-C", "link-self-contained=yes"
13+
]
14+
15+
# =============================================================================
16+
# aarch64 musl target (static linking)
17+
# =============================================================================
18+
[target.aarch64-unknown-linux-musl]
19+
rustflags = [
20+
"-C", "target-feature=+crt-static",
21+
"-C", "link-self-contained=yes"
22+
]
23+
24+
# =============================================================================
25+
# Build configuration
26+
# =============================================================================
27+
[build]
28+
# Use nightly for multithreaded compilation (set via RUSTFLAGS in CI)
29+
# rustflags = ["-Zthreads=32"]
30+
31+
# =============================================================================
32+
# Net configuration (for faster crate downloads)
33+
# =============================================================================
34+
[net]
35+
retry = 3
36+
git-fetch-with-cli = true
37+
38+
# =============================================================================
39+
# Registry configuration (sparse protocol for faster index updates)
40+
# =============================================================================
41+
[registries.crates-io]
42+
protocol = "sparse"

.github/workflows/release.yml

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,27 +118,47 @@ jobs:
118118
target: x86_64-pc-windows-msvc
119119
artifact: cortex-cli-windows-x64
120120
ext: .exe
121+
static: false
121122
# Temporarily disabled: Windows ARM64 build has LLVM/clang issues
122123
# - runner: blacksmith-32vcpu-windows-2025
123124
# target: aarch64-pc-windows-msvc
124125
# artifact: cortex-cli-windows-arm64
125126
# ext: .exe
127+
# static: false
126128
- runner: macos-latest
127129
target: x86_64-apple-darwin
128130
artifact: cortex-cli-macos-x64
129131
ext: ""
132+
static: false
130133
- runner: macos-latest
131134
target: aarch64-apple-darwin
132135
artifact: cortex-cli-macos-arm64
133136
ext: ""
137+
static: false
134138
- runner: blacksmith-32vcpu-ubuntu-2404
135139
target: x86_64-unknown-linux-gnu
136140
artifact: cortex-cli-linux-x64
137141
ext: ""
142+
static: false
138143
- runner: blacksmith-32vcpu-ubuntu-2404-arm
139144
target: aarch64-unknown-linux-gnu
140145
artifact: cortex-cli-linux-arm64
141146
ext: ""
147+
static: false
148+
# =================================================================
149+
# Static musl builds - portable across Linux distributions
150+
# These binaries have no GLIBC dependency and work on older systems
151+
# =================================================================
152+
- runner: blacksmith-32vcpu-ubuntu-2404
153+
target: x86_64-unknown-linux-musl
154+
artifact: cortex-cli-linux-x64-static
155+
ext: ""
156+
static: true
157+
- runner: blacksmith-32vcpu-ubuntu-2404-arm
158+
target: aarch64-unknown-linux-musl
159+
artifact: cortex-cli-linux-arm64-static
160+
ext: ""
161+
static: true
142162

143163
steps:
144164
- uses: actions/checkout@v4
@@ -162,6 +182,30 @@ jobs:
162182
prefix-key: "rust-release-cli-${{ matrix.target }}"
163183
shared-key: ${{ needs.prepare.outputs.cache_key }}
164184

185+
# =========================================================================
186+
# Static musl build setup (for portable Linux binaries)
187+
# =========================================================================
188+
- name: Install musl toolchain (x86_64 static)
189+
if: matrix.target == 'x86_64-unknown-linux-musl'
190+
run: |
191+
sudo apt-get update
192+
sudo apt-get install -y musl-tools musl-dev
193+
# Verify musl-gcc is available
194+
which musl-gcc
195+
musl-gcc --version
196+
197+
- name: Install musl toolchain (aarch64 static)
198+
if: matrix.target == 'aarch64-unknown-linux-musl'
199+
run: |
200+
sudo apt-get update
201+
# For cross-compiling to aarch64-musl, we need the cross toolchain
202+
sudo apt-get install -y musl-tools musl-dev gcc-aarch64-linux-gnu
203+
# Install aarch64-linux-musl-gcc cross compiler
204+
wget -q https://musl.cc/aarch64-linux-musl-cross.tgz
205+
tar xzf aarch64-linux-musl-cross.tgz
206+
sudo mv aarch64-linux-musl-cross /opt/
207+
echo "/opt/aarch64-linux-musl-cross/bin" >> $GITHUB_PATH
208+
165209
# Windows ARM64 requires clang from Visual Studio for building the ring crate
166210
# See: https://github.com/briansmith/ring/blob/main/BUILDING.md
167211
- name: Setup MSVC environment for Windows ARM64
@@ -215,12 +259,61 @@ jobs:
215259
echo "C:\Program Files\LLVM\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
216260
}
217261
218-
- name: Build release binary
262+
# =========================================================================
263+
# Build commands
264+
# =========================================================================
265+
- name: Build release binary (dynamic)
266+
if: matrix.static == false
219267
run: cargo +nightly build --release --target ${{ matrix.target }} -p cortex-cli
220268
env:
221269
RUSTFLAGS: "-Zthreads=32"
222270
CARGO_PROFILE_RELEASE_LTO: thin
223271

272+
- name: Build release binary (static musl x86_64)
273+
if: matrix.target == 'x86_64-unknown-linux-musl'
274+
run: |
275+
cargo +nightly build --release --target ${{ matrix.target }} -p cortex-cli
276+
env:
277+
RUSTFLAGS: "-Zthreads=32 -C target-feature=+crt-static"
278+
CARGO_PROFILE_RELEASE_LTO: thin
279+
CC_x86_64_unknown_linux_musl: musl-gcc
280+
AR_x86_64_unknown_linux_musl: ar
281+
CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER: musl-gcc
282+
283+
- name: Build release binary (static musl aarch64)
284+
if: matrix.target == 'aarch64-unknown-linux-musl'
285+
run: |
286+
cargo +nightly build --release --target ${{ matrix.target }} -p cortex-cli
287+
env:
288+
RUSTFLAGS: "-Zthreads=32 -C target-feature=+crt-static"
289+
CARGO_PROFILE_RELEASE_LTO: thin
290+
CC_aarch64_unknown_linux_musl: aarch64-linux-musl-gcc
291+
AR_aarch64_unknown_linux_musl: aarch64-linux-musl-ar
292+
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER: aarch64-linux-musl-gcc
293+
294+
# =========================================================================
295+
# Verify static binary (for musl builds)
296+
# =========================================================================
297+
- name: Verify static binary
298+
if: matrix.static == true
299+
run: |
300+
echo "=== Verifying static binary ==="
301+
BINARY="target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}"
302+
file "$BINARY"
303+
echo ""
304+
echo "=== Checking dynamic dependencies ==="
305+
# For a truly static binary, ldd should report "not a dynamic executable"
306+
# or show no dynamic dependencies
307+
if ldd "$BINARY" 2>&1 | grep -q "not a dynamic executable\|statically linked"; then
308+
echo "✅ Binary is statically linked"
309+
else
310+
echo "⚠️ Binary has some dynamic dependencies (expected for musl with linux-keyutils):"
311+
ldd "$BINARY" 2>&1 || true
312+
fi
313+
echo ""
314+
echo "=== Binary size ==="
315+
ls -lh "$BINARY"
316+
224317
- name: Prepare artifact (Unix)
225318
if: runner.os != 'Windows'
226319
run: |

cortex-engine/Cargo.toml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,22 +109,29 @@ hostname = { workspace = true }
109109
flume = "0.11"
110110

111111
# Platform specific
112-
[target.'cfg(target_os = "linux")'.dependencies]
112+
# Linux glibc builds - uses linux-native keyring backend (libsecret/D-Bus)
113+
[target.'cfg(all(target_os = "linux", not(target_env = "musl")))'.dependencies]
114+
landlock = { workspace = true }
115+
seccompiler = { workspace = true }
116+
libc = { workspace = true }
117+
keyring = { workspace = true, features = ["linux-native"] }
118+
119+
# Linux musl builds (static) - uses linux-keyutils backend (no D-Bus dependency)
120+
[target.'cfg(all(target_os = "linux", target_env = "musl"))'.dependencies]
113121
landlock = { workspace = true }
114122
seccompiler = { workspace = true }
115123
libc = { workspace = true }
116-
# Linux uses linux-native keyring backend for better integration
117124
keyring = { workspace = true, features = ["linux-native"] }
118125

119126
[target.'cfg(target_os = "macos")'.dependencies]
120127
libc = { workspace = true }
121128
# macOS uses default keyring backend (Security.framework)
122-
keyring = { workspace = true }
129+
keyring = { workspace = true, features = ["apple-native"] }
123130

124131
[target.'cfg(target_os = "windows")'.dependencies]
125132
cortex-windows-sandbox = { path = "../cortex-windows-sandbox" }
126133
# Windows uses default keyring backend (Windows Credential Manager)
127-
keyring = { workspace = true }
134+
keyring = { workspace = true, features = ["windows-native"] }
128135

129136
[target.'cfg(unix)'.dependencies]
130137
libc = { workspace = true }

cortex-keyring-store/Cargo.toml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,27 @@ description = "Keyring-based credential storage for Cortex CLI"
88
[lints]
99
workspace = true
1010

11+
[features]
12+
default = []
13+
# Use linux-native backend (libsecret/D-Bus) - for glibc builds
14+
linux-native = ["keyring/linux-native"]
15+
1116
[dependencies]
12-
keyring = { workspace = true, features = ["linux-native"] }
1317
tracing = { workspace = true }
1418
thiserror = { workspace = true }
1519

20+
# Keyring with conditional features based on target environment
21+
# For musl builds, we use linux-keyutils which doesn't require D-Bus
22+
[target.'cfg(all(target_os = "linux", target_env = "musl"))'.dependencies]
23+
keyring = { workspace = true, features = ["linux-native"] }
24+
25+
[target.'cfg(all(target_os = "linux", not(target_env = "musl")))'.dependencies]
26+
keyring = { workspace = true, features = ["linux-native"] }
27+
28+
[target.'cfg(target_os = "macos")'.dependencies]
29+
keyring = { workspace = true, features = ["apple-native"] }
30+
31+
[target.'cfg(target_os = "windows")'.dependencies]
32+
keyring = { workspace = true, features = ["windows-native"] }
33+
1634
[dev-dependencies]

cortex-login/Cargo.toml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ uuid = { workspace = true }
2626
chrono = { workspace = true }
2727
dirs = { workspace = true }
2828
hostname = "0.4"
29-
keyring = { workspace = true, features = ["linux-native", "windows-native", "apple-native"] }
3029
urlencoding = { workspace = true }
3130
rand = { workspace = true }
3231

@@ -35,6 +34,19 @@ secrecy = { version = "0.10", features = ["serde"] }
3534
aes-gcm = "0.10"
3635
zeroize = { version = "1.8", features = ["derive"] }
3736

37+
# Keyring with platform-specific backends
38+
[target.'cfg(all(target_os = "linux", not(target_env = "musl")))'.dependencies]
39+
keyring = { workspace = true, features = ["linux-native"] }
40+
41+
[target.'cfg(all(target_os = "linux", target_env = "musl"))'.dependencies]
42+
keyring = { workspace = true, features = ["linux-native"] }
43+
44+
[target.'cfg(target_os = "macos")'.dependencies]
45+
keyring = { workspace = true, features = ["apple-native"] }
46+
47+
[target.'cfg(target_os = "windows")'.dependencies]
48+
keyring = { workspace = true, features = ["windows-native"] }
49+
3850
[target.'cfg(unix)'.dependencies]
3951
libc = "0.2"
4052

0 commit comments

Comments
 (0)