diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 372359c6..1a846e9b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5ca11109..be6aebc4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 45987cf5..a5186b0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/Cargo.lock b/Cargo.lock index 021aff92..7d4b350c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", ] diff --git a/bindings/python/Cargo.toml b/bindings/python/Cargo.toml index be6dcea7..4336c317 100644 --- a/bindings/python/Cargo.toml +++ b/bindings/python/Cargo.toml @@ -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 diff --git a/crates/wickra-data/Cargo.toml b/crates/wickra-data/Cargo.toml index bea33f7a..5c9bbe5e 100644 --- a/crates/wickra-data/Cargo.toml +++ b/crates/wickra-data/Cargo.toml @@ -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 }