ci: one JRuby cell instead of five #1550
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| 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 |