Skip to content

Commit 6cc5aa2

Browse files
flavorjoneskddnewton
authored andcommitted
Add native gem precompilation support
Package precompiled native gems for Ruby 3.3, 3.4, and 4.0 across 10 platforms: aarch64-linux-gnu, aarch64-linux-musl, aarch64-mingw-ucrt, arm-linux-gnu, arm-linux-musl, arm64-darwin, x64-mingw-ucrt, x86_64-darwin, x86_64-linux-gnu, and x86_64-linux-musl. - Add Ruby-version-aware extension loader in lib/prism.rb with GLIBC error message for musl/glibc mismatches - Add cross-compilation support to Rakefile using rake-compiler-dock - Add a reusable `build-gems.yml` workflow called by both the CI workflow (`test-gems.yml`) and the publish workflow (`publish-gem.yml`), building source and native gems across all platforms and Ruby versions - Add scripts for building, verifying, and testing gems Update `publish-gem.yml` to handle multi-gem publishing. Note that `rubygems/release-gem` only supports a single gem, so this uses `rubygems/configure-rubygems-credentials` and loops `gem push` over each built `.gem`. Add a `workflow_dispatch` trigger alongside the existing tag-push trigger, and create/update a GitHub release for the tag with all gems plus a `CHECKSUMS.txt` manifest. Move testing of the "ruby" platform gem (compile-from-source) out of `main.yml` into `test-gems.yml` so we're testing the gem built by the workflow tooling.
1 parent 69b1c47 commit 6cc5aa2

15 files changed

Lines changed: 879 additions & 90 deletions

File tree

