Skip to content

ci: one JRuby cell instead of five #1550

ci: one JRuby cell instead of five

ci: one JRuby cell instead of five #1550

Workflow file for this run

---
name: Test
on:
push:
branches: [ master ]
paths:
- '**.gemfile'
- '**.rb'
- '.github/workflows/**'
- '.github/actions/**'
- 'Gemfile*'
- '!bin/**'
pull_request:
types: [ opened, synchronize, reopened, labeled ]
paths:
- '**.gemfile'
- '**.rb'
- '.github/workflows/**'
- '.github/actions/**'
- 'Gemfile*'
- '!bin/**'
schedule:
# Weekly full-suite drift check on master, quiet slot.
- cron: '0 6 * * 1'
workflow_dispatch:
env:
BUNDLE_GEMFILE: gemfiles/rails81_gems.rb
DEBIAN_FRONTEND: noninteractive
FERRUM_PROCESS_TIMEOUT: 40
JAVA_OPTS: -Xmn2g -Xms6g -Xmx6g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -Xss1m
-XX:+UseG1GC -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -XX:ReservedCodeCacheSize=256m
-XX:+UseCodeCacheFlushing
JRUBY_OPTS: --dev -J-Djruby.thread.pool.enabled=true
MALLOC_ARENA_MAX: 2
RUBY_GC_HEAP_FREE_SLOTS: 600000
RUBY_GC_HEAP_GROWTH_FACTOR: 1.1
RUBY_YJIT_ENABLE: 1
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
# Test that new contributors can run the tests directly after checkout.
test-minimal-setup:
name: Test with minimal setup
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
- uses: ./.github/actions/setup-ruby-and-dependencies
with:
ruby-version: "4.0"
- run: bin/rake test
env:
SCREENSHOT_DRIVER: vips
functional-test:
name: Functional Test
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v7
- uses: ./.github/actions/setup-ruby-and-dependencies
with:
ruby-version: "4.0"
cache-apt-packages: true
- run: bin/rake test
env:
COVERAGE: enabled
DISABLE_SKIP_TESTS: 1
SCREENSHOT_DRIVER: vips
- uses: ./.github/actions/upload-screenshots
if: failure()
with:
name: base-screenshots
pr-comment: 'true'
- name: Uploading Coverage Report
uses: actions/upload-artifact@v7
with:
name: coverage
retention-days: 1
path: coverage
matrix:
name: Test Ruby & Rails
# PRs run only the critical jobs. This is the master gate, plus manual
# dispatch, the weekly cron, and PRs opting in with the 'full-ci' label.
#
# A SPANNING SET, not a cross product: every Ruby once, every Rails once,
# with the oldest Ruby paired against both the oldest and newest Rails.
# The cross product re-proved the same facts -- TestsWithoutAssertions
# being Rails 7.2+ (#283) appears on every Ruby running 7.1, and JRuby
# lacking Kernel#fork (#283) appears on every Rails. Measured before
# cutting: 16 cells cost 130 min/run, 56% of it JRuby.
#
# 4.0 x rails81 is absent because `functional-test` above is exactly that.
if: >
github.ref == 'refs/heads/master' ||
github.event_name == 'workflow_dispatch' ||
github.event_name == 'schedule' ||
contains(github.event.pull_request.labels.*.name, 'full-ci')
needs: [ functional-test ]
runs-on: ubuntu-latest
# Must fit `max_attempts * timeout_minutes` below, plus ~1 min of setup,
# or the last attempt gets killed mid-run and the cell reports `cancelled`
# -- a dead gate. JRuby: 1 + 20 + 20 = 41. MRI: 1 + 4 + 4 = 9.
timeout-minutes: ${{ contains(matrix.ruby-version, 'jruby') && 41 || 9 }}
continue-on-error: ${{ matrix.experimental }}
strategy:
matrix:
include:
- { ruby-version: "3.3", gemfile: rails71_gems.rb, experimental: false }
- { ruby-version: "3.3", gemfile: rails81_gems.rb, experimental: false }
- { ruby-version: "3.4", gemfile: rails72_gems.rb, experimental: false }
- { ruby-version: "3.4", gemfile: rails80_gems.rb, experimental: false }
- { ruby-version: "4.0", gemfile: rails80_gems.rb, experimental: false }
# One JRuby cell: newest Rails, and cuprite + vips are what it
# already gets (CAPYBARA_DRIVER defaults to cuprite in
# test/support/setup_capybara_drivers.rb; the env below picks vips
# for jruby). Its differences -- no Kernel#fork, thread semantics,
# the FFI/vips path -- belong to the JVM, not to Rails, so a second
# Rails version costs ~15 min to re-prove them.
- { ruby-version: jruby-10.1, gemfile: rails81_gems.rb, experimental: false }
env:
BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}
# The JRuby cells are the only place the vips driver runs on a non-MRI
# engine, and `bin/rake test` otherwise leaves them on chunky_png (the
# default in test/system_test_case.rb) -- so ruby-vips' FFI path was
# only ever loaded there, never driven end to end. Test Drivers covers
# both drivers on CRuby; chunky_png stays covered on JRuby by its own
# unit tests, which do not read this variable.
SCREENSHOT_DRIVER: ${{ contains(matrix.ruby-version, 'jruby') && 'vips' || 'chunky_png' }}
steps:
- uses: actions/checkout@v7
- uses: ./.github/actions/setup-ruby-and-dependencies
with:
ruby-version: ${{ matrix.ruby-version }}
ruby-cache-version: ${{ matrix.ruby-version }}-${{ matrix.gemfile }}-1
cache-apt-packages: true
- name: Run tests (with 1 retry)
uses: nick-fields/retry@v4
with:
# Re-measured on master run 32758898367 (the last green one before
# the budget bit): a clean JRuby attempt is now 652-881s, not the
# 545-713s this was set from on run 32643567648. The suite grew from
# 646 to 757 runs in between, so 881s against a 900s cap left 2%
# headroom, and the jruby-10.0/rails80 cell duly timed out on run
# 32763256451 -- with tests still printing dots when SIGTERM landed,
# so it was slow, not hung.
#
# 20 min restores ~26% over the slowest attempt, which is the margin
# this was originally sized for.
#
# MRI then did the same thing one release later, which is why it is 4
# and not 3. "MRI is 128s against 3 min" was true when written; measured
# on run 32770212615 the attempts are 78-146s, so the slowest had 23%
# left and `3.3/rails81` (144s) duly timed out on run 32889000873 --
# again with tests still printing dots when SIGTERM landed. 4 min puts
# the slowest back to ~64%.
#
# The lesson both times: these numbers are measurements, not settings.
# When one bites, RE-MEASURE and raise BOTH it and the job cap -- a
# per-attempt timeout that does not fit the cap reports `cancelled`,
# which reads as an absence rather than a failure.
timeout_minutes: ${{ contains(matrix.ruby-version, 'jruby') && 20 || 4 }}
# Two, not three. A third attempt never fit inside the job cap on
# either engine (JRuby 3x15=45 > 25; MRI 3x3=9 > 8), so it only ever
# got killed partway and reported the cell as `cancelled`. Keeping
# the retry budget inside the cap is what makes the cell a real gate.
#
# The retry exists solely for the intermittent JRuby teardown hang
# (#244), which strands the process for minutes *after* the suite
# prints `Finished in ...`. Once #244 is fixed, drop this to a single
# attempt and the JRuby cap to 16 -- that halves the JRuby bill.
max_attempts: 2
command: bin/rake test
matrix-screenshot-driver:
name: Test Drivers
# Cost-intentional: full matrix stays off PRs by default (free-tier
# Actions minutes). Runs on master pushes, manual dispatch, the weekly
# scheduled drift check, and PRs opted in via the 'full-ci' label.
if: >
github.ref == 'refs/heads/master' ||
github.event_name == 'workflow_dispatch' ||
github.event_name == 'schedule' ||
contains(github.event.pull_request.labels.*.name, 'full-ci')
needs: [ 'functional-test' ]
strategy:
matrix:
capybara-driver: [ cuprite ]
screenshot-driver: [ vips, chunky_png ]
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v7
- uses: ./.github/actions/setup-ruby-and-dependencies
with:
ruby-version: "4.0"
cache-apt-packages: ${{ matrix.screenshot-driver == 'vips' }}
- name: Cache Selenium
uses: actions/cache@v6
with:
path: ~/.cache/selenium
key: ${{ runner.os }}-selenium-${{ matrix.capybara-driver }}
- run: bin/rake test:integration
env:
CAPYBARA_DRIVER: ${{ matrix.capybara-driver }}
SCREENSHOT_DRIVER: ${{ matrix.screenshot-driver }}
- uses: ./.github/actions/upload-screenshots
if: failure()
with:
name: screenshots-${{ matrix.capybara-driver }}-${{ matrix.screenshot-driver }}
test-report-upload:
name: Test Report Upload
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
- uses: ./.github/actions/setup-ruby-and-dependencies
with:
ruby-version: "4.0"
cache-apt-packages: true
- name: Generate sample report
run: bin/rake 'report:sample[embed]'
- uses: ./.github/actions/upload-screenshots
with:
name: test-report