@smbcloud / cli / commits / 66080e7

Cross-build the SDK gems and make them installable on current macOS

smbcloud-model 0.7.5 and smbcloud-auth 0.7.5 have three problems: - The darwin gems are named arm64-darwin-23 and x86_64-darwin-22 after the runner Ruby that built them, so they only match those macOS releases. Everything newer falls back to the source gem. - The source gem cannot be installed. git ls-files puts .ruby-version in it, which is a symlink to a parent path, and RubyGems refuses those. - The model gem required smbcloud-model "0.4", so "0.7.5" linked the 0.4.x crate. The release script synced auth's crate requirements but not model's. The release workflow now cross-compiles every native gem on Linux with oxidize-rb/actions/cross-gem. That gives generic arm64-darwin and x86_64-darwin platforms and fat gems for each Ruby >= 3.1, and the loaders try the per-Ruby bundle first. Pull requests that touch the gems run the builds without publishing. Both gemspecs require rb_sys >= 0.9.130. Older rb_sys runs install_name_tool -id "" on darwin bundles, which dyld on macOS 15+ rejects. 0.9.130 sets the bundle's file name instead, so auth's extconf.rb rewrite, its build.rs and both Rakefile hooks are removed. The model gem now requires smbcloud-model "0.7". The sync script keeps it at the release's major.minor, and check-release-versions fails if either gem's smbcloud-* requirement drifts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Seto Elkahfi committed Oct 24, 2026 at 12:05 UTC 66080e7c63f8a266b01992bdfedfaefeb3c0e363
16 files changed +118 -131
.github/workflows/release-sdk-gem.yml
+44 -43
@@ -2,8 +2,13 @@ name: SDK Ruby Gem Release
2
3 # Publishes the smbcloud-auth (sdk/gems/auth) and smbcloud-model
4 # (sdk/gems/model) Ruby gems to RubyGems.
5 -# Builds pre-compiled native gems for the supported Unix platforms, plus a
6 -# source gem as a fallback for unsupported environments.
5 +# Native gems are cross-compiled on Linux with rb-sys-dock, so the darwin gems
6 +# get the generic `arm64-darwin` / `x86_64-darwin` platforms (a gem built with a
7 +# runner's Ruby gets e.g. `arm64-darwin-23`, which only matches that macOS
8 +# release). Each native gem is fat: one extension per Ruby minor >= 3.1. A
9 +# source gem covers every other platform and Ruby.
10 +#
11 +# Pull requests that touch the gems build everything but publish nothing.
12 #
13 # Required secrets:
14 # RUBYGEMS_API_KEY — RubyGems.org API key with push access to smbcloud-auth and
@@ -18,6 +23,11 @@ on:
23 ref:
24 description: "Git ref to build from (defaults to the tag)"
25 required: false
26 + pull_request:
27 + paths:
28 + - "sdk/gems/auth/**"
29 + - "sdk/gems/model/**"
30 + - ".github/workflows/release-sdk-gem.yml"
31
32 permissions:
33 contents: read
@@ -28,30 +38,13 @@ env:
38 jobs:
39 # ── Build native gems ──────────────────────────────────────────────────────
40 build-gems:
31 - name: Build ${{ matrix.gem }} gem (${{ matrix.name }})
32 - runs-on: ${{ matrix.runner }}
41 + name: Build ${{ matrix.gem }} gem (${{ matrix.platform }})
42 + runs-on: ubuntu-latest
43 strategy:
44 fail-fast: false
45 matrix:
46 gem: [auth, model]
37 - name: [linux-x64, linux-arm64, darwin-arm64, darwin-x64]
38 - include:
39 - - { name: linux-x64, runner: ubuntu-latest, platform: x86_64-linux }
40 - - {
41 - name: linux-arm64,
42 - runner: ubuntu-24.04-arm,
43 - platform: aarch64-linux,
44 - }
45 - - { name: darwin-arm64, runner: macos-latest, platform: arm64-darwin }
46 - - {
47 - name: darwin-x64,
48 - runner: macos-15-intel,
49 - platform: x86_64-darwin,
50 - }
51 - # Windows is excluded: rb-sys bindgen fails because RubyInstaller is
52 - # MinGW-based and its rbconfig emits GCC flags that are incompatible
53 - # with the MSVC clang used by bindgen. The source gem provides a
54 - # fallback for Windows users (compiled at install time).
47 + platform: [x86_64-linux, aarch64-linux, arm64-darwin, x86_64-darwin]
48
49 steps:
50 - name: Checkout
@@ -62,7 +55,9 @@ jobs:
55 - name: Set the release version
56 shell: bash
57 run: |
65 - if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
58 + if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
59 + release_version="$(sed -n "s/.*VERSION = '\(.*\)'/\1/p" sdk/gems/${{ matrix.gem }}/lib/${{ matrix.gem }}/version.rb)"
60 + elif [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
61 release_version="${GITHUB_REF_NAME#v}"
62 else
63 release_version="${{ github.event.inputs.tag }}"
@@ -71,6 +66,7 @@ jobs:
66 echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
67
68 - name: Wait for Rust crate dependencies on crates.io
69 + if: github.event_name != 'pull_request'
70 shell: bash
71 run: |
72 for crate_name in smbcloud-auth-sdk smbcloud-model smbcloud-network; do
@@ -90,10 +86,6 @@ jobs:
86 done
87 done
88
93 - - name: Install system dependencies (Linux)
94 - if: runner.os == 'Linux'
95 - run: sudo apt-get update -q && sudo apt-get install -y libclang-dev
96 -
89 - name: Read Rust toolchain
90 shell: bash
91 run: |
@@ -109,18 +101,12 @@ jobs:
101 with:
102 toolchain: ${{ env.RUST_TOOLCHAIN }}
103
104 + # The container build uses this lockfile, so resolve the published
105 + # smbcloud-* crates for this release on the host first.
106 - name: Resolve the published Rust crate dependencies
107 shell: bash
108 run: cargo generate-lockfile --manifest-path sdk/gems/${{ matrix.gem }}/Cargo.toml
109
116 - # Scope the Rust cache to the gem's own Cargo workspace so its registry
117 - # deps are cached independently from the main workspace.
118 - - name: Setup Rust cache
119 - uses: Swatinem/rust-cache@v2
120 - with:
121 - key: sdk-gem-${{ matrix.gem }}-${{ matrix.name }}
122 - workspaces: sdk/gems/${{ matrix.gem }}
123 -
110 - name: Setup Ruby
111 uses: ruby/setup-ruby@v1
112 with:
@@ -128,16 +114,20 @@ jobs:
114 bundler-cache: true
115 working-directory: sdk/gems/${{ matrix.gem }}
116
131 - - name: Build native gem
132 - working-directory: sdk/gems/${{ matrix.gem }}
133 - shell: bash
134 - run: bundle exec rake native gem
117 + - name: Cross-compile native gem
118 + id: cross-gem
119 + uses: oxidize-rb/actions/cross-gem@v1
120 + with:
121 + platform: ${{ matrix.platform }}
122 + ruby-versions: ">= 3.1"
123 + working-directory: sdk/gems/${{ matrix.gem }}
124 + cache-version: ${{ matrix.gem }}
125
126 - name: Upload gem artifact
127 uses: actions/upload-artifact@v7
128 with:
139 - name: gem-${{ matrix.gem }}-${{ matrix.name }}
140 - path: sdk/gems/${{ matrix.gem }}/pkg/*.gem
129 + name: gem-${{ matrix.gem }}-${{ matrix.platform }}
130 + path: ${{ steps.cross-gem.outputs.gem-path }}
131
132 # ── Build source gem (fallback for unsupported platforms) ──────────────────
133 build-source-gem:
@@ -156,7 +146,9 @@ jobs:
146 - name: Set the release version
147 shell: bash
148 run: |
159 - if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
149 + if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
150 + release_version="$(sed -n "s/.*VERSION = '\(.*\)'/\1/p" sdk/gems/${{ matrix.gem }}/lib/${{ matrix.gem }}/version.rb)"
151 + elif [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
152 release_version="${GITHUB_REF_NAME#v}"
153 else
154 release_version="${{ github.event.inputs.tag }}"
@@ -177,6 +169,12 @@ jobs:
169 exit 1
170 fi
171
172 + # The source gem ships Cargo.lock; resolve it against the release's
173 + # published crates rather than shipping the committed lock.
174 + - name: Resolve the published Rust crate dependencies
175 + shell: bash
176 + run: cargo generate-lockfile --manifest-path sdk/gems/${{ matrix.gem }}/Cargo.toml
177 +
178 - name: Setup Ruby
179 uses: ruby/setup-ruby@v1
180 with:
@@ -198,6 +196,7 @@ jobs:
196 push-gems:
197 name: Push ${{ matrix.gem }} to RubyGems
198 needs: [build-gems, build-source-gem]
199 + if: github.event_name == 'workflow_dispatch'
200 runs-on: ubuntu-latest
201 strategy:
202 fail-fast: false
@@ -213,7 +212,9 @@ jobs:
212 - name: Set the release version
213 shell: bash
214 run: |
216 - if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
215 + if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
216 + release_version="$(sed -n "s/.*VERSION = '\(.*\)'/\1/p" sdk/gems/${{ matrix.gem }}/lib/${{ matrix.gem }}/version.rb)"
217 + elif [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
218 release_version="${GITHUB_REF_NAME#v}"
219 else
220 release_version="${{ github.event.inputs.tag }}"
scripts/check-release-versions.mjs
+12
@@ -64,6 +64,18 @@ for (const path of [
64 check(path, read(path).match(/VERSION\s*=\s*['"]([^'"]+)['"]/)?.[1]);
65 }
66
67 +// The Ruby gems link the smbcloud-* crates from crates.io. Their requirement
68 +// must be the release's major.minor, or a gem can silently wrap an older crate.
69 +const [expectedMajor, expectedMinor] = expectedVersion.split(".");
70 +const expectedRequirement = `${expectedMajor}.${expectedMinor}`;
71 +for (const path of ["sdk/gems/auth/ext/auth/Cargo.toml", "sdk/gems/model/ext/model/Cargo.toml"]) {
72 + for (const match of read(path).matchAll(/^(smbcloud-[\w-]+)\s*=\s*\{\s*version\s*=\s*"([^"]+)"/gm)) {
73 + if (match[2] !== expectedRequirement) {
74 + mismatches.push(`${path} dependency ${match[1]}: ${match[2]} (expected ${expectedRequirement})`);
75 + }
76 + }
77 +}
78 +
79 const npmPackage = JSON.parse(read("npm/smbcloud-cli/package.json"));
80 check("npm/smbcloud-cli/package.json", npmPackage.version);
81
scripts/sync-release-version.mjs
+13 -4
@@ -228,14 +228,23 @@ if (
228 }
229
230 if (
231 - updateFile(modelGemCargoTomlPath, (content) =>
232 - replaceOrThrow(
231 + updateFile(modelGemCargoTomlPath, (content) => {
232 + let next = replaceOrThrow(
233 content,
234 /^version\s*=\s*"[^"]+"/m,
235 `version = "${releaseVersion}"`,
236 `${modelGemCargoTomlPath} package version`,
237 - ),
238 - )
237 + );
238 +
239 + next = replaceOrThrow(
240 + next,
241 + /(smbcloud-model\s*=\s*\{\s*version\s*=\s*")([^"]+)("\s*\})/,
242 + `$1${rubySdkRequirement}$3`,
243 + `${modelGemCargoTomlPath} dependency smbcloud-model`,
244 + );
245 +
246 + return next;
247 + })
248 ) {
249 updatedPaths.push(modelGemCargoTomlPath);
250 }
sdk/gems/auth/Cargo.lock
+7 -7
@@ -98,7 +98,7 @@ dependencies = [
98
99 [[package]]
100 name = "auth"
101 -version = "0.4.13"
101 +version = "0.7.5"
102 dependencies = [
103 "magnus",
104 "serde",
@@ -1437,9 +1437,9 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
1437
1438 [[package]]
1439 name = "smbcloud-auth-sdk"
1440 -version = "0.4.12"
1440 +version = "0.7.5"
1441 source = "registry+https://github.com/rust-lang/crates.io-index"
1442 -checksum = "d25084bf6466294617b6f5a9f9f4900069b8f4085140b5fc9b9964b37e580e0c"
1442 +checksum = "ed95dfce60d603625cc5cc350b52c243aef2c13f846cb9565359e19eebe8a8c0"
1443 dependencies = [
1444 "base64",
1445 "reqwest",
@@ -1454,9 +1454,9 @@ dependencies = [
1454
1455 [[package]]
1456 name = "smbcloud-model"
1457 -version = "0.4.12"
1457 +version = "0.7.5"
1458 source = "registry+https://github.com/rust-lang/crates.io-index"
1459 -checksum = "e395d04033c6b95f1fbe1dff549b62f9a376f782e04a3f7ec6fa47b3f463362f"
1459 +checksum = "e9a960b4ee5fb1615367600f21431f547a7406d10bdde8b7f7fade0d4887e5c6"
1460 dependencies = [
1461 "chrono",
1462 "log",
@@ -1472,9 +1472,9 @@ dependencies = [
1472
1473 [[package]]
1474 name = "smbcloud-network"
1475 -version = "0.4.12"
1475 +version = "0.7.5"
1476 source = "registry+https://github.com/rust-lang/crates.io-index"
1477 -checksum = "1da12d7419edd1a61f5422e8d717a33dac0fe7bc0478280e873078f72e8e263e"
1477 +checksum = "7152f4588d5a5ec3c18a94a99701724795fa7bb4f8977df038d49536b16b8fb9"
1478 dependencies = [
1479 "clap 4.6.4",
1480 "log",
sdk/gems/auth/Gemfile.lock
+4 -4
@@ -3,7 +3,7 @@ PATH
3 specs:
4 smbcloud-auth (0.7.5)
5 json
6 - rb_sys (~> 0.9.91)
6 + rb_sys (~> 0.9, >= 0.9.130)
7
8 GEM
9 remote: https://rubygems.org/
@@ -12,9 +12,9 @@ GEM
12 rake (13.3.1)
13 rake-compiler (1.3.1)
14 rake
15 - rake-compiler-dock (1.11.0)
16 - rb_sys (0.9.124)
17 - rake-compiler-dock (= 1.11.0)
15 + rake-compiler-dock (1.12.0)
16 + rb_sys (0.9.130)
17 + rake-compiler-dock (= 1.12.0)
18
19 PLATFORMS
20 arm64-darwin-20
sdk/gems/auth/Rakefile
+2 -18
@@ -9,24 +9,8 @@ GEMSPEC = Gem::Specification.load('auth.gemspec')
9
10 RbSys::ExtensionTask.new('auth', GEMSPEC) do |ext|
11 ext.lib_dir = 'lib/auth'
12 -end
13 -
14 -# macOS: the rb_sys/rake-compiler build emits the bundle as an MH_DYLIB with an
15 -# empty LC_ID_DYLIB install name (a plain `cargo build` does not). dyld on macOS
16 -# 15+/Tahoe rejects that with "load command string extends beyond end of load
17 -# command", so the gem fails to load. Stamp a valid install name onto every
18 -# bundle the build produces by enhancing the bundle file tasks themselves (which
19 -# is what `rake native gem` runs, not the top-level `compile`). install_name_tool
20 -# re-signs ad-hoc, ships on the macOS gem-build runners, and works across archs
21 -# (cross-built x86_64 bundles too). Idempotent.
22 -if RbConfig::CONFIG['host_os'] =~ /darwin/
23 - Rake::Task.tasks.each do |t|
24 - next unless t.name.end_with?('auth.bundle')
25 -
26 - t.enhance do
27 - sh 'install_name_tool', '-id', '@rpath/auth.bundle', t.name if File.exist?(t.name)
28 - end
29 - end
12 + ext.cross_compile = true
13 + ext.cross_platform = %w[x86_64-linux aarch64-linux arm64-darwin x86_64-darwin]
14 end
15
16 task default: :compile
sdk/gems/auth/auth.gemspec
+5 -2
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24 spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls|
25 ls.readlines("\x0", chomp: true).reject do |f|
26 (f == gemspec) ||
27 - f.start_with?(*%w[bin/ test/ spec/ features/ .git appveyor Gemfile])
27 + f.start_with?(*%w[bin/ test/ spec/ features/ .git .ruby-version .rubocop.yml appveyor Gemfile])
28 end
29 end
30 spec.bindir = 'exe'
@@ -33,5 +33,8 @@ Gem::Specification.new do |spec|
33 spec.extensions = ['ext/auth/extconf.rb']
34
35 spec.add_dependency 'json'
36 - spec.add_dependency 'rb_sys', '~> 0.9.91'
36 + # 0.9.130 is the first rb_sys that gives darwin bundles a non-empty install
37 + # name. Older versions run `install_name_tool -id ""`, which dyld on macOS 15+
38 + # rejects, so the extension fails to load.
39 + spec.add_dependency 'rb_sys', '~> 0.9', '>= 0.9.130'
40 end
sdk/gems/auth/ext/auth/build.rs deleted
-11
@@ -1,11 +0,0 @@
1 -fn main() {
2 - // On macOS, a Rust `cdylib` is emitted with an empty LC_ID_DYLIB install
3 - // name. The dyld in macOS 15+/Tahoe (Darwin 27) validates this strictly and
4 - // refuses to load the resulting `.bundle` with:
5 - // "load command #4 string extends beyond end of load command"
6 - // Give the dylib a valid install name at link time so the Ruby extension
7 - // loads on the stricter loader.
8 - if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("macos") {
9 - println!("cargo:rustc-cdylib-link-arg=-Wl,-install_name,@rpath/auth.bundle");
10 - }
11 -}
sdk/gems/auth/ext/auth/extconf.rb
-12
@@ -4,15 +4,3 @@ require 'mkmf'
4 require 'rb_sys/mkmf'
5
6 create_rust_makefile('auth/auth')
7 -
8 -# rb-sys (fixup_libnames) appends `install_name_tool -id "" $(DLLIB)` to the
9 -# generated Makefile, blanking the bundle's LC_ID_DYLIB install name. dyld on
10 -# macOS 26/27 rejects an empty dylib-ID string ("load command string extends
11 -# beyond end of load command"), so the extension fails to dlopen. Rewrite that
12 -# step to stamp a valid @rpath id instead. This covers the `gem install`
13 -# (extconf) build path; the Rakefile covers `rake native gem` builds.
14 -if RUBY_PLATFORM.include?('darwin') && File.exist?('Makefile')
15 - makefile = File.read('Makefile')
16 - patched = makefile.gsub('install_name_tool -id ""', 'install_name_tool -id "@rpath/auth.bundle"')
17 - File.write('Makefile', patched) if patched != makefile
18 -end
sdk/gems/auth/lib/auth.rb
+8 -1
@@ -2,7 +2,14 @@
2
3 require 'json'
4 require_relative 'auth/version'
5 -require_relative 'auth/auth'
5 +# Fat native gems ship one extension per Ruby minor (lib/auth/3.4/auth.bundle);
6 +# source and single-ABI builds put it directly under lib/auth/.
7 +begin
8 + RUBY_VERSION =~ /(\d+\.\d+)/
9 + require_relative "auth/#{Regexp.last_match(1)}/auth"
10 +rescue LoadError
11 + require_relative 'auth/auth'
12 +end
13
14 module Auth
15 end
sdk/gems/model/Cargo.lock
+3 -3
@@ -392,7 +392,7 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
392
393 [[package]]
394 name = "model"
395 -version = "0.4.13"
395 +version = "0.7.5"
396 dependencies = [
397 "magnus",
398 "smbcloud-model",
@@ -668,9 +668,9 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
668
669 [[package]]
670 name = "smbcloud-model"
671 -version = "0.4.12"
671 +version = "0.7.5"
672 source = "registry+https://github.com/rust-lang/crates.io-index"
673 -checksum = "e395d04033c6b95f1fbe1dff549b62f9a376f782e04a3f7ec6fa47b3f463362f"
673 +checksum = "e9a960b4ee5fb1615367600f21431f547a7406d10bdde8b7f7fade0d4887e5c6"
674 dependencies = [
675 "chrono",
676 "log",
sdk/gems/model/Gemfile.lock
+4 -4
@@ -2,7 +2,7 @@ PATH
2 remote: .
3 specs:
4 smbcloud-model (0.7.5)
5 - rb_sys (~> 0.9.91)
5 + rb_sys (~> 0.9, >= 0.9.130)
6
7 GEM
8 remote: https://rubygems.org/
@@ -10,9 +10,9 @@ GEM
10 rake (13.2.1)
11 rake-compiler (1.3.0)
12 rake
13 - rake-compiler-dock (1.9.1)
14 - rb_sys (0.9.115)
15 - rake-compiler-dock (= 1.9.1)
13 + rake-compiler-dock (1.12.0)
14 + rb_sys (0.9.130)
15 + rake-compiler-dock (= 1.12.0)
16
17 PLATFORMS
18 arm64-darwin-24
sdk/gems/model/Rakefile
+2 -18
@@ -9,24 +9,8 @@ GEMSPEC = Gem::Specification.load('model.gemspec')
9
10 RbSys::ExtensionTask.new('model', GEMSPEC) do |ext|
11 ext.lib_dir = 'lib/model'
12 -end
13 -
14 -# macOS: the rb_sys/rake-compiler build emits the bundle as an MH_DYLIB with an
15 -# empty LC_ID_DYLIB install name (a plain `cargo build` does not). dyld on macOS
16 -# 15+/Tahoe rejects that with "load command string extends beyond end of load
17 -# command", so the gem fails to load. Stamp a valid install name onto every
18 -# bundle the build produces by enhancing the bundle file tasks themselves (which
19 -# is what `rake native gem` runs, not the top-level `compile`). install_name_tool
20 -# re-signs ad-hoc, ships on the macOS gem-build runners, and works across archs
21 -# (cross-built x86_64 bundles too). Idempotent.
22 -if RbConfig::CONFIG['host_os'] =~ /darwin/
23 - Rake::Task.tasks.each do |t|
24 - next unless t.name.end_with?('model.bundle')
25 -
26 - t.enhance do
27 - sh 'install_name_tool', '-id', '@rpath/model.bundle', t.name if File.exist?(t.name)
28 - end
29 - end
12 + ext.cross_compile = true
13 + ext.cross_platform = %w[x86_64-linux aarch64-linux arm64-darwin x86_64-darwin]
14 end
15
16 task default: :compile
sdk/gems/model/ext/model/Cargo.toml
+1 -1
@@ -11,4 +11,4 @@ crate-type = ["cdylib"]
11 [dependencies]
12 magnus = { version = "0.7.1" }
13 strum = "0.27"
14 -smbcloud-model = { version = "0.4" }
14 +smbcloud-model = { version = "0.7" }
sdk/gems/model/lib/model.rb
+8 -1
@@ -1,7 +1,14 @@
1 # frozen_string_literal: true
2
3 require_relative 'model/version'
4 -require_relative 'model/model'
4 +# Fat native gems ship one extension per Ruby minor (lib/model/3.4/model.bundle);
5 +# source and single-ABI builds put it directly under lib/model/.
6 +begin
7 + RUBY_VERSION =~ /(\d+\.\d+)/
8 + require_relative "model/#{Regexp.last_match(1)}/model"
9 +rescue LoadError
10 + require_relative 'model/model'
11 +end
12
13 module Model
14 class Error < StandardError; end
sdk/gems/model/model.gemspec
+5 -2
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
26 spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls|
27 ls.readlines("\x0", chomp: true).reject do |f|
28 (f == gemspec) ||
29 - f.start_with?(*%w[bin/ test/ spec/ features/ .git appveyor Gemfile])
29 + f.start_with?(*%w[bin/ test/ spec/ features/ .git .ruby-version .rubocop.yml appveyor Gemfile])
30 end
31 end
32 spec.bindir = "exe"
@@ -34,5 +34,8 @@ Gem::Specification.new do |spec|
34 spec.require_paths = ["lib"]
35 spec.extensions = ["ext/model/extconf.rb"]
36
37 - spec.add_dependency "rb_sys", "~> 0.9.91"
37 + # 0.9.130 is the first rb_sys that gives darwin bundles a non-empty install
38 + # name. Older versions run `install_name_tool -id ""`, which dyld on macOS 15+
39 + # rejects, so the extension fails to load.
40 + spec.add_dependency "rb_sys", "~> 0.9", ">= 0.9.130"
41 end