From 8b2a5f4a60cc1229ef7e09be0be21c89adcf64d2 Mon Sep 17 00:00:00 2001 From: Zilvinas Kucinskas Date: Thu, 19 Feb 2026 01:04:03 +0200 Subject: [PATCH 1/3] fix(release): build arm64-darwin gem on native macOS runner BoringSSL cannot be cross-compiled for Darwin from Linux because CMake picks up /usr/bin/ld (ELF linker) instead of the osxcross Mach-O linker inside rb-sys-dock containers. Switch arm64-darwin to a native macos-latest runner that compiles natively, avoiding the cross-compilation issue entirely. Add macOS smoke tests to verify the gem loads on Ruby 3.3, 3.4, and 4.0. --- .github/workflows/release.yml | 72 +++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 868d9a5..a36d077 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,7 +40,6 @@ jobs: platform: - x86_64-linux - aarch64-linux - - arm64-darwin steps: - uses: actions/checkout@v6 @@ -57,8 +56,51 @@ jobs: path: pkg/*.gem if-no-files-found: error + native-darwin: + name: Build native gem (arm64-darwin) + runs-on: macos-latest + steps: + - uses: actions/checkout@v6 + + - uses: dtolnay/rust-toolchain@stable + + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.4" + bundler-cache: true + + - name: Compile native extension + env: + RB_SYS_CARGO_PROFILE: release + run: bundle exec rake compile + + - name: Build platform gem + run: | + ruby -e ' + require "rubygems" + require "rubygems/package" + require "fileutils" + + spec = Gem::Specification.load("wreq.gemspec") + spec.platform = Gem::Platform.new("arm64-darwin") + spec.extensions = [] + + bundle_path = "lib/wreq_ruby/wreq_ruby.bundle" + spec.files << bundle_path unless spec.files.include?(bundle_path) + + FileUtils.mkdir_p("pkg") + gem_file = Gem::Package.build(spec) + FileUtils.mv(gem_file, "pkg/") + ' + + - uses: actions/upload-artifact@v4 + with: + name: cross-gem-arm64-darwin + path: pkg/*arm64-darwin*.gem + if-no-files-found: error + smoke-test: - name: Smoke test (Ruby ${{ matrix.ruby }}) + name: Smoke test Linux (Ruby ${{ matrix.ruby }}) needs: [cross-compile] runs-on: ubuntu-latest strategy: @@ -81,9 +123,33 @@ jobs: - name: Verify gem loads and prints version run: ruby -rwreq -e "puts Wreq::VERSION" + smoke-test-darwin: + name: Smoke test macOS (Ruby ${{ matrix.ruby }}) + needs: [native-darwin] + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + ruby: ["3.3", "3.4", "4.0"] + steps: + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + + - uses: actions/download-artifact@v4 + with: + name: cross-gem-arm64-darwin + path: pkg/ + + - name: Install native gem + run: gem install pkg/wreq-*-arm64-darwin.gem + + - name: Verify gem loads and prints version + run: ruby -rwreq -e "puts Wreq::VERSION" + release: name: Release - needs: [source-gem, cross-compile, smoke-test] + needs: [source-gem, cross-compile, native-darwin, smoke-test, smoke-test-darwin] runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/v') permissions: From b4ba645ee16c38960520d3df429d572c27995d23 Mon Sep 17 00:00:00 2001 From: Zilvinas Kucinskas Date: Thu, 19 Feb 2026 01:26:47 +0200 Subject: [PATCH 2/3] Build per-Ruby-version binaries for macOS, add cargo cache - Compile separate .bundle for Ruby 3.3, 3.4, and 4.0 on macOS, matching the per-version binary layout rb-sys-dock produces for Linux - Add Swatinem/rust-cache to avoid rebuilding BoringSSL every release - Extract gem packaging into script/build_platform_gem.rb with required_ruby_version upper bound (>= 3.3, < 4.1.dev) - Rename artifact to native-gem-arm64-darwin (not cross-compiled) - Remove stale arm64-darwin from Rakefile cross_platform --- .github/workflows/release.yml | 65 ++++++++++++++++++++++++----------- Rakefile | 2 +- script/build_platform_gem.rb | 33 ++++++++++++++++++ 3 files changed, 79 insertions(+), 21 deletions(-) create mode 100644 script/build_platform_gem.rb diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a36d077..06a7ac0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,6 +56,9 @@ jobs: path: pkg/*.gem if-no-files-found: error + # arm64-darwin cannot use rb-sys-dock: BoringSSL cross-compilation fails because + # CMake picks up /usr/bin/ld (ELF) instead of the osxcross Mach-O linker. + # No upstream project (wreq, rnet, boring) cross-compiles BoringSSL for Darwin. native-darwin: name: Build native gem (arm64-darwin) runs-on: macos-latest @@ -64,38 +67,60 @@ jobs: - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: macos-arm64 + shared-key: release + + # Compile per-Ruby-version binaries (matching rb-sys-dock behavior on Linux). + # BoringSSL is compiled once and cached; only rb-sys/magnus rebuild per version. - uses: ruby/setup-ruby@v1 with: - ruby-version: "3.4" + ruby-version: "3.3" bundler-cache: true - - name: Compile native extension + - name: Compile for Ruby 3.3 env: RB_SYS_CARGO_PROFILE: release - run: bundle exec rake compile + run: | + bundle exec rake compile + mkdir -p lib/wreq_ruby/3.3 + cp lib/wreq_ruby/wreq_ruby.bundle lib/wreq_ruby/3.3/ - - name: Build platform gem + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.4" + bundler-cache: true + + - name: Compile for Ruby 3.4 + env: + RB_SYS_CARGO_PROFILE: release run: | - ruby -e ' - require "rubygems" - require "rubygems/package" - require "fileutils" + cargo clean -p rb-sys -p wreq-ruby --release + bundle exec rake compile + mkdir -p lib/wreq_ruby/3.4 + cp lib/wreq_ruby/wreq_ruby.bundle lib/wreq_ruby/3.4/ - spec = Gem::Specification.load("wreq.gemspec") - spec.platform = Gem::Platform.new("arm64-darwin") - spec.extensions = [] + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "4.0" + bundler-cache: true - bundle_path = "lib/wreq_ruby/wreq_ruby.bundle" - spec.files << bundle_path unless spec.files.include?(bundle_path) + - name: Compile for Ruby 4.0 + env: + RB_SYS_CARGO_PROFILE: release + run: | + cargo clean -p rb-sys -p wreq-ruby --release + bundle exec rake compile + mkdir -p lib/wreq_ruby/4.0 + cp lib/wreq_ruby/wreq_ruby.bundle lib/wreq_ruby/4.0/ - FileUtils.mkdir_p("pkg") - gem_file = Gem::Package.build(spec) - FileUtils.mv(gem_file, "pkg/") - ' + - name: Build platform gem + run: ruby script/build_platform_gem.rb arm64-darwin - uses: actions/upload-artifact@v4 with: - name: cross-gem-arm64-darwin + name: native-gem-arm64-darwin path: pkg/*arm64-darwin*.gem if-no-files-found: error @@ -138,7 +163,7 @@ jobs: - uses: actions/download-artifact@v4 with: - name: cross-gem-arm64-darwin + name: native-gem-arm64-darwin path: pkg/ - name: Install native gem @@ -168,7 +193,7 @@ jobs: - uses: actions/download-artifact@v4 with: path: pkg/ - pattern: "{cross-gem-*,source-gem}" + pattern: "{cross-gem-*,native-gem-*,source-gem}" merge-multiple: true - name: List gems diff --git a/Rakefile b/Rakefile index 8477cb2..e4e61eb 100644 --- a/Rakefile +++ b/Rakefile @@ -18,10 +18,10 @@ RbSys::ExtensionTask.new(CRATE_PACKAGE_NAME, GEMSPEC) do |ext| ext.ext_dir = "." ext.lib_dir = "lib/wreq_ruby" ext.cross_compile = true + # arm64-darwin is built natively on macOS (see .github/workflows/release.yml) ext.cross_platform = %w[ x86_64-linux aarch64-linux - arm64-darwin ] # Override Ruby version for native gems (keep in sync with wreq.gemspec) diff --git a/script/build_platform_gem.rb b/script/build_platform_gem.rb new file mode 100644 index 0000000..0abc6a7 --- /dev/null +++ b/script/build_platform_gem.rb @@ -0,0 +1,33 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Build a platform-specific gem with pre-compiled native extensions. +# +# Usage: ruby script/build_platform_gem.rb PLATFORM +# Example: ruby script/build_platform_gem.rb arm64-darwin +# +# Expects compiled .bundle/.so files in version-specific directories: +# lib/wreq_ruby/3.3/wreq_ruby.bundle +# lib/wreq_ruby/3.4/wreq_ruby.bundle +# lib/wreq_ruby/4.0/wreq_ruby.bundle + +require "rubygems/package" +require "fileutils" + +platform = ARGV.fetch(0) { abort "Usage: #{$0} PLATFORM" } + +spec = Gem::Specification.load("wreq.gemspec") +spec.platform = Gem::Platform.new(platform) +spec.extensions = [] +spec.required_ruby_version = Gem::Requirement.new(">= 3.3", "< 4.1.dev") + +# Add version-specific compiled extensions +Dir.glob("lib/wreq_ruby/[0-9]*/*.{bundle,so}").each do |path| + spec.files << path unless spec.files.include?(path) +end + +FileUtils.mkdir_p("pkg") +gem_file = Gem::Package.build(spec) +FileUtils.mv(gem_file, "pkg/") + +puts "Built: pkg/#{File.basename(gem_file)}" From 49f8f699d00115dfa290dedee6bf33aa880c8634 Mon Sep 17 00:00:00 2001 From: Zilvinas Kucinskas Date: Thu, 19 Feb 2026 01:37:56 +0200 Subject: [PATCH 3/3] Review fixes: expand cargo clean, add binary guard - Include magnus and serde_magnus in cargo clean (both have version-conditional #[cfg(ruby_gte_*)] code that must recompile) - Add cargo clean before first compilation too (handles stale cache from previous release run) - Abort if no compiled binaries found before packaging gem - Simplify glob to idiomatic spec.files += --- .github/workflows/release.yml | 5 +++-- script/build_platform_gem.rb | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 06a7ac0..ec2760b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -83,6 +83,7 @@ jobs: env: RB_SYS_CARGO_PROFILE: release run: | + cargo clean -p rb-sys -p magnus -p serde_magnus -p wreq-ruby --release 2>/dev/null || true bundle exec rake compile mkdir -p lib/wreq_ruby/3.3 cp lib/wreq_ruby/wreq_ruby.bundle lib/wreq_ruby/3.3/ @@ -96,7 +97,7 @@ jobs: env: RB_SYS_CARGO_PROFILE: release run: | - cargo clean -p rb-sys -p wreq-ruby --release + cargo clean -p rb-sys -p magnus -p serde_magnus -p wreq-ruby --release bundle exec rake compile mkdir -p lib/wreq_ruby/3.4 cp lib/wreq_ruby/wreq_ruby.bundle lib/wreq_ruby/3.4/ @@ -110,7 +111,7 @@ jobs: env: RB_SYS_CARGO_PROFILE: release run: | - cargo clean -p rb-sys -p wreq-ruby --release + cargo clean -p rb-sys -p magnus -p serde_magnus -p wreq-ruby --release bundle exec rake compile mkdir -p lib/wreq_ruby/4.0 cp lib/wreq_ruby/wreq_ruby.bundle lib/wreq_ruby/4.0/ diff --git a/script/build_platform_gem.rb b/script/build_platform_gem.rb index 0abc6a7..579d28f 100644 --- a/script/build_platform_gem.rb +++ b/script/build_platform_gem.rb @@ -19,12 +19,13 @@ spec = Gem::Specification.load("wreq.gemspec") spec.platform = Gem::Platform.new(platform) spec.extensions = [] +# Keep in sync with Rakefile cross_compiling block spec.required_ruby_version = Gem::Requirement.new(">= 3.3", "< 4.1.dev") # Add version-specific compiled extensions -Dir.glob("lib/wreq_ruby/[0-9]*/*.{bundle,so}").each do |path| - spec.files << path unless spec.files.include?(path) -end +binaries = Dir.glob("lib/wreq_ruby/[0-9]*/*.{bundle,so}") +abort "No compiled binaries found in lib/wreq_ruby/*/. Did compilation succeed?" if binaries.empty? +spec.files += binaries FileUtils.mkdir_p("pkg") gem_file = Gem::Package.build(spec)