diff --git a/.github/workflows/deploy-doc.yml b/.github/workflows/deploy-doc.yml index 7eba579a..c2d1f7f4 100644 --- a/.github/workflows/deploy-doc.yml +++ b/.github/workflows/deploy-doc.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: ruby/setup-ruby@19a43a6a2428d455dbd1b85344698725179c9d8c # v1.289.0 with: - ruby-version: '3.4.8' + ruby-version: '4.0' - run: bundle install - name: Setup Pages uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 246ab182..f82d6185 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -23,7 +23,7 @@ jobs: - uses: ruby/setup-ruby@19a43a6a2428d455dbd1b85344698725179c9d8c # v1.289.0 with: bundler-cache: true - ruby-version: 3.4.8 + ruby-version: '4.0' - name: Update version file with the release version run: | if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 2c2f38c7..29a02b69 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -18,6 +18,7 @@ jobs: - '3.2' - '3.3' - '3.4' + - '4.0' name: Ruby v${{ matrix.ruby }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/Gemfile b/Gemfile index b44ad4d6..7c73b4ac 100644 --- a/Gemfile +++ b/Gemfile @@ -4,9 +4,11 @@ source 'https://rubygems.org' gemspec group :development, :test do + gem 'irb', '~> 1.15.0' gem 'rack', '~> 3.1' # for yard server gem 'rackup', '~> 2.1' # for yard server gem 'rbs', '~> 3.10.0' + gem 'redcarpet', '~> 3.6.0' gem 'rubocop', '~> 1.85.0', require: false gem 'steep', '~> 1.10.0' gem 'webrick', '~> 1.9.1' diff --git a/Gemfile.lock b/Gemfile.lock index ae943750..c3ea11d0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,14 +8,14 @@ PATH GEM remote: https://rubygems.org/ specs: - activesupport (8.0.2) + activesupport (8.1.2) 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) securerandom (>= 0.3) @@ -25,7 +25,6 @@ GEM public_suffix (>= 2.0.2, < 7.0) ast (2.4.3) base64 (0.2.0) - benchmark (0.4.0) bigdecimal (3.3.1) concurrent-ruby (1.3.5) connection_pool (2.5.2) @@ -33,14 +32,20 @@ GEM bigdecimal rexml csv (3.3.4) + date (3.5.1) diff-lcs (1.6.2) drb (2.2.1) - ffi (1.17.2-arm64-darwin) - ffi (1.17.2-x86_64-linux-gnu) + erb (6.0.1) + ffi (1.17.2) fileutils (1.7.3) hashdiff (1.2.1) i18n (1.14.7) concurrent-ruby (~> 1.0) + io-console (0.8.2) + irb (1.15.3) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) json (2.18.1) json-schema (4.3.1) addressable (>= 2.8) @@ -52,14 +57,21 @@ GEM logger (1.7.0) mcp (0.8.0) json-schema (>= 4.1) - minitest (5.25.5) + minitest (6.0.1) + prism (~> 1.5) multipart-post (2.4.1) mutex_m (0.3.0) parallel (1.27.0) parser (3.3.10.2) ast (~> 2.4.1) racc + pp (0.6.3) + prettyprint + prettyprint (0.2.0) prism (1.9.0) + psych (5.3.1) + date + stringio public_suffix (6.0.2) racc (1.8.1) rack (3.2.5) @@ -73,7 +85,14 @@ GEM rbs (3.10.3) logger tsort + rdoc (7.2.0) + erb + psych (>= 4.0.0) + tsort + redcarpet (3.6.1) regexp_parser (2.11.3) + reline (0.6.3) + io-console (~> 0.5) rexml (3.4.4) rspec (3.13.2) rspec-core (~> 3.13.0) @@ -122,6 +141,7 @@ GEM strscan (>= 1.0.0) terminal-table (>= 2, < 5) uri (>= 0.12.0) + stringio (3.2.0) strscan (3.1.3) terminal-table (4.0.0) unicode-display_width (>= 1.1.1, < 4) @@ -142,14 +162,17 @@ GEM PLATFORMS arm64-darwin-21 arm64-darwin-22 + arm64-darwin-24 x86_64-linux DEPENDENCIES + irb (~> 1.15.0) line-bot-api! rack (~> 3.1) rackup (~> 2.1) rake (~> 13.0) rbs (~> 3.10.0) + redcarpet (~> 3.6.0) rspec (~> 3.13.0) rubocop (~> 1.85.0) steep (~> 1.10.0) diff --git a/Rakefile b/Rakefile index 6f7861d7..91ab596d 100644 --- a/Rakefile +++ b/Rakefile @@ -5,6 +5,13 @@ RSpec::Core::RakeTask.new(:spec) task default: :spec +# To avoid double "bundle exec" calls, e.g. bundle exec ... in bundle exec rake ... +def bundle_exec(command) + Bundler.with_unbundled_env do + sh "bundle exec #{command}" + end +end + # We don't want to push tags to source control when releasing the gem Rake::Task["release"].clear @@ -24,17 +31,19 @@ task :test_normal, [:skip_tag] do |_, args| rspec_opts = [] rspec_opts << "--tag '~#{skip_tag}'" if skip_tag - sh "bundle exec rspec --exclude-pattern 'spec/line/bot/line_bot_api_gem_spec.rb,spec/line/bot/line_bot_gem_spec.rb' #{rspec_opts.join(' ')}" + bundle_exec( + "rspec --exclude-pattern 'spec/line/bot/line_bot_api_gem_spec.rb,spec/line/bot/line_bot_gem_spec.rb' #{rspec_opts.join(' ')}" + ) end desc "Test line-bot-api gem spec" task :test_line_bot_api do - sh "bundle exec rspec --pattern 'spec/line/bot/line_bot_api_gem_spec.rb'" + bundle_exec("rspec --pattern 'spec/line/bot/line_bot_api_gem_spec.rb'") end desc "Test line/bot gem(?) spec" task :test_line_bot do - sh "bundle exec rspec --pattern 'spec/line/bot/line_bot_gem_spec.rb'" + bundle_exec("rspec --pattern 'spec/line/bot/line_bot_gem_spec.rb'") end desc "Run all tests in separate processes" @@ -46,18 +55,18 @@ end desc "Validate comment for YARD" task :validate_yard_comment do - sh "bundle exec yard stats ./lib/line/bot/v2 --fail-on-warning" + bundle_exec("yard stats ./lib/line/bot/v2 --fail-on-warning") end desc "RBS type check" task :rbs do - sh "bundle exec rbs collection install" - sh "bundle exec rbs -I sig validate" + bundle_exec("rbs collection install") + bundle_exec("rbs -I sig validate") end desc "RBS type check (with steep)" task :rbs_steep do - sh "bundle exec steep check" + bundle_exec("steep check") end desc "RBS type check (with test)" @@ -86,17 +95,17 @@ end desc "Run rubocop" task :rubocop do - sh "bundle exec rubocop" + bundle_exec("rubocop") end desc "Fix rubocop errors" task :rubocop_fix do - sh "bundle exec rubocop -A" + bundle_exec("rubocop -A") end desc "Check buildable and its contents" task :build_test do - sh "bundle exec rake build" + bundle_exec("rake build") sh "tar -xvf pkg/line-bot-api-*.gem" puts "<>" diff --git a/rbs_collection.lock.yaml b/rbs_collection.lock.yaml index 9ed3b8cc..27db5b87 100644 --- a/rbs_collection.lock.yaml +++ b/rbs_collection.lock.yaml @@ -25,6 +25,10 @@ gems: revision: 0ef449166f72c767f58f920cc36aca818fbf082f remote: https://github.com/ruby/gem_rbs_collection.git repo_dir: gems +- name: dbm + version: '0' + source: + type: stdlib - name: diff-lcs version: '1.5' source: @@ -37,6 +41,10 @@ gems: version: '0' source: type: stdlib +- name: erb + version: '0' + source: + type: stdlib - name: hashdiff version: '1.1' source: @@ -45,6 +53,10 @@ gems: revision: 0ef449166f72c767f58f920cc36aca818fbf082f remote: https://github.com/ruby/gem_rbs_collection.git repo_dir: gems +- name: io-console + version: '0' + source: + type: stdlib - name: json version: '0' source: @@ -61,6 +73,26 @@ gems: version: '0' source: type: stdlib +- name: pp + version: '0' + source: + type: stdlib +- name: prettyprint + version: '0' + source: + type: stdlib +- name: pstore + version: '0' + source: + type: stdlib +- name: psych + version: '0' + source: + type: stdlib +- name: rdoc + version: '0' + source: + type: stdlib - name: socket version: '0' source: @@ -69,6 +101,10 @@ gems: version: '0' source: type: stdlib +- name: tsort + version: '0' + source: + type: stdlib - name: uri version: '0' source: diff --git a/spec/workflows/ruby_version_consistency_spec.rb b/spec/workflows/ruby_version_consistency_spec.rb new file mode 100644 index 00000000..5c799438 --- /dev/null +++ b/spec/workflows/ruby_version_consistency_spec.rb @@ -0,0 +1,54 @@ +require 'yaml' + +RSpec.describe "GitHub Actions workflow Ruby version consistency" do + let(:workflows_dir) { File.expand_path('../../.github/workflows', __dir__) } + + let(:latest_matrix_version) do + pr_workflow = YAML.safe_load( + File.read(File.join(workflows_dir, 'pull_request.yml')), + permitted_classes: [Symbol] + ) + versions = pr_workflow.dig('jobs', 'build', 'strategy', 'matrix', 'ruby') + expect(versions).not_to be_nil, "No ruby matrix found in pull_request.yml" + versions.map(&:to_s).max_by { |v| Gem::Version.new(v) } + end + + let(:hardcoded_ruby_versions) do + versions = {} + + Dir[File.join(workflows_dir, '*.yml')].each do |file| + workflow = YAML.safe_load(File.read(file), permitted_classes: [Symbol]) + basename = File.basename(file) + + next unless workflow['jobs'] + + workflow['jobs'].each do |job_name, job| + steps = job['steps'] || [] + steps.each do |step| + next unless step.is_a?(Hash) && step.dig('with', 'ruby-version') + + ruby_version = step['with']['ruby-version'].to_s + next if ruby_version.include?('${{') + + versions["#{basename} -> #{job_name}"] = ruby_version + end + end + end + + versions + end + + it "uses the same Ruby version as the latest in pull_request.yml matrix" do + expect(hardcoded_ruby_versions).not_to( + be_empty, "No hardcoded ruby-version found in workflows" + ) + + mismatched = hardcoded_ruby_versions.reject { |_, v| v == latest_matrix_version } + expect(mismatched).to( + be_empty, + "Expected all hardcoded ruby-version to be '#{latest_matrix_version}' " \ + "(latest in pull_request.yml matrix), but found:\n" \ + "#{mismatched.map { |k, v| " #{k}: #{v}" }.join("\n")}" + ) + end +end