From 84db1fe63f622723e54a35153fa1f596cab15d89 Mon Sep 17 00:00:00 2001 From: zaidoon Date: Sat, 2 May 2026 01:59:47 -0400 Subject: [PATCH] ci: add Linux ARM and macOS to GitHub Actions Add an OS dimension to the matrix so the full toolchain suite (nightly, MSRV, stable) runs on Linux x86, Linux ARM64, and macOS. Dependency install is gated on runner.os; the macOS leg taps openresty/brew, drops the unavailable GeoIP nginx module, and adds a 127.0.0.2 lo0 alias so loopback-binding tests work. The mock origin in pingora-proxy is invoked with an explicit -c nginx.conf path because openresty's macOS layout differs from Linux. The flaky pingora-limits rate test (test_observe_rate_custom_proportional) relies on tight epsilons that are too noisy on the macOS GitHub runner, so it is gated to target_os = "linux". --- .github/workflows/build.yml | 32 +++++++++++++++++++++--- pingora-limits/src/rate.rs | 6 +++++ pingora-proxy/tests/utils/mock_origin.rs | 3 ++- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index adf2b014..47f4fa80 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,12 +4,14 @@ name: build jobs: pingora: + name: ${{ matrix.os }} (${{ matrix.toolchain }}) strategy: fail-fast: false matrix: + os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest] # nightly, msrv, and latest stable toolchain: [nightly, 1.84.0, 1.91.1] - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} # Only run on "pull_request" event for external PRs. This is to avoid # duplicate builds for PRs created from internal branches. if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository @@ -19,16 +21,40 @@ jobs: with: submodules: "recursive" - - name: Install build dependencies + - name: Install build dependencies (Linux) + if: runner.os == 'Linux' run: | sudo apt update sudo apt install -y cmake libclang-dev wget gnupg ca-certificates lsb-release --no-install-recommends # openresty is used for convenience in tests as a server. + # arm64 packages are under a separate repo URL. wget -O - https://openresty.org/package/pubkey.gpg | sudo gpg --dearmor -o /usr/share/keyrings/openresty.gpg - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/openresty.gpg] http://openresty.org/package/ubuntu $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/openresty.list > /dev/null + ARCH=$(dpkg --print-architecture) + if [ "$ARCH" = "arm64" ]; then + REPO_URL="http://openresty.org/package/arm64/ubuntu" + else + REPO_URL="http://openresty.org/package/ubuntu" + fi + echo "deb [arch=$ARCH signed-by=/usr/share/keyrings/openresty.gpg] $REPO_URL $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/openresty.list > /dev/null sudo apt update sudo apt install -y openresty --no-install-recommends + - name: Install build dependencies (macOS) + if: runner.os == 'macOS' + run: | + # macOS only assigns 127.0.0.1 to loopback by default (Linux + # assigns the entire 127.0.0.0/8). Tests bind to 127.0.0.2. + sudo ifconfig lo0 alias 127.0.0.2 + brew tap openresty/brew + # The openresty formula tries to build with the GeoIP nginx module + # by default, but the legacy libGeoIP library is no longer available + # in Homebrew and the --without-geoip option is no longer supported. + # Patch the formula to remove geoip references before building. + sed -i '' '/geoip/Id' "$(brew --prefix)/Library/Taps/openresty/homebrew-brew/Formula/openresty.rb" + brew install --build-from-source openresty/brew/openresty + echo "$(brew --prefix openresty)/bin" >> $GITHUB_PATH + echo "$(brew --prefix openresty)/nginx/sbin" >> $GITHUB_PATH + - name: Install toolchain uses: dtolnay/rust-toolchain@master with: diff --git a/pingora-limits/src/rate.rs b/pingora-limits/src/rate.rs index bd1268b3..37109aba 100644 --- a/pingora-limits/src/rate.rs +++ b/pingora-limits/src/rate.rs @@ -222,6 +222,7 @@ impl Rate { #[cfg(test)] mod tests { + #[cfg(target_os = "linux")] use float_cmp::assert_approx_eq; use super::*; @@ -259,6 +260,7 @@ mod tests { /// tests are doing a lot of literal sleeping, so the measured results /// can't be accurate or consistent. This function does an assert with a /// generous tolerance + #[cfg(target_os = "linux")] fn assert_eq_ish(left: f64, right: f64) { assert_approx_eq!(f64, left, right, epsilon = 0.15) } @@ -296,6 +298,10 @@ mod tests { assert_eq!(r.rate_with(&key, rate_90_10_fn), 0f64); } + // The macOS GitHub Actions runner has noisier timing than Linux, which + // makes the tight epsilons used by `assert_eq_ish` flake. Gate this test + // to Linux where the timing is reliable. + #[cfg(target_os = "linux")] #[test] fn test_observe_rate_custom_proportional() { let r = Rate::new(Duration::from_secs(1)); diff --git a/pingora-proxy/tests/utils/mock_origin.rs b/pingora-proxy/tests/utils/mock_origin.rs index fa5a327f..4b2ef95a 100644 --- a/pingora-proxy/tests/utils/mock_origin.rs +++ b/pingora-proxy/tests/utils/mock_origin.rs @@ -54,8 +54,9 @@ fn init() -> bool { .unwrap() .wait(); let _origin = thread::spawn(|| { + let prefix = format!("{}/origin", super::conf_dir()); process::Command::new("openresty") - .args(["-p", &format!("{}/origin", super::conf_dir())]) + .args(["-p", &prefix, "-c", &format!("{}/conf/nginx.conf", prefix)]) .output() .unwrap(); });