From 6491187e06cf1b83510c6e230396e3b2b07a32cb Mon Sep 17 00:00:00 2001 From: Isaac Good Date: Wed, 13 May 2026 16:08:56 -0700 Subject: [PATCH] Update Ruby 3.2.2 -> 4.0.4 * Use alpine 3.23 with a pinned hash. * Make Gemfile and Dockerfile uniform across Ruby repos to allow shared layers. --- .ruby-version | 2 +- Dockerfile | 12 +- Gemfile | 32 +++-- Gemfile.lock | 118 ++++++++++++------ lib/extract_metadata.rb | 6 +- test/test_helper.rb | 1 - tests/deep_exception/expected_results.json | 2 +- tests/empty/expected_results.json | 2 +- tests/exception/expected_results.json | 2 +- tests/metadata/expected_results.json | 2 +- .../expected_results.json | 2 +- .../expected_results.json | 2 +- 12 files changed, 107 insertions(+), 76 deletions(-) diff --git a/.ruby-version b/.ruby-version index 03463f3..8b52f98 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -ruby-3.3.0 +ruby-4.0.3 diff --git a/Dockerfile b/Dockerfile index 9d0a84a..75d69ca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,22 +1,22 @@ -FROM ruby:3.3.0-alpine3.18 AS build +FROM ruby:4.0.4-alpine3.23@sha256:ac9c68cd41d6a49a9138fca74aa344968e8ddb5e20d8a3b1f455b97c7148f8da AS build RUN apk update && apk upgrade && \ apk add --no-cache git openssh build-base gcc wget git -COPY Gemfile Gemfile.lock . +COPY Gemfile Gemfile.lock ./ -RUN gem install bundler -v "$(grep -A 1 "BUNDLED WITH" Gemfile.lock | tail -n 1)" && \ +RUN gem install bundler:4.0.11 && \ bundle config set without 'development test' && \ bundle install -FROM ruby:3.3.0-alpine3.18 +FROM ruby:4.0.4-alpine3.23@sha256:ac9c68cd41d6a49a9138fca74aa344968e8ddb5e20d8a3b1f455b97c7148f8da AS runtime RUN apk add --no-cache bash -WORKDIR /opt/test-runner - COPY --from=build /usr/local/bundle /usr/local/bundle +WORKDIR /opt/test-runner + COPY . . ENTRYPOINT [ "sh", "/opt/test-runner/bin/run.sh" ] diff --git a/Gemfile b/Gemfile index 9a4bb70..bf8120d 100644 --- a/Gemfile +++ b/Gemfile @@ -1,26 +1,22 @@ source 'https://rubygems.org' git_source(:github) do |repo_name| - repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") + repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?('/') "https://github.com/#{repo_name}.git" end -ruby '3.3.0' - -gem "mandate", "~> 1.0.0" -gem 'rake' +gem 'activesupport' +gem 'json' +gem 'mandate' +gem 'minitest' +gem 'minitest-stub-const' +gem 'mocha' gem 'mutex_m' -gem 'json', '~> 2.6.1' -gem 'minitest', '~> 5.11.3' - -gem 'parser' +gem 'pry' +gem 'rake' +gem 'rubocop' gem 'rubocop-ast' - -group :test do - gem 'mocha' - gem 'pry' - gem 'simplecov', '~> 0.17.0' - gem 'rubocop' - gem 'rubocop-minitest' - gem 'rubocop-performance' -end +gem 'rubocop-minitest' +gem 'rubocop-performance' +gem 'sexp_processor' +gem 'simplecov', '~> 0.17.0' diff --git a/Gemfile.lock b/Gemfile.lock index 3818b72..645f69a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,77 +1,115 @@ GEM remote: https://rubygems.org/ specs: - ast (2.4.2) + activesupport (8.1.3) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + json + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) + ast (2.4.3) + base64 (0.3.0) + bigdecimal (4.1.2) coderay (1.1.3) - docile (1.4.0) - json (2.6.3) - language_server-protocol (3.17.0.3) - mandate (1.0.0) - method_source (1.0.0) - minitest (5.11.3) - mocha (2.1.0) + concurrent-ruby (1.3.6) + connection_pool (3.0.2) + docile (1.4.1) + drb (2.2.3) + i18n (1.14.8) + concurrent-ruby (~> 1.0) + io-console (0.8.2) + json (2.19.5) + language_server-protocol (3.17.0.5) + lint_roller (1.1.0) + logger (1.7.0) + mandate (2.2.0) + method_source (1.1.0) + minitest (6.0.6) + drb (~> 2.0) + prism (~> 1.5) + minitest-stub-const (0.6) + mocha (3.1.0) ruby2_keywords (>= 0.0.5) - mutex_m (0.2.0) - parallel (1.24.0) - parser (3.3.0.5) + mutex_m (0.3.0) + parallel (2.1.0) + parser (3.3.11.1) ast (~> 2.4.1) racc - pry (0.14.2) + prism (1.9.0) + pry (0.16.0) coderay (~> 1.1) method_source (~> 1.0) - racc (1.7.3) + reline (>= 0.6.0) + racc (1.8.1) rainbow (3.1.1) - rake (13.1.0) - regexp_parser (2.9.0) - rexml (3.4.2) - rubocop (1.61.0) + rake (13.4.2) + regexp_parser (2.12.0) + reline (0.6.3) + io-console (~> 0.5) + rubocop (1.86.2) json (~> 2.3) - language_server-protocol (>= 3.17.0) - parallel (~> 1.10) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) + parallel (>= 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.30.0, < 2.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.49.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.31.1) - parser (>= 3.3.0.4) - rubocop-minitest (0.34.5) - rubocop (>= 1.39, < 2.0) - rubocop-ast (>= 1.30.0, < 2.0) - rubocop-performance (1.20.2) - rubocop (>= 1.48.1, < 2.0) - rubocop-ast (>= 1.30.0, < 2.0) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.49.1) + parser (>= 3.3.7.2) + prism (~> 1.7) + rubocop-minitest (0.39.1) + lint_roller (~> 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.38.0, < 2.0) + rubocop-performance (1.26.1) + lint_roller (~> 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.47.1, < 2.0) ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) + securerandom (0.4.1) + sexp_processor (4.17.5) simplecov (0.17.1) docile (~> 1.1) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.2) - unicode-display_width (2.5.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.2.0) + uri (1.1.1) PLATFORMS ruby DEPENDENCIES - json (~> 2.6.1) - mandate (~> 1.0.0) - minitest (~> 5.11.3) + activesupport + json + mandate + minitest + minitest-stub-const mocha mutex_m - parser pry rake rubocop rubocop-ast rubocop-minitest rubocop-performance + sexp_processor simplecov (~> 0.17.0) -RUBY VERSION - ruby 3.3.0p0 - BUNDLED WITH - 2.5.3 + 4.0.11 diff --git a/lib/extract_metadata.rb b/lib/extract_metadata.rb index 67c1d8e..89bb1ae 100644 --- a/lib/extract_metadata.rb +++ b/lib/extract_metadata.rb @@ -1,5 +1,5 @@ require 'rubocop/ast' -require 'parser/current' +require 'prism' # This class takes a test file, gets its AST, and # looks for tests (def test_...). Each of these then @@ -14,9 +14,7 @@ class ExtractMetadata < Parser::AST::Processor def call @filelines = File.readlines(filepath) @num_tests = 0 - buffer = Parser::Source::Buffer.new('', source: filelines.join) - builder = RuboCop::AST::Builder.new - ast = Parser::CurrentRuby.new(builder).parse(buffer) + ast = Prism::Translation::RubyParser.parse_file(filepath) @tests = [] diff --git a/test/test_helper.rb b/test/test_helper.rb index 3b5195a..a2033c5 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -13,7 +13,6 @@ require 'securerandom' require 'json' require 'English' -require 'pathname' class Minitest::Test SAFE_WRITE_PATH = Pathname.new('/tmp') diff --git a/tests/deep_exception/expected_results.json b/tests/deep_exception/expected_results.json index 0a407e2..fe38ed7 100644 --- a/tests/deep_exception/expected_results.json +++ b/tests/deep_exception/expected_results.json @@ -1 +1 @@ -{"version":2,"status":"fail","message":null,"tests":[{"name":"No name given","test_code":"assert_equal \"One for you, one for me.\", TwoFer.two_fer","status":"error","message":"NoMethodError: undefined method `non_existant_method' for nil\n\nTraceback (most recent call first):\n Line 8:in `work_out_name'\n Line 3:in `two_fer'"}]} +{"version":2,"status":"fail","message":null,"tests":[{"name":"No name given","test_code":"assert_equal \"One for you, one for me.\", TwoFer.two_fer","status":"error","message":"NoMethodError: undefined method 'non_existant_method' for nil\n\nTraceback (most recent call first):\n"}]} diff --git a/tests/empty/expected_results.json b/tests/empty/expected_results.json index c880a92..9ae14af 100644 --- a/tests/empty/expected_results.json +++ b/tests/empty/expected_results.json @@ -1 +1 @@ -{"version":2,"status":"fail","message":null,"tests":[{"name":"No name given","test_code":"assert_equal \"One for you, one for me.\", TwoFer.two_fer","status":"error","message":"NameError: uninitialized constant TwoFerTest::TwoFer\n\nTraceback (most recent call first):\n Line 7:in `test_no_name_given'"}]} +{"version":2,"status":"fail","message":null,"tests":[{"name":"No name given","test_code":"assert_equal \"One for you, one for me.\", TwoFer.two_fer","status":"error","message":"NameError: uninitialized constant TwoFerTest::TwoFer\n\nTraceback (most recent call first):\n"}]} diff --git a/tests/exception/expected_results.json b/tests/exception/expected_results.json index a01c2a1..1ecfbf1 100644 --- a/tests/exception/expected_results.json +++ b/tests/exception/expected_results.json @@ -1 +1 @@ -{"version":2,"status":"error","message":"Line 3: undefined local variable or method `raise_an_error_because_i_am_a_random_method' for main (NameError)","tests":null} +{"version":2,"status":"error","message":"Line 3: undefined local variable or method 'raise_an_error_because_i_am_a_random_method' for main (NameError)","tests":null} diff --git a/tests/metadata/expected_results.json b/tests/metadata/expected_results.json index 2d4567c..de837aa 100644 --- a/tests/metadata/expected_results.json +++ b/tests/metadata/expected_results.json @@ -1 +1 @@ -{"version":2,"status":"fail","message":null,"tests":[{"name":"Assert equal works properly","test_code":"some_result = TwoFer.two_fer\nassert_equal \"One for you, one for me.\", some_result","status":"error","message":"NameError: uninitialized constant SomeTest::TwoFer\n\nTraceback (most recent call first):\n Line 3:in `test_assert_equal_works_properly'"},{"name":"Skip works properly","test_code":"something = \"Something\"\nassert something.present?","status":"error","message":"NoMethodError: undefined method `present?' for an instance of String\n\nTraceback (most recent call first):\n Line 5:in `test_skip_works_properly'"},{"name":"Zebra","test_code":"some_result = TwoFer.two_fer(\"zebra\")\nassert_equal \"One for you, one for zebra.\", some_result","status":"error","message":"NameError: uninitialized constant SomeTest::TwoFer\n\nTraceback (most recent call first):\n Line 3:in `test_zebra'"},{"name":"Anaconda","test_code":"some_result = TwoFer.two_fer(\"anaconda\")\nassert_equal \"One for you, one for anaconda.\", some_result","status":"error","message":"NameError: uninitialized constant SomeTest::TwoFer\n\nTraceback (most recent call first):\n Line 8:in `test_anaconda'"},{"name":"Gorilla","test_code":"some_result = TwoFer.two_fer(\"gorilla\")\nassert_equal \"One for you, one for gorilla.\", some_result","status":"error","message":"NameError: uninitialized constant SomeTest::TwoFer\n\nTraceback (most recent call first):\n Line 13:in `test_gorilla'"},{"name":"Boa","test_code":"some_result = TwoFer.two_fer(\"boa\")\nassert_equal \"One for you, one for boa.\", some_result","status":"error","message":"NameError: uninitialized constant SomeTest::TwoFer\n\nTraceback (most recent call first):\n Line 18:in `test_boa'"}]} +{"version":2,"status":"fail","message":null,"tests":[{"name":"Assert equal works properly","test_code":"some_result = TwoFer.two_fer\nassert_equal \"One for you, one for me.\", some_result","status":"error","message":"NameError: uninitialized constant SomeTest::TwoFer\n\nTraceback (most recent call first):\n"},{"name":"Skip works properly","test_code":"something = \"Something\"\nassert something.present?","status":"error","message":"NoMethodError: undefined method 'present?' for an instance of String\n\nTraceback (most recent call first):\n"},{"name":"Zebra","test_code":"some_result = TwoFer.two_fer(\"zebra\")\nassert_equal \"One for you, one for zebra.\", some_result","status":"error","message":"NameError: uninitialized constant SomeTest::TwoFer\n\nTraceback (most recent call first):\n"},{"name":"Anaconda","test_code":"some_result = TwoFer.two_fer(\"anaconda\")\nassert_equal \"One for you, one for anaconda.\", some_result","status":"error","message":"NameError: uninitialized constant SomeTest::TwoFer\n\nTraceback (most recent call first):\n"},{"name":"Gorilla","test_code":"some_result = TwoFer.two_fer(\"gorilla\")\nassert_equal \"One for you, one for gorilla.\", some_result","status":"error","message":"NameError: uninitialized constant SomeTest::TwoFer\n\nTraceback (most recent call first):\n"},{"name":"Boa","test_code":"some_result = TwoFer.two_fer(\"boa\")\nassert_equal \"One for you, one for boa.\", some_result","status":"error","message":"NameError: uninitialized constant SomeTest::TwoFer\n\nTraceback (most recent call first):\n"}]} diff --git a/tests/syntax_error_in_code/expected_results.json b/tests/syntax_error_in_code/expected_results.json index 66ea13d..a7de11e 100644 --- a/tests/syntax_error_in_code/expected_results.json +++ b/tests/syntax_error_in_code/expected_results.json @@ -1 +1 @@ -{"version":2,"status":"error","message":"Line 2: syntax error, unexpected ',', expecting end-of-input\nend,A stray comma\n ^\n","tests":null} +{"version":2,"status":"error","message":"Line 2: syntax errors found\n 1 | module TwoFer\n> 2 | end,A stray comma\n | ^ unexpected ',', ignoring it\n | ^ unexpected ',', expecting end-of-input\n","tests":null} diff --git a/tests/syntax_error_in_tests/expected_results.json b/tests/syntax_error_in_tests/expected_results.json index 53422dd..1a3c78c 100644 --- a/tests/syntax_error_in_tests/expected_results.json +++ b/tests/syntax_error_in_tests/expected_results.json @@ -1 +1 @@ -{"version":2,"status":"error","message":"Line 3: syntax error, unexpected ','\n,'This is meant to be a syntax...\n^\n","tests":null} +{"version":2,"status":"error","message":"Line 3: syntax error found\n 1 | require 'minitest/autorun'\n 2 | \n> 3 | ,'This is meant to be a syntax ...\n | ^ unexpected ',', ignoring it\n","tests":null}