這會是 Mongory 能夠被大多數讀者輕鬆用起來的轉折點:預編譯。
筆者在公司的 pod 實際安裝時,從「本機沒 toolchain 裝不起來」的挫折出發,走過 rake-compiler-dock 的嘗試與受挫,最後落在 GitHub Actions 的原生矩陣編譯方案,才把交付真正穩下來。這篇把整段歷程與最終做法完整交代,讓讀者只需安裝即可使用,無需額外設定
目標:
.bundle
/.so
)挑戰:
在公司 pod 安裝時實際遇到的第一個阻礙就是「開發機沒有完整 toolchain,安裝時直接卡住」,筆者意識到不能要求所有人都裝編譯環境,因此預編譯勢在必行
一開始筆者嘗試 rake-compiler-dock 的跨平台編譯,希望一次性打包多平台產物;但在本機實驗時頻繁遇到不明失敗與版本對不上。於是將重心轉到 GitHub Actions,讓 CI 代替本機做原生編譯:
tree
輸出把目錄結構看清楚,確保每個 Ruby 版本的產物都正確歸位最終決策清晰:放棄 dock 黑箱,全面改走「總控 workflow + 子流程」的原生矩陣編譯,讓每個平台的 log 與產物都可觀測、可驗證
作業系統與平台三元組:
x86_64-darwin
(runner macos-13
)、arm64-darwin
(runner macos-14
)x86_64-linux
、aarch64-linux
、x86_64-linux-musl
、aarch64-linux-musl
x64-mingw32
、x64-mingw-ucrt
Ruby 版本:
2.6
–3.4
3.0
–3.4
C/C++ 工具鏈:
build-essential
、cmake
(若用 mongory-core/build.sh
或 CMake 流程)GLIBC 與相容性:同時產出 *-linux
(glibc)與 *-linux-musl
(musl)平台 gem,以覆蓋主流發行版。若個別環境仍不相容,RubyGems 會自動走原始碼編譯路徑,讀者無需手動介入
採用一個總控 workflow build.yml
觸發三個可重用子流程(Linux/macOS/Windows),各自產出平台 gem,最後彙整並推送;讀者只需 gem install mongory
或在 Gemfile 指定版本,即可自動取得對應平台的預編譯套件
# .github/workflows/build.yml(節錄)
name: Release Mongory
on:
push:
tags:
- "v*"
jobs:
linux-runners:
strategy:
matrix:
platform:
[x86_64-linux-musl, aarch64-linux-musl, x86_64-linux, aarch64-linux]
uses: ./.github/workflows/_linux_cross_compile.yml
with:
platform: ${{ matrix.platform }}
macos-runners:
strategy:
matrix:
include:
- platform: x86_64-darwin
runner: macos-13
- platform: arm64-darwin
runner: macos-14
uses: ./.github/workflows/_macos_cross_compile.yml
with:
platform: ${{ matrix.platform }}
runner: ${{ matrix.runner }}
windows-runners:
strategy:
matrix:
platform: [x64-mingw32, x64-mingw-ucrt]
uses: ./.github/workflows/_windows_cross_compile.yml
with:
platform: ${{ matrix.platform }}
release-gem:
needs: [linux-runners, macos-runners, windows-runners]
runs-on: ubuntu-latest
steps:
- uses: ruby/setup-ruby@v1
with:
ruby-version: "3.2"
- uses: rubygems/release-gem@v1 # 先釋出 source gem
- uses: actions/download-artifact@v4
with:
pattern: gem-*
path: ./artifacts
merge-multiple: true
- name: Push precompiled platform gems
run: |
set -euo pipefail
shopt -s nullglob globstar
while IFS= read -r -d '' g; do
gem push "$g"
done < <(find ./artifacts -type f -name '*.gem' -print0 | sort -z)
Linux 子流程(節錄):
# .github/workflows/_linux_cross_compile.yml(節錄)
env:
PLATFORM: ${{ inputs.platform }}
RUBIES: "2.6 2.7 3.0 3.1 3.2 3.3 3.4"
jobs:
c-extension-compile:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- ruby: "2.6"; bundler: "2.1.4"
- ruby: "2.7"; bundler: "2.1.4"
- ruby: "3.0"; bundler: "2.5.6"
- ruby: "3.1"; bundler: "2.5.6"
- ruby: "3.2"; bundler: "2.5.6"
- ruby: "3.3"; bundler: "2.5.6"
- ruby: "3.4"; bundler: "2.5.6"
steps:
- uses: ruby/setup-ruby@v1
with: { ruby-version: ${{ matrix.ruby }}, bundler: ${{ matrix.bundler }}, bundler-cache: true }
- run: bundle exec rake clean && bundle exec rake compile
- uses: actions/upload-artifact@v4
with:
name: compiled-${{ inputs.platform }}-${{ matrix.ruby }}
path: lib/core/${{ matrix.ruby }}/mongory_ext.so
build-gem:
needs: c-extension-compile
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with: { pattern: compiled-${{ inputs.platform }}-*, path: ./artifacts }
- name: Move files to lib/core
run: |
IFS=' ' read -r -a ruby_versions <<< "$RUBIES"
for ruby in "${ruby_versions[@]}"; do
mkdir -p lib/core/$ruby
mv -f ./artifacts/compiled-$PLATFORM-$ruby/mongory_ext.so ./lib/core/$ruby/
done
- name: Native build Gem
env: { RCD_PLATFORM: ${{ inputs.platform }}, NATIVE_BUILD: 1 }
run: gem build mongory.gemspec
- uses: actions/upload-artifact@v4
with: { name: gem-${{ inputs.platform }}, path: "*.gem" }
macOS/Windows 子流程與 Linux 類似:
mongory_ext.bundle
,runner 分別是 macos-13
(x86_64)與 macos-14
(arm64)windows-latest
,目前產物檔名為 mongory_ext.so
(依現況保留)另外有一個測試用流程(維運用):
# .github/workflows/build-test.yml(節錄)
on:
push:
branches: ["feature/cross-platform-build"]
jobs:
linux-runners: { uses: ./.github/workflows/_linux_cross_compile.yml, with: { platform: ${{ matrix.platform }} } }
macos-runners: { uses: ./.github/workflows/_macos_cross_compile.yml, with: { platform: ${{ matrix.platform }}, runner: ${{ matrix.runner }} } }
windows-runners: { uses: ./.github/workflows/_windows_cross_compile.yml, with: { platform: ${{ matrix.platform }} } }
重點說明:
lib/core/<ruby>/mongory_ext.(bundle|so)
,再以 RCD_PLATFORM
+NATIVE_BUILD=1
打包平台 gemmongory-rb
會在 CI 自動完成 C 擴充與 C Core 的同步建置lib/core/<ruby>/mongory_ext.(bundle|so)
形式封裝)安裝時會先嘗試取得對應平台的預編譯 gem;若目標環境較舊或不相容,RubyGems 會自動改走原始碼編譯路徑。整個過程對讀者透明,無需額外設定或環境變數
筆者選擇了「原生矩陣編譯+保留回退」的務實路線:一方面保有可觀測的 CI、降低黑箱,另一方面讓讀者在任何環境都能安裝成功。這正符合 Mongory 的精神:把複雜度藏在可控的邊界,對外呈現穩定、可預期的使用體驗
Day 22:Mongory Bridge 的下一站:為什麼選 Go