.github/scripts/gem-build

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#! /usr/bin/env bash
2+
#
3+
# run as part of CI, or as part of the release pipeline with --release
4+
#
5+
RELEASE=false
6+
if [[ "${1:-}" == "--release" ]] ; then
7+
RELEASE=true
8+
shift
9+
fi
10+
11+
if [[ $# -lt 2 ]] ; then
12+
echo "usage: $(basename $0) [--release] <output_dir> <platform>"
13+
exit 1
14+
fi
15+
16+
set -e -u
17+
18+
OUTPUT_DIR=$1
19+
BUILD_NATIVE_GEM=$2
20+
21+
test -e /etc/os-release && cat /etc/os-release
22+
23+
set -x
24+
25+
bundle config set without development
26+
27+
if [[ "${RELEASE}" == "true" ]] ; then
28+
bundle install --local || bundle install
29+
else
30+
bundle config set frozen false
31+
bundle install --local || bundle install
32+
bundle exec rake gemspec:fake-version
33+
fi
34+
35+
if [[ "${BUILD_NATIVE_GEM}" == "ruby" ]] ; then
36+
bundle exec rake build
37+
else
38+
bundle exec rake gem:${BUILD_NATIVE_GEM}:build
39+
fi
40+
41+
./test/prism/packaging/test-gem-file-contents pkg/*.gem
42+
43+
mkdir -p ${OUTPUT_DIR}
44+
cp -v pkg/*.gem ${OUTPUT_DIR}
45+
ls -l ${OUTPUT_DIR}/*

.github/scripts/gem-install-test

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#! /usr/bin/env sh
2+
#
3+
# run as part of CI
4+
#
5+
if [ $# -lt 1 ] ; then
6+
echo "usage: $(basename $0) <gem-file>"
7+
exit 1
8+
fi
9+
10+
gemfile=$1
11+
shift
12+
13+
test -e /etc/os-release && cat /etc/os-release
14+
15+
set -e -x -u
16+
17+
ls -l ${gemfile}
18+
gem install --no-document ${gemfile}
19+
gem list -d prism
20+
21+
bundle config set without development
22+
bundle install --local || bundle install
23+
24+
rm -rf lib ext # ensure we don't use the local files
25+
rake test

.github/workflows/build-gems.yml

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
name: Build gems
2+
on:
3+
workflow_call:
4+
inputs:
5+
release:
6+
description: "Pass --release to .github/scripts/gem-build (skip timestamp version)"
7+
type: boolean
8+
default: false
9+
artifact_prefix:
10+
description: "Prefix for artifact names (e.g., 'release-v1.10.0-')"
11+
type: string
12+
default: ""
13+
ref:
14+
description: "Git ref to check out (e.g., a version tag). Defaults to the caller's ref."
15+
type: string
16+
default: ""
17+
18+
permissions:
19+
contents: read
20+
21+
jobs:
22+
build_source_gem:
23+
name: "source gem"
24+
runs-on: ubuntu-latest
25+
steps:
26+
- uses: actions/checkout@v7
27+
with:
28+
ref: ${{ inputs.ref || github.ref }}
29+
persist-credentials: false
30+
- uses: ruby/setup-ruby@v1
31+
with:
32+
ruby-version: ruby
33+
bundler-cache: true
34+
- name: Build source gem
35+
run: |
36+
./.github/scripts/gem-build ${RELEASE_FLAG} gems ruby
37+
env:
38+
RELEASE_FLAG: ${{ inputs.release && '--release' || '' }}
39+
- uses: actions/upload-artifact@v7
40+
with:
41+
name: "${{ inputs.artifact_prefix }}source-gem"
42+
path: gems
43+
retention-days: 1
44+
45+
build_native_setup:
46+
name: "setup"
47+
runs-on: ubuntu-latest
48+
outputs:
49+
rcd_image_version: ${{ steps.rcd_image_version.outputs.rcd_image_version }}
50+
steps:
51+
- uses: actions/checkout@v7
52+
with:
53+
ref: ${{ inputs.ref || github.ref }}
54+
persist-credentials: false
55+
- uses: ruby/setup-ruby@v1
56+
with:
57+
ruby-version: ruby
58+
bundler-cache: true
59+
- id: rcd_image_version
60+
run: bundle exec ruby -e 'require "rake_compiler_dock"; puts "rcd_image_version=#{RakeCompilerDock::IMAGE_VERSION}"' >> "$GITHUB_OUTPUT"
61+
62+
build_native_gem:
63+
needs: build_native_setup
64+
name: "native gem ${{ matrix.platform }}"
65+
strategy:
66+
# In test runs, surface every platform's failure (fail-fast: false).
67+
# In release runs, stop on first failure to avoid pushing a partial set of gems.
68+
fail-fast: ${{ inputs.release }}
69+
matrix:
70+
platform:
71+
- aarch64-linux-gnu
72+
- aarch64-linux-musl
73+
- aarch64-mingw-ucrt
74+
- arm-linux-gnu
75+
- arm-linux-musl
76+
- arm64-darwin
77+
- x64-mingw-ucrt
78+
- x86_64-darwin
79+
- x86_64-linux-gnu
80+
- x86_64-linux-musl
81+
runs-on: ubuntu-latest
82+
steps:
83+
- uses: actions/checkout@v7
84+
with:
85+
ref: ${{ inputs.ref || github.ref }}
86+
persist-credentials: false
87+
- name: Build native gem
88+
run: |
89+
docker run --rm -v "$PWD:/work" -w /work \
90+
"ghcr.io/rake-compiler/rake-compiler-dock-image:${RCD_IMAGE_VERSION}-mri-${PLATFORM}" \
91+
./.github/scripts/gem-build ${RELEASE_FLAG} gems "${PLATFORM}"
92+
env:
93+
RCD_IMAGE_VERSION: ${{ needs.build_native_setup.outputs.rcd_image_version }}
94+
RELEASE_FLAG: ${{ inputs.release && '--release' || '' }}
95+
PLATFORM: ${{ matrix.platform }}
96+
- uses: actions/upload-artifact@v7
97+
with:
98+
name: "${{ inputs.artifact_prefix }}cruby-${{ matrix.platform }}-gem"
99+
path: gems
100+
retention-days: 1

.github/workflows/main.yml

Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -274,82 +274,6 @@ jobs:
274274
- name: Run Ruby tests with valgrind
275275
run: bundle exec rake test:valgrind
276276

277-
gem-package:
278-
runs-on: ubuntu-latest
279-
steps:
280-
- uses: actions/checkout@v7
281-
- uses: ruby/setup-ruby@v1
282-
with:
283-
ruby-version: ruby
284-
bundler-cache: true
285-
- run: bundle config --local frozen false
286-
- run: bundle exec rake build:dev
287-
- uses: actions/upload-artifact@v7
288-
with:
289-
name: gem-package
290-
path: pkg
291-
retention-days: 1
292-
293-
gem-install:
294-
needs: ["gem-package"]
295-
strategy:
296-
fail-fast: false
297-
matrix:
298-
target:
299-
- { ruby: "2.7", os: "ubuntu-latest", gemfile: "2.7" }
300-
- { ruby: "3.0", os: "ubuntu-latest", gemfile: "3.0" }
301-
- { ruby: "3.1", os: "ubuntu-latest", gemfile: "3.1" }
302-
- { ruby: "3.2", os: "ubuntu-latest", gemfile: "3.2" }
303-
- { ruby: "3.3", os: "ubuntu-latest", gemfile: "3.3" }
304-
- { ruby: "3.4", os: "ubuntu-latest", gemfile: "3.4" }
305-
- { ruby: "4.0", os: "ubuntu-latest", gemfile: "4.0" }
306-
- { ruby: "head", os: "ubuntu-latest", gemfile: "4.1" }
307-
- { ruby: "jruby", os: "ubuntu-latest", gemfile: ".." }
308-
- { ruby: "truffleruby", os: "ubuntu-latest", gemfile: ".." }
309-
310-
- { ruby: "2.7", os: "macos-latest", gemfile: "2.7" }
311-
- { ruby: "3.0", os: "macos-latest", gemfile: "3.0" }
312-
- { ruby: "3.1", os: "macos-latest", gemfile: "3.1" }
313-
- { ruby: "3.2", os: "macos-latest", gemfile: "3.2" }
314-
- { ruby: "3.3", os: "macos-latest", gemfile: "3.3" }
315-
- { ruby: "3.4", os: "macos-latest", gemfile: "3.4" }
316-
- { ruby: "4.0", os: "macos-latest", gemfile: "4.0" }
317-
- { ruby: "head", os: "macos-latest", gemfile: "4.1" }
318-
- { ruby: "jruby", os: "macos-latest", gemfile: ".." }
319-
- { ruby: "truffleruby", os: "macos-latest", gemfile: ".." }
320-
321-
- { ruby: "2.7", os: "windows-latest", gemfile: "2.7" }
322-
- { ruby: "3.0", os: "windows-latest", gemfile: "3.0" }
323-
- { ruby: "3.1", os: "windows-latest", gemfile: "3.1" }
324-
- { ruby: "3.2", os: "windows-latest", gemfile: "3.2" }
325-
- { ruby: "3.3", os: "windows-latest", gemfile: "3.3" }
326-
- { ruby: "3.4", os: "windows-latest", gemfile: "3.4" }
327-
- { ruby: "4.0", os: "windows-latest", gemfile: "4.0" }
328-
- { ruby: "head", os: "windows-latest", gemfile: "4.1" }
329-
- { ruby: "jruby", os: "windows-latest", gemfile: ".." }
330-
env:
331-
BUNDLE_GEMFILE: gemfiles/${{ matrix.target.gemfile }}/Gemfile
332-
runs-on: ${{ matrix.target.os }}
333-
steps:
334-
- uses: actions/checkout@v7
335-
- uses: ruby/setup-ruby@v1
336-
with:
337-
ruby-version: ${{ matrix.target.ruby }}
338-
- uses: actions/download-artifact@v8
339-
with:
340-
name: gem-package
341-
path: pkg
342-
- run: |
343-
gem install --local pkg/prism-*.gem
344-
gem list -d prism
345-
shell: bash
346-
- name: Run tests
347-
run: |
348-
bundle install
349-
rm -rf lib ext # ensure we don't use the local files
350-
rake test
351-
shell: bash
352-
353277
gcc-analyzer:
354278
runs-on: ubuntu-latest
355279
steps:

.github/workflows/publish-gem.yml

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,87 @@
11
name: Publish gem to rubygems.org
2+
concurrency:
3+
group: "release-${{ inputs.version_tag || github.ref }}"
4+
cancel-in-progress: false
25

36
on:
47
push:
58
tags:
69
- 'v*'
10+
workflow_dispatch:
11+
inputs:
12+
version_tag:
13+
description: "Version tag to release (e.g., v1.10.0)"
14+
required: true
15+
type: string
716

817
permissions:
918
contents: read
1019

1120
jobs:
21+
build:
22+
uses: ./.github/workflows/build-gems.yml
23+
with:
24+
release: true
25+
artifact_prefix: "release-${{ inputs.version_tag || github.ref_name }}-"
26+
ref: ${{ inputs.version_tag || github.ref }}
27+
1228
push:
29+
name: "Push gems and create GitHub release"
30+
needs: build
1331
if: github.repository == 'ruby/prism'
1432
runs-on: ubuntu-latest
15-
1633
environment:
1734
name: rubygems.org
1835
url: https://rubygems.org/gems/prism
19-
2036
permissions:
21-
contents: write
22-
id-token: write
23-
37+
contents: write # create/update GitHub releases and upload .gem assets
38+
id-token: write # OIDC token for rubygems.org trusted publishing
2439
steps:
2540
- name: Harden Runner
2641
uses: step-security/harden-runner@v2
2742
with:
2843
egress-policy: audit
2944

3045
- uses: actions/checkout@v7
46+
with:
47+
ref: ${{ inputs.version_tag || github.ref }}
48+
persist-credentials: false
3149

3250
- name: Set up Ruby
3351
uses: ruby/setup-ruby@v1
3452
with:
3553
ruby-version: ruby
3654
bundler-cache: true
3755

38-
- name: Publish to RubyGems
39-
uses: rubygems/release-gem@v1
56+
- uses: actions/download-artifact@v8
57+
with:
58+
path: gems
59+
pattern: "release-${{ inputs.version_tag || github.ref_name }}-*"
60+
merge-multiple: true
61+
62+
- name: Print and record checksums
63+
run: |
64+
cd gems
65+
sha256sum ./*.gem | tee CHECKSUMS.txt
66+
67+
- uses: rubygems/configure-rubygems-credentials@v2
68+
69+
# TODO: once RubyGems >= 4.1 is in setup-ruby, `gem push` will auto-attest (ruby/rubygems#9325).
70+
- name: Push gems to RubyGems.org
71+
run: |
72+
for gem in gems/*.gem; do
73+
echo "Pushing ${gem} ..."
74+
gem push "${gem}" || echo "WARNING: Failed to push ${gem} (may already exist)"
75+
done
76+
77+
- name: Create or update GitHub release
78+
env:
79+
GH_TOKEN: ${{ github.token }}
80+
TAG: ${{ inputs.version_tag || github.ref_name }}
81+
REPO: ${{ github.repository }}
82+
run: |
83+
if gh release view "$TAG" --repo "$REPO" >/dev/null 2>&1 ; then
84+
gh release upload "$TAG" --repo "$REPO" --clobber gems/*.gem gems/CHECKSUMS.txt
85+
else
86+
gh release create "$TAG" --repo "$REPO" --title "$TAG" --generate-notes gems/*.gem gems/CHECKSUMS.txt
87+
fi

0 commit comments

Comments
 (0)