From 0e503273135e35644528adbdc111124c8e990843 Mon Sep 17 00:00:00 2001 From: Isaac Good Date: Wed, 13 May 2026 16:08:32 -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 | 15 ++- Gemfile | 23 +++-- Gemfile.lock | 93 ++++++++++++------- bin/run-tests.sh | 12 +-- lib/analyzer.rb | 2 +- lib/analyzers/solution_representation.rb | 9 +- scripts/manual_test.rb | 4 +- test/exercises/two_fer_test.rb | 60 ++++++------ test/test_helper.rb | 9 +- .../expected_analysis.json | 0 .../two_fer.rb | 0 12 files changed, 117 insertions(+), 112 deletions(-) rename tests/two-fer/{explit_return => explicit_return}/expected_analysis.json (100%) rename tests/two-fer/{explit_return => explicit_return}/two_fer.rb (100%) 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 bf0389d..54a04ab 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,22 @@ -FROM ruby:3.2.2-alpine3.18 AS build +FROM ruby:4.0.4-alpine3.23@sha256:ac9c68cd41d6a49a9138fca74aa344968e8ddb5e20d8a3b1f455b97c7148f8da AS build RUN apk update && apk upgrade && \ - apk add --no-cache bash git openssh && \ - apk add build-base gcc wget git + apk add --no-cache git openssh build-base gcc wget git -COPY Gemfile Gemfile.lock . +COPY Gemfile Gemfile.lock ./ -RUN gem install bundler:2.4.18 && \ +RUN gem install bundler:4.0.11 && \ bundle config set without 'development test' && \ bundle install -FROM ruby:3.2.2-alpine3.18 AS runtime +FROM ruby:4.0.4-alpine3.23@sha256:ac9c68cd41d6a49a9138fca74aa344968e8ddb5e20d8a3b1f455b97c7148f8da AS runtime RUN apk add --no-cache bash -WORKDIR /opt/analyzer - COPY --from=build /usr/local/bundle /usr/local/bundle +WORKDIR /opt/analyzer + COPY . . ENTRYPOINT ["sh", "/opt/analyzer/bin/run.sh"] diff --git a/Gemfile b/Gemfile index 3b34e98..bf8120d 100644 --- a/Gemfile +++ b/Gemfile @@ -1,23 +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 +gem 'activesupport' +gem 'json' gem 'mandate' +gem 'minitest' +gem 'minitest-stub-const' +gem 'mocha' +gem 'mutex_m' +gem 'pry' gem 'rake' -gem 'json' -gem 'activesupport' - -gem 'parser' gem 'rubocop' +gem 'rubocop-ast' gem 'rubocop-minitest' gem 'rubocop-performance' - -group :test do - gem 'minitest' - gem 'minitest-stub-const' - gem 'mocha' - gem 'simplecov', '~> 0.17.0' -end +gem 'sexp_processor' +gem 'simplecov', '~> 0.17.0' diff --git a/Gemfile.lock b/Gemfile.lock index dcac903..645f69a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,67 +1,84 @@ GEM remote: https://rubygems.org/ specs: - activesupport (7.2.3.1) + activesupport (8.1.3) base64 - benchmark (>= 0.3) 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, < 6) + minitest (>= 5.1) securerandom (>= 0.3) tzinfo (~> 2.0, >= 2.0.5) - ast (2.4.2) + uri (>= 0.13.1) + ast (2.4.3) base64 (0.3.0) - benchmark (0.5.0) - bigdecimal (4.0.1) + bigdecimal (4.1.2) + coderay (1.1.3) concurrent-ruby (1.3.6) connection_pool (3.0.2) - docile (1.4.0) + docile (1.4.1) drb (2.2.3) i18n (1.14.8) concurrent-ruby (~> 1.0) - json (2.7.2) - language_server-protocol (3.17.0.3) + 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) - minitest (5.24.1) + method_source (1.1.0) + minitest (6.0.6) + drb (~> 2.0) + prism (~> 1.5) minitest-stub-const (0.6) - mocha (2.4.0) + mocha (3.1.0) ruby2_keywords (>= 0.0.5) - parallel (1.25.1) - parser (3.3.3.0) + mutex_m (0.3.0) + parallel (2.1.0) + parser (3.3.11.1) ast (~> 2.4.1) racc - racc (1.8.0) + prism (1.9.0) + pry (0.16.0) + coderay (~> 1.1) + method_source (~> 1.0) + reline (>= 0.6.0) + racc (1.8.1) rainbow (3.1.1) - rake (13.2.1) - regexp_parser (2.9.2) - rexml (3.4.2) - rubocop (1.64.1) + 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.31.1, < 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.3) - parser (>= 3.3.1.0) - rubocop-minitest (0.35.0) - rubocop (>= 1.61, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) - rubocop-performance (1.21.1) - rubocop (>= 1.48.1, < 2.0) - rubocop-ast (>= 1.31.1, < 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) @@ -69,7 +86,10 @@ GEM simplecov-html (0.10.2) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (2.5.0) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.2.0) + uri (1.1.1) PLATFORMS ruby @@ -81,12 +101,15 @@ DEPENDENCIES minitest minitest-stub-const mocha - parser + mutex_m + pry rake rubocop + rubocop-ast rubocop-minitest rubocop-performance + sexp_processor simplecov (~> 0.17.0) BUNDLED WITH - 2.4.18 + 4.0.11 diff --git a/bin/run-tests.sh b/bin/run-tests.sh index c7a0222..3b8f227 100755 --- a/bin/run-tests.sh +++ b/bin/run-tests.sh @@ -23,12 +23,10 @@ for test_dir in tests/*/*; do bin/run.sh "${test_slug}" "${test_dir_path}" "${test_dir_path}" - # echo "${test_dir_name}: comparing analysis.json to expected_analysis.json" - # diff "${results_file_path}" "${expected_results_file_path}" - - # if [ $? -ne 0 ]; then - # exit_code=1 - # fi + echo "${test_dir_name}: comparing analysis.json to expected_analysis.json" + if ! diff "${results_file_path}" "${expected_results_file_path}"; then + exit_code=1 + fi done -exit ${exit_code} +exit "${exit_code}" diff --git a/lib/analyzer.rb b/lib/analyzer.rb index af27bba..b929f54 100644 --- a/lib/analyzer.rb +++ b/lib/analyzer.rb @@ -17,7 +17,7 @@ require 'mandate' require 'json' require 'rubocop' -require 'parser/current' +require 'prism' require 'active_support/inflector' require_relative "generic/helpers" diff --git a/lib/analyzers/solution_representation.rb b/lib/analyzers/solution_representation.rb index 2e9adb5..97f98eb 100644 --- a/lib/analyzers/solution_representation.rb +++ b/lib/analyzers/solution_representation.rb @@ -43,13 +43,6 @@ def default_argument_value end def root_node - @root_node ||= begin - buffer = Parser::Source::Buffer.new(nil) - buffer.source = code_to_analyze - builder = RuboCop::AST::Builder.new - parser = Parser::CurrentRuby.new(builder) - - parser.parse(buffer) - end + @root_node ||= Prism::Translation::RubyParser.parse(code_to_analyze) end end diff --git a/scripts/manual_test.rb b/scripts/manual_test.rb index 0047374..147983e 100644 --- a/scripts/manual_test.rb +++ b/scripts/manual_test.rb @@ -1,7 +1,7 @@ $LOAD_PATH.unshift File.expand_path('../lib', __dir__) require "analyzer" -source = %q{ +source = ' class TwoFer def self.two_fer(name = nil) name = "you" if name.nil? @@ -9,7 +9,7 @@ def self.two_fer(name = nil) "One for #{name}, one for me." end end -} +' puts "\n\n\n\n" pp TwoFer::Analyze.(source) diff --git a/test/exercises/two_fer_test.rb b/test/exercises/two_fer_test.rb index 12d3cf1..6641f92 100644 --- a/test/exercises/two_fer_test.rb +++ b/test/exercises/two_fer_test.rb @@ -30,31 +30,31 @@ def test_fixtures # ### def test_simple_class_passes # skip - source = %q{ + source = ' class TwoFer def self.two_fer(name="you") "One for #{name}, one for me." end end - } + ' assert_equal "approve", analysis_results(source)["status"] end def test_simple_module_passes # skip - source = %q{ + source = ' module TwoFer def self.two_fer(name="you") "One for #{name}, one for me." end end - } + ' assert_equal "approve", analysis_results(source)["status"] end def test_simple_module_with_bookkeeping_passes # skip - source = %q{ + source = ' module TwoFer def self.two_fer(name="you") "One for #{name}, one for me." @@ -64,19 +64,19 @@ def self.two_fer(name="you") module Bookkeeping VERSION = 10 end - } + ' assert_equal "approve", analysis_results(source)["status"] end def test_different_module_name_fails # skip - source = %q{ + source = ' module SomethingElse def self.two_fer(name="you") "One for #{name}, one for me." end end - } + ' results = analysis_results(source) assert_equal "disapprove", results["status"] assert_equal ["ruby.general.no_target_module"], results["comments"] @@ -88,13 +88,13 @@ def self.two_fer(name="you") def test_different_method_value_fails # skip - source = %q{ + source = ' module TwoFer def self.foobar(name="you") "One for #{name}, one for me." end end - } + ' results = analysis_results(source) assert_equal "disapprove", results["status"] assert_equal ["ruby.general.no_target_method"], results["comments"] @@ -102,13 +102,13 @@ def self.foobar(name="you") def test_missing_param # skip - source = %q( + source = ' module TwoFer def self.two_fer "One for #{name}, one for me." end end - ) + ' results = analysis_results(source) assert_equal "disapprove", results["status"] assert_equal ["ruby.two-fer.missing_default_param"], results["comments"] @@ -116,13 +116,13 @@ def self.two_fer def test_missing_default_value_fails # skip - source = %q{ + source = ' module TwoFer def self.two_fer(name) "One for #{name}, one for me." end end - } + ' results = analysis_results(source) assert_equal "disapprove", results["status"] assert_equal ["ruby.two-fer.missing_default_param"], results["comments"] @@ -130,13 +130,13 @@ def self.two_fer(name) def test_splat_fails # skip - source = %q{ + source = ' module TwoFer def self.two_fer(*foos) "One for #{name}, one for me." end end - } + ' results = analysis_results(source) assert_equal "disapprove", results["status"] assert_equal [{"comment" => "ruby.two-fer.splat_args", "params" => {"name_variable" => "foos"}}], results["comments"] @@ -160,13 +160,13 @@ def self.two_fer(name="you") end def test_string_interpolation_passes - source = %q{ + source = ' class TwoFer def self.two_fer(name="you") "One for #{name}, one for me." end end - } + ' results = analysis_results(source) assert_equal "approve", results["status"] assert_equal [{"comment" => "ruby.two-fer.string_interpolation", "params" => {"name_variable" => "name"}}], results["comments"] @@ -202,7 +202,7 @@ def self.two_fer(name="you") def test_conditional_as_boolean # skip - source = %q{ + source = ' module TwoFer def self.two_fer(name=nil) if name @@ -212,7 +212,7 @@ def self.two_fer(name=nil) end end end - } + ' results = analysis_results(source) assert_equal "disapprove", results["status"] assert_equal ["ruby.two-fer.incorrect_default_param"], results["comments"] @@ -220,7 +220,7 @@ def self.two_fer(name=nil) def test_conditional_with_nil # skip - source = %q{ + source = ' module TwoFer def self.two_fer(name=nil) if name == nil @@ -230,7 +230,7 @@ def self.two_fer(name=nil) end end end - } + ' results = analysis_results(source) assert_equal "disapprove", results["status"] assert_equal ["ruby.two-fer.incorrect_default_param"], results["comments"] @@ -238,7 +238,7 @@ def self.two_fer(name=nil) def test_conditional_with_nil_reversed # skip - source = %q{ + source = ' module TwoFer def self.two_fer(name=nil) if nil == name @@ -248,7 +248,7 @@ def self.two_fer(name=nil) end end end - } + ' results = analysis_results(source) assert_equal "disapprove", results["status"] assert_equal ["ruby.two-fer.incorrect_default_param"], results["comments"] @@ -352,13 +352,13 @@ def self.two_fer(name="you") def test_explit_return # skip - source = %q{ + source = ' class TwoFer def self.two_fer(name="you") return "One for #{name}, one for me." end end - } + ' results = analysis_results(source) assert_equal "disapprove", results["status"] assert_equal ["ruby.general.explicit_return"], results["comments"] @@ -366,14 +366,14 @@ def self.two_fer(name="you") def test_reassigned_param # skip - source = %q{ + source = ' module TwoFer def self.two_fer(name=nil) name ||= "you" "One for #{name}, one for me." end end - } + ' results = analysis_results(source) assert_equal "disapprove", results["status"] assert_equal ["ruby.two-fer.reassigning_param"], results["comments"] @@ -381,7 +381,7 @@ def self.two_fer(name=nil) def test_reassigned_param_using_conditional # skip - source = %q{ + source = ' class TwoFer def self.two_fer(name = nil) name = "you" if name.nil? @@ -389,7 +389,7 @@ def self.two_fer(name = nil) "One for #{name}, one for me." end end - } + ' results = analysis_results(source) assert_equal "disapprove", results["status"] assert_equal ["ruby.two-fer.incorrect_default_param"], results["comments"] diff --git a/test/test_helper.rb b/test/test_helper.rb index 7f81dbe..cff3290 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -15,14 +15,7 @@ class Minitest::Test def parse_ast(source) - @parse_ast ||= begin - buffer = Parser::Source::Buffer.new(nil) - buffer.source = source - builder = RuboCop::AST::Builder.new - parser = Parser::CurrentRuby.new(builder) - - parser.parse(buffer) - end + @parse_ast ||= Prism::Translation::RubyParser.parse(source) end def extract_module_from_ast(source, classname = "TestModule") diff --git a/tests/two-fer/explit_return/expected_analysis.json b/tests/two-fer/explicit_return/expected_analysis.json similarity index 100% rename from tests/two-fer/explit_return/expected_analysis.json rename to tests/two-fer/explicit_return/expected_analysis.json diff --git a/tests/two-fer/explit_return/two_fer.rb b/tests/two-fer/explicit_return/two_fer.rb similarity index 100% rename from tests/two-fer/explit_return/two_fer.rb rename to tests/two-fer/explicit_return/two_fer.rb