fix: build Linux Python wheels with vendored OpenSSL (manylinux + musllinux) (#327)
Fixes the Linux Python wheel build that broke the `0.9.3` release (and would have broken `0.9.4`), and adds a CI guard so it cannot regress silently. ### Root cause The `live-binance` data layer links `native-tls` -> `openssl-sys`, which needs OpenSSL at build time. Neither wheel container provides it: - **manylinux** ships no OpenSSL headers, and - **musllinux** cross-compiles against a musl sysroot that has no OpenSSL at all, so installing a host package (`yum`/`apk`) cannot reach the cross target. The 3-OS Python CI jobs build natively on the runner, which already has system OpenSSL, so CI stayed green while the release container build failed. ### Fix - New opt-in **`vendored-tls`** feature on `wickra-data` and the Python binding: enables `native-tls/vendored`, compiling OpenSSL from source and linking it statically. No system OpenSSL needed on either libc. No-op on macOS/Windows (Security.framework / SChannel — `openssl-sys` is never in the graph there). - `release.yml` builds the Linux wheels with `--features vendored-tls` (replaces the manylinux-only `before-script-linux` header install, which could not fix the musllinux cross build). - CI gains a **`manylinux` + `musllinux` container build-smoke** matrix job, so both container builds run on every PR. This PR's own CI is the proof the fix works before any release re-attempt. ### Notes - No version bump: `0.9.4` published nowhere (the release run was cancelled before any publish job ran), so this lands on `0.9.4` and the tag is re-pointed at the fixed commit. - Adds checks to `ci.yml` (the smoke job is now a 2-entry matrix).
This commit is contained in:
@@ -1031,6 +1031,47 @@ jobs:
|
||||
mvn -B -q -f examples/java exec:exec -Dexec.mainClass="org.wickra.examples.$cls"
|
||||
done
|
||||
|
||||
# Build a Python wheel inside both the manylinux and the musllinux container,
|
||||
# mirroring the Linux wheel build in release.yml. The 3-OS Python jobs build
|
||||
# natively on the runner, which already ships system OpenSSL, so they cannot
|
||||
# catch a build-time gap that only exists inside the slim release containers —
|
||||
# exactly what broke the 0.9.3 Linux wheels (the live-binance data layer links
|
||||
# native-tls -> openssl-sys, and the containers provide no OpenSSL: manylinux
|
||||
# lacks the headers, musllinux cross-compiles against a musl sysroot without
|
||||
# OpenSSL at all). The wheels are built with the `vendored-tls` feature, which
|
||||
# statically compiles OpenSSL from source. This job exercises both container
|
||||
# builds on every PR, so the same class of breakage now fails CI, not release.
|
||||
python-wheel-container-smoke:
|
||||
name: Python wheel (${{ matrix.manylinux }} smoke)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30 # backstop: vendored OpenSSL adds a from-source compile
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
manylinux: [auto, musllinux_1_2]
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Sync root README into bindings/python so the build matches release
|
||||
run: cp README.md bindings/python/README.md
|
||||
|
||||
- uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0
|
||||
with:
|
||||
working-directory: bindings/python
|
||||
target: x86_64
|
||||
# Keep in sync with release.yml: vendored OpenSSL for the Linux wheels.
|
||||
args: --release --out dist --features vendored-tls
|
||||
manylinux: ${{ matrix.manylinux }}
|
||||
# Building OpenSSL from source needs Perl modules (IPC::Cmd,
|
||||
# Time::Piece, ...) the minimal manylinux (CentOS 7) image lacks.
|
||||
# perl-core pulls the full distribution; the explicit names document
|
||||
# the ones OpenSSL's Configure has required. The musllinux cross image
|
||||
# ships a complete Perl and has no yum, so this is a no-op there.
|
||||
before-script-linux: |
|
||||
if command -v yum >/dev/null 2>&1; then yum install -y perl-core perl-IPC-Cmd perl-Data-Dumper perl-Time-Piece; fi
|
||||
|
||||
# The cross-library benchmark has moved to a dedicated scheduled workflow
|
||||
# (.github/workflows/bench.yml) — see audit finding R10. It runs nightly
|
||||
# at 03:00 UTC and on-demand via `workflow_dispatch`, and is no longer on
|
||||
|
||||
@@ -184,16 +184,22 @@ jobs:
|
||||
with:
|
||||
working-directory: bindings/python
|
||||
target: ${{ matrix.target }}
|
||||
args: --release --strip --out dist
|
||||
manylinux: ${{ matrix.manylinux }}
|
||||
# The live-binance data layer links native-tls -> openssl-sys, which
|
||||
# needs the system OpenSSL headers at build time. The manylinux and
|
||||
# musllinux build containers do not ship them, so install them inside
|
||||
# the container before maturin compiles the wheel (no-op on the native
|
||||
# macOS/Windows runners, where this hook does not run).
|
||||
# needs OpenSSL at build time. The manylinux containers lack the headers
|
||||
# and the musllinux build cross-compiles against a musl sysroot with no
|
||||
# OpenSSL at all, so build the Linux wheels with vendored OpenSSL
|
||||
# (compiled from source, linked statically). No-op on the native
|
||||
# macOS/Windows runners, where native-tls never pulls openssl-sys. The
|
||||
# CI `python-wheel-container-smoke` job exercises both containers on PRs.
|
||||
args: --release --strip --out dist --features vendored-tls
|
||||
manylinux: ${{ matrix.manylinux }}
|
||||
# Building OpenSSL from source needs Perl modules (IPC::Cmd,
|
||||
# Time::Piece, ...) the minimal manylinux (CentOS 7) image lacks.
|
||||
# perl-core pulls the full distribution; the explicit names document
|
||||
# the ones OpenSSL's Configure has required. The musllinux cross image
|
||||
# ships a complete Perl and has no yum, so this is a no-op there.
|
||||
before-script-linux: |
|
||||
if command -v yum >/dev/null 2>&1; then yum install -y openssl-devel; fi
|
||||
if command -v apk >/dev/null 2>&1; then apk add --no-cache openssl-dev pkgconfig; fi
|
||||
if command -v yum >/dev/null 2>&1; then yum install -y perl-core perl-IPC-Cmd perl-Data-Dumper perl-Time-Piece; fi
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
# Include manylinux in the name so the glibc and musl x86_64/aarch64
|
||||
|
||||
+19
-9
@@ -9,18 +9,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [0.9.4] - 2026-06-17
|
||||
|
||||
Packaging fix for the `0.9.3` data layer. No library code changed; `0.9.4` is
|
||||
identical to `0.9.3` on every platform that already published. The Linux Python
|
||||
wheels are released for the first time here, because `0.9.3` could not build them.
|
||||
Packaging fix for the `0.9.3` data layer. The library is identical to `0.9.3` on
|
||||
every platform that already published; the only additions are an opt-in
|
||||
`vendored-tls` build feature and the Linux Python wheels, which `0.9.3` could not
|
||||
build.
|
||||
|
||||
### Fixed
|
||||
- **Linux Python wheels (`manylinux` / `musllinux`) now build.** The `live-binance`
|
||||
data layer links `native-tls`, which pulls in `openssl-sys`, and the wheel build
|
||||
containers do not ship the system OpenSSL headers. The release workflow now
|
||||
installs them inside the container before compiling (`openssl-devel` on
|
||||
`manylinux`, `openssl-dev` on `musllinux`). The native macOS and Windows wheels
|
||||
were unaffected. As a result `0.9.3` shipped to crates.io, Maven Central, NuGet,
|
||||
and npm but not to PyPI; PyPI publishes starting with `0.9.4`.
|
||||
data layer links `native-tls` -> `openssl-sys`, which needs OpenSSL at build
|
||||
time. The `manylinux` wheel containers ship no OpenSSL headers and the
|
||||
`musllinux` build cross-compiles against a musl sysroot that has no OpenSSL at
|
||||
all, so the wheels failed to compile. The Linux wheels are now built with a new
|
||||
opt-in `vendored-tls` feature that compiles OpenSSL from source and links it
|
||||
statically (no system OpenSSL required, on either libc). The native macOS and
|
||||
Windows wheels were unaffected (Security.framework / SChannel). As a result
|
||||
`0.9.3` shipped to crates.io, Maven Central, NuGet, and npm but not to PyPI;
|
||||
PyPI publishes starting with `0.9.4`.
|
||||
|
||||
### Added
|
||||
- **`vendored-tls` feature** on `wickra-data` (and the Python binding): builds the
|
||||
`live-binance` TLS stack against a statically compiled OpenSSL. Off by default;
|
||||
used by the release wheels and exercised on every PR by a `manylinux` /
|
||||
`musllinux` container build-smoke CI job.
|
||||
|
||||
|
||||
## [0.9.3] - 2026-06-17
|
||||
|
||||
Generated
+10
@@ -907,6 +907,15 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "300.5.4+3.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.116"
|
||||
@@ -915,6 +924,7 @@ checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"openssl-src",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
@@ -17,6 +17,13 @@ publish = false
|
||||
name = "_wickra"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[features]
|
||||
# Build the bundled data layer against a statically compiled OpenSSL. The
|
||||
# release workflow (and the manylinux/musllinux CI smoke job) enable this so the
|
||||
# Linux wheels do not depend on system OpenSSL at build time. See the
|
||||
# `vendored-tls` feature in wickra-data for the rationale.
|
||||
vendored-tls = ["wickra-data/vendored-tls"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
|
||||
@@ -54,6 +54,15 @@ default = []
|
||||
# want. `live-binance` covers both the live WebSocket feed and the historical
|
||||
# REST kline fetcher.
|
||||
live-binance = ["dep:tokio", "dep:tokio-tungstenite", "dep:futures-util", "dep:url", "dep:ureq", "dep:native-tls"]
|
||||
# `live-binance` with a statically built OpenSSL instead of the system one. The
|
||||
# native-tls stack (tokio-tungstenite + ureq, unified on the same `native-tls`
|
||||
# crate) links `openssl-sys`, which needs OpenSSL at build time. The manylinux
|
||||
# and musllinux wheel containers do not provide it — manylinux lacks the headers
|
||||
# and the musllinux build cross-compiles against a musl sysroot that has no
|
||||
# OpenSSL at all — so the Linux wheels are built with this feature, which
|
||||
# compiles OpenSSL from source and links it statically. No-op on macOS/Windows,
|
||||
# where native-tls uses Security.framework / SChannel and never pulls openssl-sys.
|
||||
vendored-tls = ["live-binance", "native-tls/vendored"]
|
||||
|
||||
[dev-dependencies]
|
||||
approx = { workspace = true }
|
||||
|
||||
Reference in New Issue
Block a user