From fe8c66b822a6e3fb03e02d89b7ad9b81049f94fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20V=C3=A1squez?= Date: Wed, 23 Sep 2026 14:02:49 -0600 Subject: [PATCH 1/5] Step 1: Add a benchmarks-ok check that can be required A PR with a failing benchmark could still be merged: main has no required checks. Requiring the benchmark jobs themselves would block docs-only PRs forever, because the paths filter skipped the whole workflow and a required check that never reports stays "Expected". The workflow now always runs. A changes job decides once what to benchmark (the same rules as before, moved out of each matrix job), the benchmark jobs run only when needed, and benchmarks-ok always reports: it passes when they passed or were skipped. That is the check to require on main. --- .github/workflows/benchmarks.yml | 97 +++++++++++++++++++++----------- 1 file changed, 63 insertions(+), 34 deletions(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 7c90408..f464ff3 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -1,22 +1,59 @@ name: Benchmarks -# Only changes that can affect a benchmark run it: README-only changes do not. +# Always runs, so the benchmarks-ok check at the end always reports and can be +# required. The changes job decides whether there is anything to benchmark. on: push: branches: [ main ] - paths: &benchmark_paths - - 'code/**/*.rb' - - 'Gemfile' - - 'Rakefile' - - 'compose.yaml' - - 'docker/**' - - '.github/workflows/benchmarks.yml' pull_request: branches: [ main ] - paths: *benchmark_paths jobs: + changes: + runs-on: ubuntu-latest + outputs: + run: ${{ steps.pick.outputs.run }} + files: ${{ steps.pick.outputs.files }} + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + # A pull request runs only the benchmark files it changes, unless it + # changes something every benchmark depends on. A push to main runs + # everything, if anything that affects benchmarks changed. + - name: Pick benchmarks + id: pick + env: + # A pull_request checks out a merge commit: HEAD^1 is the base. + BASE: ${{ github.event_name == 'pull_request' && 'HEAD^1' || github.event.before }} + run: | + if ! git cat-file -e "$BASE^{commit}" 2>/dev/null; then + echo "Base $BASE not found, running every benchmark" + echo "run=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + changed=$(git diff --name-only "$BASE" HEAD) + files=$(git diff --name-only --diff-filter=d "$BASE" HEAD -- 'code/**/*.rb' | tr '\n' ' ') + if echo "$changed" | grep -qE '^(Gemfile|Rakefile|compose\.yaml|docker/|\.github/workflows/benchmarks\.yml)'; then + echo "Shared files changed, running every benchmark" + echo "run=true" >> "$GITHUB_OUTPUT" + elif [ -z "$files" ]; then + # Docs only, or a PR that only deletes a benchmark. + echo "No benchmark to run" + echo "run=false" >> "$GITHUB_OUTPUT" + elif [ "$GITHUB_EVENT_NAME" = pull_request ]; then + echo "Running: $files" + echo "run=true" >> "$GITHUB_OUTPUT" + echo "files=$files" >> "$GITHUB_OUTPUT" + else + echo "Benchmarks changed, running every benchmark" + echo "run=true" >> "$GITHUB_OUTPUT" + fi + rake: + needs: changes + if: needs.changes.outputs.run == 'true' runs-on: ubuntu-latest timeout-minutes: 60 @@ -38,36 +75,28 @@ jobs: run: | echo "SHARE=1" >> "$GITHUB_ENV" - uses: actions/checkout@v4 - with: - # A pull_request checks out a merge commit: HEAD^1 is the base. - fetch-depth: 2 - # On a pull request, run only the benchmark files it changes, unless it - # changes something every benchmark depends on. Pushes to main run all. - - name: Pick benchmarks - id: pick - if: github.event_name == 'pull_request' - run: | - changed=$(git diff --name-only HEAD^1 HEAD) - if echo "$changed" | grep -qE '^(Gemfile|Rakefile|compose\.yaml|docker/|\.github/workflows/benchmarks\.yml)'; then - echo "Shared files changed, running every benchmark" - else - files=$(git diff --name-only --diff-filter=d HEAD^1 HEAD -- 'code/**/*.rb' | tr '\n' ' ') - if [ -z "$files" ]; then - # For example a PR that only deletes a benchmark. - echo "No benchmark to run" - echo "skip=true" >> "$GITHUB_OUTPUT" - else - echo "Running: $files" - echo "files=$files" >> "$GITHUB_OUTPUT" - fi - fi - name: Run benchmarks on ${{ matrix.ruby }} - if: steps.pick.outputs.skip != 'true' env: - FILES: ${{ steps.pick.outputs.files }} + FILES: ${{ needs.changes.outputs.files }} run: | # Unquoted on purpose: one argument per file. set -f keeps names like # dig-vs-[]-vs-fetch.rb from being read as glob patterns. set -f # shellcheck disable=SC2086 docker compose run --rm -T ${{ matrix.ruby }} $FILES + + # The check to require on main. Passes when every benchmark job passed, or + # when there was nothing to benchmark. + benchmarks-ok: + needs: [changes, rake] + if: always() + runs-on: ubuntu-latest + + steps: + - name: Check the benchmark jobs + env: + CHANGES: ${{ needs.changes.result }} + RAKE: ${{ needs.rake.result }} + run: | + echo "changes: $CHANGES, rake: $RAKE" + [ "$CHANGES" = success ] && { [ "$RAKE" = success ] || [ "$RAKE" = skipped ]; } From 07d08bc57b7f27fad345da2b42837239e5c0031e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20V=C3=A1squez?= Date: Wed, 23 Sep 2026 14:04:42 -0600 Subject: [PATCH 2/5] Step 2: Say how to fix a benchmark that breaks on older Rubies A failed run listed the files but not what to do. Both the Rakefile and the run script now point to a new CONTRIBUTING section: skip one report or the whole file with `if RUBY_VERSION >= ...`, and write new syntax (like <<~) in a form older Rubies can parse, since an `if` cannot help when the file does not parse. --- CONTRIBUTING.md | 30 ++++++++++++++++++++++++++++++ Rakefile | 6 +++++- docker/run-benchmarks.sh | 3 +++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 843b17c..e24804a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,6 +34,36 @@ Run your result: $ ruby -v code/your-new/entry.rb ``` +### Benchmarks that need a newer Ruby + +CI runs every benchmark on every Ruby in `compose.yaml`, back to Ruby 2.1, and +fails when one crashes. If your entry uses something older Rubies do not have, +make it skip them. + +Skip one report, so the rest still run everywhere: + +```ruby +Benchmark.ips do |x| + x.report('String#delete_suffix') { fast } if RUBY_VERSION >= '2.5.0' + x.report('String#sub') { slow } + x.compare! +end +``` + +Skip the whole file when nothing in it makes sense without the feature: + +```ruby +if RUBY_VERSION >= '2.5.0' + # everything, including Benchmark.ips +end +``` + +New syntax (for example `<<~` before 2.3) cannot be skipped this way: older +Rubies fail to parse the file before the `if` runs. Write it with syntax they +understand instead. + +To check an entry on an older Ruby, see Docker below. + To run it on a Ruby you don't have installed, use Docker. There is one service per Ruby in the CI matrix (see `compose.yaml`): diff --git a/Rakefile b/Rakefile index a040a1e..222a90e 100644 --- a/Rakefile +++ b/Rakefile @@ -12,7 +12,11 @@ task :run_benchmark do failed << benchmark unless system("ruby", "-v", "-W0", benchmark) end - abort "Failed benchmarks:\n#{failed.join("\n")}" unless failed.empty? + unless failed.empty? + abort "Failed benchmarks:\n#{failed.join("\n")}\n\n" \ + "If a benchmark needs a newer Ruby, make it skip older ones, see " \ + "\"Benchmarks that need a newer Ruby\" in CONTRIBUTING.md." + end end task default: :run_benchmark diff --git a/docker/run-benchmarks.sh b/docker/run-benchmarks.sh index 61cb1e8..514efac 100755 --- a/docker/run-benchmarks.sh +++ b/docker/run-benchmarks.sh @@ -23,5 +23,8 @@ done if [ -n "$failed" ]; then echo "Failed benchmarks:$failed" >&2 + echo >&2 + echo "If a benchmark needs a newer Ruby, make it skip older ones, see" \ + "\"Benchmarks that need a newer Ruby\" in CONTRIBUTING.md." >&2 exit 1 fi From d42903c9ce1f82b5443922e59b5d712a49a897f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20V=C3=A1squez?= Date: Wed, 23 Sep 2026 14:06:30 -0600 Subject: [PATCH 3/5] Step 3: Show failed benchmarks on the pull request A failure was only visible by opening each of the 18 job logs. When a benchmark job fails, it now adds an annotation per failed file, shown on the PR's changed files, and a list on the run's summary page, both pointing to the CONTRIBUTING section on newer-Ruby benchmarks. The run step uses `shell: bash` so pipefail is on and tee cannot turn a failing run into a passing step. --- .github/workflows/benchmarks.yml | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index f464ff3..7174055 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -76,6 +76,8 @@ jobs: echo "SHARE=1" >> "$GITHUB_ENV" - uses: actions/checkout@v4 - name: Run benchmarks on ${{ matrix.ruby }} + # Explicit bash turns on pipefail, so tee does not hide a failure. + shell: bash env: FILES: ${{ needs.changes.outputs.files }} run: | @@ -83,7 +85,29 @@ jobs: # dig-vs-[]-vs-fetch.rb from being read as glob patterns. set -f # shellcheck disable=SC2086 - docker compose run --rm -T ${{ matrix.ruby }} $FILES + docker compose run --rm -T ${{ matrix.ruby }} $FILES 2>&1 | tee benchmarks.log + # One annotation per failed benchmark, shown on the PR's changed files, + # and a list on the run's summary page. + - name: Report failed benchmarks + if: failure() + env: + RUBY_SERVICE: ${{ matrix.ruby }} + run: | + failed=$(sed -n '/^Failed benchmarks:/,$p' benchmarks.log | grep -E '^code/.+\.rb$' || true) + [ -n "$failed" ] || exit 0 + hint='If it needs a newer Ruby, make it skip older ones, see "Benchmarks that need a newer Ruby" in CONTRIBUTING.md.' + { + echo "### Benchmarks that failed on $RUBY_SERVICE" + echo + echo "$failed" | while read -r file; do echo "- \`$file\`"; done + echo + echo "$hint" + } >> "$GITHUB_STEP_SUMMARY" + echo "$failed" | while read -r file; do + # Annotation properties cannot contain raw % , or : + escaped=$(printf '%s' "$file" | sed 's/%/%25/g; s/,/%2C/g; s/:/%3A/g') + echo "::error file=$escaped,title=Fails on $RUBY_SERVICE::Crashed on $RUBY_SERVICE. $hint" + done # The check to require on main. Passes when every benchmark job passed, or # when there was nothing to benchmark. From 8954b0c038b4bb8d880acea6582452cea0d1567b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20V=C3=A1squez?= Date: Wed, 23 Sep 2026 14:34:36 -0600 Subject: [PATCH 4/5] Step 4: Move the workflow's shell code into .github/scripts The workflow had grown long inline scripts. Each one is now a file the workflow calls, so the YAML shows what runs and when, and the scripts can be shellchecked on their own: - pick-benchmarks.sh: what a run benchmarks (was the changes job step) - run-benchmarks.sh: runs one Ruby's job and keeps its log - report-failures.sh: annotations and summary for failed benchmarks - check-benchmarks.sh: the benchmarks-ok check A change under .github/scripts/ now runs every benchmark, like a change to the workflow. run-benchmarks.sh sets pipefail itself, so the run step no longer needs `shell: bash`. --- .github/scripts/check-benchmarks.sh | 7 +++ .github/scripts/pick-benchmarks.sh | 35 +++++++++++++++ .github/scripts/report-failures.sh | 27 +++++++++++ .github/scripts/run-benchmarks.sh | 11 +++++ .github/workflows/benchmarks.yml | 69 +++-------------------------- 5 files changed, 86 insertions(+), 63 deletions(-) create mode 100755 .github/scripts/check-benchmarks.sh create mode 100755 .github/scripts/pick-benchmarks.sh create mode 100755 .github/scripts/report-failures.sh create mode 100755 .github/scripts/run-benchmarks.sh diff --git a/.github/scripts/check-benchmarks.sh b/.github/scripts/check-benchmarks.sh new file mode 100755 index 0000000..d5bebb8 --- /dev/null +++ b/.github/scripts/check-benchmarks.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# The benchmarks-ok check. Passes when every benchmark job passed, or when +# there was nothing to benchmark. Usage: check-benchmarks.sh +set -e + +echo "changes: $1, rake: $2" +[ "$1" = success ] && { [ "$2" = success ] || [ "$2" = skipped ]; } diff --git a/.github/scripts/pick-benchmarks.sh b/.github/scripts/pick-benchmarks.sh new file mode 100755 index 0000000..874030b --- /dev/null +++ b/.github/scripts/pick-benchmarks.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# Decides what a CI run benchmarks, and writes `run` and `files` to +# $GITHUB_OUTPUT. Usage: pick-benchmarks.sh +# +# A pull request runs only the benchmark files it changes, unless it changes +# something every benchmark depends on. A push to main runs everything, if +# anything that affects benchmarks changed. +set -e + +base=$1 + +if ! git cat-file -e "$base^{commit}" 2>/dev/null; then + echo "Base $base not found, running every benchmark" + echo "run=true" >> "$GITHUB_OUTPUT" + exit 0 +fi + +changed=$(git diff --name-only "$base" HEAD) +files=$(git diff --name-only --diff-filter=d "$base" HEAD -- 'code/**/*.rb' | tr '\n' ' ') + +if echo "$changed" | grep -qE '^(Gemfile|Rakefile|compose\.yaml|docker/|\.github/workflows/benchmarks\.yml|\.github/scripts/)'; then + echo "Shared files changed, running every benchmark" + echo "run=true" >> "$GITHUB_OUTPUT" +elif [ -z "$files" ]; then + # Docs only, or a PR that only deletes a benchmark. + echo "No benchmark to run" + echo "run=false" >> "$GITHUB_OUTPUT" +elif [ "$GITHUB_EVENT_NAME" = pull_request ]; then + echo "Running: $files" + echo "run=true" >> "$GITHUB_OUTPUT" + echo "files=$files" >> "$GITHUB_OUTPUT" +else + echo "Benchmarks changed, running every benchmark" + echo "run=true" >> "$GITHUB_OUTPUT" +fi diff --git a/.github/scripts/report-failures.sh b/.github/scripts/report-failures.sh new file mode 100755 index 0000000..c52c97e --- /dev/null +++ b/.github/scripts/report-failures.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# After a failed benchmark job: one annotation per failed benchmark, shown on +# the PR's changed files, and a list on the run's summary page. +# Usage: report-failures.sh +set -e + +log=$1 +ruby=$2 + +failed=$(sed -n '/^Failed benchmarks:/,$p' "$log" | grep -E '^code/.+\.rb$' || true) +[ -n "$failed" ] || exit 0 + +hint='If it needs a newer Ruby, make it skip older ones, see "Benchmarks that need a newer Ruby" in CONTRIBUTING.md.' + +{ + echo "### Benchmarks that failed on $ruby" + echo + echo "$failed" | while read -r file; do echo "- \`$file\`"; done + echo + echo "$hint" +} >> "$GITHUB_STEP_SUMMARY" + +echo "$failed" | while read -r file; do + # Annotation properties cannot contain raw % , or : + escaped=$(printf '%s' "$file" | sed 's/%/%25/g; s/,/%2C/g; s/:/%3A/g') + echo "::error file=$escaped,title=Fails on $ruby::Crashed on $ruby. $hint" +done diff --git a/.github/scripts/run-benchmarks.sh b/.github/scripts/run-benchmarks.sh new file mode 100755 index 0000000..74e4607 --- /dev/null +++ b/.github/scripts/run-benchmarks.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# Runs one Ruby's benchmark job and keeps its output in benchmarks.log for +# report-failures.sh. Usage: FILES="..." run-benchmarks.sh +# Without FILES, every benchmark runs. +set -eo pipefail + +# Unquoted on purpose: one argument per file. set -f keeps names like +# dig-vs-[]-vs-fetch.rb from being read as glob patterns. +set -f +# shellcheck disable=SC2086 +docker compose run --rm -T "$1" $FILES 2>&1 | tee benchmarks.log diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 7174055..a6c9b80 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -19,37 +19,10 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - # A pull request runs only the benchmark files it changes, unless it - # changes something every benchmark depends on. A push to main runs - # everything, if anything that affects benchmarks changed. - name: Pick benchmarks id: pick - env: - # A pull_request checks out a merge commit: HEAD^1 is the base. - BASE: ${{ github.event_name == 'pull_request' && 'HEAD^1' || github.event.before }} - run: | - if ! git cat-file -e "$BASE^{commit}" 2>/dev/null; then - echo "Base $BASE not found, running every benchmark" - echo "run=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - changed=$(git diff --name-only "$BASE" HEAD) - files=$(git diff --name-only --diff-filter=d "$BASE" HEAD -- 'code/**/*.rb' | tr '\n' ' ') - if echo "$changed" | grep -qE '^(Gemfile|Rakefile|compose\.yaml|docker/|\.github/workflows/benchmarks\.yml)'; then - echo "Shared files changed, running every benchmark" - echo "run=true" >> "$GITHUB_OUTPUT" - elif [ -z "$files" ]; then - # Docs only, or a PR that only deletes a benchmark. - echo "No benchmark to run" - echo "run=false" >> "$GITHUB_OUTPUT" - elif [ "$GITHUB_EVENT_NAME" = pull_request ]; then - echo "Running: $files" - echo "run=true" >> "$GITHUB_OUTPUT" - echo "files=$files" >> "$GITHUB_OUTPUT" - else - echo "Benchmarks changed, running every benchmark" - echo "run=true" >> "$GITHUB_OUTPUT" - fi + # A pull_request checks out a merge commit: HEAD^1 is the base. + run: .github/scripts/pick-benchmarks.sh "${{ github.event_name == 'pull_request' && 'HEAD^1' || github.event.before }}" rake: needs: changes @@ -76,38 +49,12 @@ jobs: echo "SHARE=1" >> "$GITHUB_ENV" - uses: actions/checkout@v4 - name: Run benchmarks on ${{ matrix.ruby }} - # Explicit bash turns on pipefail, so tee does not hide a failure. - shell: bash env: FILES: ${{ needs.changes.outputs.files }} - run: | - # Unquoted on purpose: one argument per file. set -f keeps names like - # dig-vs-[]-vs-fetch.rb from being read as glob patterns. - set -f - # shellcheck disable=SC2086 - docker compose run --rm -T ${{ matrix.ruby }} $FILES 2>&1 | tee benchmarks.log - # One annotation per failed benchmark, shown on the PR's changed files, - # and a list on the run's summary page. + run: .github/scripts/run-benchmarks.sh ${{ matrix.ruby }} - name: Report failed benchmarks if: failure() - env: - RUBY_SERVICE: ${{ matrix.ruby }} - run: | - failed=$(sed -n '/^Failed benchmarks:/,$p' benchmarks.log | grep -E '^code/.+\.rb$' || true) - [ -n "$failed" ] || exit 0 - hint='If it needs a newer Ruby, make it skip older ones, see "Benchmarks that need a newer Ruby" in CONTRIBUTING.md.' - { - echo "### Benchmarks that failed on $RUBY_SERVICE" - echo - echo "$failed" | while read -r file; do echo "- \`$file\`"; done - echo - echo "$hint" - } >> "$GITHUB_STEP_SUMMARY" - echo "$failed" | while read -r file; do - # Annotation properties cannot contain raw % , or : - escaped=$(printf '%s' "$file" | sed 's/%/%25/g; s/,/%2C/g; s/:/%3A/g') - echo "::error file=$escaped,title=Fails on $RUBY_SERVICE::Crashed on $RUBY_SERVICE. $hint" - done + run: .github/scripts/report-failures.sh benchmarks.log ${{ matrix.ruby }} # The check to require on main. Passes when every benchmark job passed, or # when there was nothing to benchmark. @@ -117,10 +64,6 @@ jobs: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 - name: Check the benchmark jobs - env: - CHANGES: ${{ needs.changes.result }} - RAKE: ${{ needs.rake.result }} - run: | - echo "changes: $CHANGES, rake: $RAKE" - [ "$CHANGES" = success ] && { [ "$RAKE" = success ] || [ "$RAKE" = skipped ]; } + run: .github/scripts/check-benchmarks.sh ${{ needs.changes.result }} ${{ needs.rake.result }} From 59a1cc05c13e21004fa52f20a363c9dcd0c5ea65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20V=C3=A1squez?= Date: Wed, 23 Sep 2026 14:34:37 -0600 Subject: [PATCH 5/5] Step 5: Give CONTRIBUTING a contents list and one section per topic The file grew one addition at a time. The opening stays as it was; in between there is now a contents list and the sections in the order a contributor needs them: writing an entry, running it on other Rubies, and entries that need a newer Ruby. The license closes the file under its own heading. --- CONTRIBUTING.md | 51 +++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e24804a..28ffaf5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,6 +8,13 @@ These idioms list here are trying to satisfy following goals: [![GOALS](/images/Goals.png)](https://speakerdeck.com/sferik/writing-fast-ruby?slide=11) +## Contents + +- [Note on entry](#note-on-entry) +- [Running it on other Rubies](#running-it-on-other-rubies) +- [Benchmarks that need a newer Ruby](#benchmarks-that-need-a-newer-ruby) +- [License](#license) + ## Note on entry Fast code first. @@ -31,10 +38,29 @@ end Run your result: ``` -$ ruby -v code/your-new/entry.rb +ruby -v code/your-new/entry.rb ``` -### Benchmarks that need a newer Ruby +## Running it on other Rubies + +To run it on a Ruby you don't have installed, use Docker. There is one service +per Ruby in the CI matrix (see `compose.yaml`): + +``` +docker compose run --rm ruby_2.1 code/your-new/entry.rb +docker compose run --rm truffleruby_head code/your-new/entry.rb +``` + +Without a file argument, the service runs every benchmark, the same way CI does. + +The `*_head` and `truffleruby_22` images are built once and then reused, so +the head builds go stale. To get the latest nightly build: + +``` +docker compose build --no-cache ruby_head +``` + +## Benchmarks that need a newer Ruby CI runs every benchmark on every Ruby in `compose.yaml`, back to Ruby 2.1, and fails when one crashes. If your entry uses something older Rubies do not have, @@ -62,31 +88,14 @@ New syntax (for example `<<~` before 2.3) cannot be skipped this way: older Rubies fail to parse the file before the `if` runs. Write it with syntax they understand instead. -To check an entry on an older Ruby, see Docker below. - -To run it on a Ruby you don't have installed, use Docker. There is one service -per Ruby in the CI matrix (see `compose.yaml`): - -``` -docker compose run --rm ruby_2.1 code/your-new/entry.rb -docker compose run --rm truffleruby_head code/your-new/entry.rb -``` - -Without a file argument, the service runs every benchmark, the same way CI does. +To check an entry on an older Ruby, see [Running it on other Rubies](#running-it-on-other-rubies). -The `*_head` and `truffleruby_22` images are built once and then reused, so -the head builds go stale. To get the latest nightly build: - -``` -docker compose build --no-cache ruby_head -``` +## License Thanks in advance!!! Look forward to learning more from you! <3 [JuanitoFatas](https://twitter.com/juanitofatas) -###### License - The documentation is [CC BY-SA 4.0 (International)](https://github.com/JuanitoFatas/fast-ruby#license). And code will be [CC0 1.0 Universal](https://github.com/JuanitoFatas/fast-ruby#code-license).