diff --git a/.github/workflows/heads.yml b/.github/workflows/heads.yml new file mode 100644 index 0000000..31d4282 --- /dev/null +++ b/.github/workflows/heads.yml @@ -0,0 +1,43 @@ +name: heads + +on: [push, pull_request] + +jobs: + test: + name: Specs - Ruby ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + strategy: + fail-fast: true + matrix: + include: + # ruby-head + - ruby: "head" + appraisal: "rails-8-0" + taskname: "spec" + gemfile: "appraisal_root" + rubygems: latest + bundler: latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Ruby & RubyGems + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + rubygems: ${{ matrix.rubygems }} + bundler: ${{ matrix.bundler }} + bundler-cache: false + # `bundle` uses BUNDLE_GEMFILE, set automatically via matrix.gemfile (i.e. appraisal_root) + # We need to do this first to get appraisal installed. + # NOTE: We do not use the root Gemfile in CI workflows at all. + - name: Bundle for Appraisal ${{ matrix.appraisal }} on Ruby ${{ matrix.ruby }} + run: bundle + - name: Install Appraisal ${{ matrix.appraisal }} dependencies on Ruby ${{ matrix.ruby }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + - name: Run ${{ matrix.appraisal }} tests via ${{ matrix.taskname }} on Ruby ${{ matrix.ruby }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec rake ${{ matrix.taskname }} diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml new file mode 100644 index 0000000..757e003 --- /dev/null +++ b/.github/workflows/style.yml @@ -0,0 +1,39 @@ +name: style + +on: [push, pull_request] + +jobs: + rubocop: + name: RuboCop + strategy: + fail-fast: false + matrix: + include: + - ruby: "3.3" + appraisal: "style" + taskname: "rubocop" + gemfile: "appraisal_root" + rubygems: latest + bundler: latest + runs-on: ubuntu-latest + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Ruby & RubyGems + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + rubygems: ${{ matrix.rubygems }} + bundler: ${{ matrix.bundler }} + bundler-cache: false + # `bundle` uses BUNDLE_GEMFILE, set automatically via matrix.gemfile (i.e. appraisal_root) + # We need to do this first to get appraisal installed. + # NOTE: We do not use the root Gemfile in CI workflows at all. + - name: Bundle for Appraisal ${{ matrix.appraisal }} on Ruby ${{ matrix.ruby }} + run: bundle + - name: Install Appraisal ${{ matrix.appraisal }} dependencies on Ruby ${{ matrix.ruby }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + - name: Run ${{ matrix.appraisal }} style checks via ${{ matrix.taskname }} on Ruby ${{ matrix.ruby }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec rake ${{ matrix.taskname }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 13641ab..f68ab2e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,27 +4,104 @@ on: [push, pull_request] jobs: test: + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile runs-on: ubuntu-latest strategy: fail-fast: false matrix: - ruby: [3.0, 3.1, 3.2] - gemfile: - - Gemfile + include: + # Ruby 3.0 + - ruby: "3.0" + appraisal: "rails-6-1" + taskname: "spec" + gemfile: "appraisal_root" + rubygems: '3.5.23' + bundler: '2.5.23' + - ruby: "3.0" + appraisal: "rails-7-0" + taskname: "spec" + gemfile: "appraisal_root" + rubygems: '3.5.23' + bundler: '2.5.23' + - ruby: "3.0" + appraisal: "rails-7-1" + taskname: "spec" + gemfile: "appraisal_root" + rubygems: '3.5.23' + bundler: '2.5.23' + + # Ruby 3.1 + - ruby: "3.1" + appraisal: "rails-7-0" + taskname: "spec" + gemfile: "appraisal_root" + rubygems: latest + bundler: latest + - ruby: "3.1" + appraisal: "rails-7-1" + taskname: "spec" + gemfile: "appraisal_root" + rubygems: latest + bundler: latest + - ruby: "3.1" + appraisal: "rails-7-2" + taskname: "spec" + gemfile: "appraisal_root" + rubygems: latest + bundler: latest + + # Ruby 3.2 + - ruby: "3.2" + appraisal: "rails-7-1" + taskname: "spec" + gemfile: "appraisal_root" + rubygems: latest + bundler: latest + - ruby: "3.2" + appraisal: "rails-7-2" + taskname: "spec" + gemfile: "appraisal_root" + rubygems: latest + bundler: latest + - ruby: "3.2" + appraisal: "rails-8-0" + taskname: "spec" + gemfile: "appraisal_root" + rubygems: latest + bundler: latest + + # Ruby 3.3 + - ruby: "3.3" + appraisal: "rails-7-2" + taskname: "spec" + gemfile: "appraisal_root" + rubygems: latest + bundler: latest + - ruby: "3.3" + appraisal: "rails-8-0" + taskname: "spec" + gemfile: "appraisal_root" + rubygems: latest + bundler: latest steps: - - uses: actions/checkout@v2 - - uses: ruby/setup-ruby@v1 - env: - BUNDLE_GEMFILE: ${{ matrix.gemfile }} + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Ruby & RubyGems + uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} - bundler-cache: true - - name: Run linter - env: - BUNDLE_GEMFILE: ${{ matrix.gemfile }} - run: bundle exec rubocop --parallel - - name: Run tests - env: - BUNDLE_GEMFILE: ${{ matrix.gemfile }} - run: bundle exec rake + rubygems: ${{ matrix.rubygems }} + bundler: ${{ matrix.bundler }} + bundler-cache: false + # `bundle` uses BUNDLE_GEMFILE, set automatically via matrix.gemfile (i.e. appraisal_root) + # We need to do this first to get appraisal installed. + # NOTE: We do not use the root Gemfile in CI workflows at all. + - name: Bundle for Appraisal ${{ matrix.appraisal }} on Ruby ${{ matrix.ruby }} + run: bundle + - name: Install Appraisal ${{ matrix.appraisal }} dependencies on Ruby ${{ matrix.ruby }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + - name: Run ${{ matrix.appraisal }} tests via ${{ matrix.taskname }} on Ruby ${{ matrix.ruby }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec rake ${{ matrix.taskname }} diff --git a/.gitignore b/.gitignore index c1ebcea..6bffc09 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /pkg /.idea + +gemfiles/*.gemfile.lock diff --git a/.rubocop.yml b/.rubocop.yml index 5061024..2f864f6 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -4,7 +4,11 @@ inherit_from: require: - rubocop/cop/internal_affairs + - rubocop-packaging AllCops: NewCops: enable TargetRubyVersion: 3.0 + +Rails/RakeEnvironment: + Enabled: false diff --git a/Appraisals b/Appraisals new file mode 100644 index 0000000..07be10a --- /dev/null +++ b/Appraisals @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +# Appraisals + +# To update Appraisal-generated gemfiles/*.gemfile run: +# BUNDLE_GEMFILE=appraisal_root.gemfile bundle update +# BUNDLE_GEMFILE=appraisal_root.gemfile appraisal update +# BUNDLE_GEMFILE=gemfiles/style.gemfile bundle exec rubocop -A --only Style/FrozenStringLiteralComment,Layout/EmptyLineAfterMagicComment + +appraise "style" do + gem "mutex_m", "~> 0.2" + gem "rails", ">= 6.1" + gem "rake", ">= 13" + gem "rubocop", "~> 1.69", ">= 1.69.2" + gem "rubocop-packaging", "~> 0.5", ">= 0.5.2" + # Skip some bad releases of standard + gem "standard", ">= 1.35.1", "!= 1.41.1", "!= 1.42.0" + gem "stringio", "~> 3.0" +end + +# Compat: Ruby >= 2.5 +# Test Matrix: +# - Ruby 2.5 +# - Ruby 2.6 +# - Ruby 2.7 +# + Ruby 3.0 +appraise "rails-6-1" do + gem "mutex_m", "~> 0.2" + gem "rails", "~> 6.1.7.10" + gem "rake", ">= 13" + gem "rspec-rails" + gem "rubocop", "~> 1.69", ">= 1.69.2" + # Skip some bad releases of standard + gem "standard", ">= 1.35.1", "!= 1.41.1", "!= 1.42.0" + gem "stringio", "~> 3.0" +end + +# Compat: Ruby >= 2.7 +# Test Matrix: +# - Ruby 2.7 +# + Ruby 3.0 +# + Ruby 3.1 +appraise "rails-7-0" do + gem "mutex_m", "~> 0.2" + gem "rails", "~> 7.0.8", ">= 7.0.8.7" + gem "rake", ">= 13" + gem "rspec-rails" + gem "rubocop", "~> 1.69", ">= 1.69.2" + # Skip some bad releases of standard + gem "standard", ">= 1.35.1", "!= 1.41.1", "!= 1.42.0" + gem "stringio", "~> 3.0" +end + +# Compat: Ruby >= 2.7 +# Test Matrix: +# - Ruby 2.7 +# + Ruby 3.0 +# + Ruby 3.1 +# + Ruby 3.2 +appraise "rails-7-1" do + gem "mutex_m", "~> 0.2" + gem "rails", "~> 7.1.5", ">= 7.1.5.1" + gem "rake", ">= 13" + gem "rspec-rails" + gem "rubocop", "~> 1.69", ">= 1.69.2" + # Skip some bad releases of standard + gem "standard", ">= 1.35.1", "!= 1.41.1", "!= 1.42.0" + gem "stringio", "~> 3.0" +end + +# Compat: Ruby >= 3.1 +# Test Matrix: +# + Ruby 3.1 +# + Ruby 3.2 +# + Ruby 3.3 +appraise "rails-7-2" do + gem "mutex_m", "~> 0.2" + gem "rails", "~> 7.2.2", ">= 7.2.2.1" + gem "rake", ">= 13" + gem "rspec-rails" + gem "rubocop", "~> 1.69", ">= 1.69.2" + # Skip some bad releases of standard + gem "standard", ">= 1.35.1", "!= 1.41.1", "!= 1.42.0" + gem "stringio", "~> 3.0" +end + +# Compat: Ruby >= 3.2 +# Test Matrix: +# + Ruby 3.2 +# + Ruby 3.3 +# + ruby-head +appraise "rails-8-0" do + gem "mutex_m", "~> 0.2" + gem "rails", "~> 8.0.1" + gem "rake", ">= 13" + gem "rspec-rails" + gem "rubocop", "~> 1.69", ">= 1.69.2" + # Skip some bad releases of standard + gem "standard", ">= 1.35.1", "!= 1.41.1", "!= 1.42.0" + gem "stringio", "~> 3.0" +end diff --git a/Gemfile b/Gemfile index d95b235..904b954 100644 --- a/Gemfile +++ b/Gemfile @@ -2,10 +2,14 @@ source 'https://rubygems.org' gemspec -group :development do - gem 'pry' - gem 'bundler' - gem 'rspec-rails' - gem 'rake', '>= 12.3.3' - gem 'standard' -end +# NOTE: Not adding appraisal here, because it should not be run against this Gemfile. +# See: `appraisal_root.gemfile` +gem 'rails', '>= 6.1' +gem 'pry' +gem 'rspec', '~> 3.13' +gem 'rspec-rails' +gem 'rake', '>= 13' +gem 'rubocop', '~> 1.69', '>= 1.69.2' +gem 'rubocop-packaging', '~> 0.5', '>= 0.5.2' +# Skip some bad releases of standard +gem 'standard', '>= 1.35.1', '!= 1.41.1', '!= 1.42.0' diff --git a/Gemfile.lock b/Gemfile.lock index 55038ed..56a7e8c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,82 +2,150 @@ PATH remote: . specs: betterlint (1.16.0) - rubocop (~> 1.62.0) - rubocop-graphql (~> 1.5.0) - rubocop-performance (~> 1.21.0) - rubocop-rails (~> 2.24.0) - rubocop-rake (~> 0.6.0) - rubocop-rspec (~> 2.28.0) + rubocop (~> 1.62) + rubocop-factory_bot (~> 2.26) + rubocop-graphql (~> 1.5) + rubocop-performance (~> 1.21) + rubocop-rails (~> 2.24) + rubocop-rake (~> 0.6) + rubocop-rspec (>= 2.28) GEM remote: https://rubygems.org/ specs: - actionpack (7.1.4.1) - actionview (= 7.1.4.1) - activesupport (= 7.1.4.1) + actioncable (8.0.1) + actionpack (= 8.0.1) + activesupport (= 8.0.1) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + zeitwerk (~> 2.6) + actionmailbox (8.0.1) + actionpack (= 8.0.1) + activejob (= 8.0.1) + activerecord (= 8.0.1) + activestorage (= 8.0.1) + activesupport (= 8.0.1) + mail (>= 2.8.0) + actionmailer (8.0.1) + actionpack (= 8.0.1) + actionview (= 8.0.1) + activejob (= 8.0.1) + activesupport (= 8.0.1) + mail (>= 2.8.0) + rails-dom-testing (~> 2.2) + actionpack (8.0.1) + actionview (= 8.0.1) + activesupport (= 8.0.1) nokogiri (>= 1.8.5) - racc rack (>= 2.2.4) rack-session (>= 1.0.1) rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - actionview (7.1.4.1) - activesupport (= 7.1.4.1) + useragent (~> 0.16) + actiontext (8.0.1) + actionpack (= 8.0.1) + activerecord (= 8.0.1) + activestorage (= 8.0.1) + activesupport (= 8.0.1) + globalid (>= 0.6.0) + nokogiri (>= 1.8.5) + actionview (8.0.1) + activesupport (= 8.0.1) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activesupport (7.1.4.1) + activejob (8.0.1) + activesupport (= 8.0.1) + globalid (>= 0.3.6) + activemodel (8.0.1) + activesupport (= 8.0.1) + activerecord (8.0.1) + activemodel (= 8.0.1) + activesupport (= 8.0.1) + timeout (>= 0.4.0) + activestorage (8.0.1) + actionpack (= 8.0.1) + activejob (= 8.0.1) + activerecord (= 8.0.1) + activesupport (= 8.0.1) + marcel (~> 1.0) + activesupport (8.0.1) base64 + benchmark (>= 0.3) bigdecimal - concurrent-ruby (~> 1.0, >= 1.0.2) + concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) - mutex_m - tzinfo (~> 2.0) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) ast (2.4.2) base64 (0.2.0) + benchmark (0.4.0) bigdecimal (3.1.8) builder (3.3.0) coderay (1.1.3) concurrent-ruby (1.3.4) connection_pool (2.4.1) crass (1.0.6) - diff-lcs (1.5.0) + date (3.4.1) + diff-lcs (1.5.1) drb (2.2.1) - erubi (1.13.0) + erubi (1.13.1) + globalid (1.2.1) + activesupport (>= 6.1) i18n (1.14.6) concurrent-ruby (~> 1.0) - io-console (0.7.1) - irb (1.11.0) - rdoc - reline (>= 0.3.8) - json (2.7.1) + io-console (0.8.0) + irb (1.14.3) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + json (2.9.1) language_server-protocol (3.17.0.3) lint_roller (1.1.0) - loofah (2.22.0) + logger (1.6.4) + loofah (2.23.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) - method_source (1.0.0) - minitest (5.25.1) - mutex_m (0.2.0) - nokogiri (1.16.7-arm64-darwin) + mail (2.8.1) + mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp + marcel (1.0.4) + method_source (1.1.0) + mini_mime (1.1.5) + minitest (5.25.4) + net-imap (0.5.3) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.5.0) + net-protocol + nio4r (2.7.4) + nokogiri (1.17.2-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.7-x86_64-darwin) + nokogiri (1.17.2-x86_64-darwin) racc (~> 1.4) - nokogiri (1.16.7-x86_64-linux) + nokogiri (1.17.2-x86_64-linux) racc (~> 1.4) - parallel (1.24.0) - parser (3.3.0.5) + parallel (1.26.3) + parser (3.3.6.0) ast (~> 2.4.1) racc - pry (0.14.2) + pry (0.15.0) coderay (~> 1.1) method_source (~> 1.0) - psych (5.1.2) + psych (5.2.2) + date stringio racc (1.8.1) rack (3.1.8) @@ -85,121 +153,141 @@ GEM rack (>= 3.0.0) rack-test (2.1.0) rack (>= 1.3) - rackup (2.1.0) + rackup (2.2.1) rack (>= 3) - webrick (~> 1.8) + rails (8.0.1) + actioncable (= 8.0.1) + actionmailbox (= 8.0.1) + actionmailer (= 8.0.1) + actionpack (= 8.0.1) + actiontext (= 8.0.1) + actionview (= 8.0.1) + activejob (= 8.0.1) + activemodel (= 8.0.1) + activerecord (= 8.0.1) + activestorage (= 8.0.1) + activesupport (= 8.0.1) + bundler (>= 1.15.0) + railties (= 8.0.1) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.0) + rails-html-sanitizer (1.6.2) loofah (~> 2.21) - nokogiri (~> 1.14) - railties (7.1.4.1) - actionpack (= 7.1.4.1) - activesupport (= 7.1.4.1) - irb + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + railties (8.0.1) + actionpack (= 8.0.1) + activesupport (= 8.0.1) + irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) thor (~> 1.0, >= 1.2.2) zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.1.0) - rdoc (6.6.3.1) + rake (13.2.1) + rdoc (6.10.0) psych (>= 4.0.0) - regexp_parser (2.9.0) - reline (0.4.1) + regexp_parser (2.9.3) + reline (0.6.0) io-console (~> 0.5) - rexml (3.3.6) - strscan - rspec-core (3.12.2) - rspec-support (~> 3.12.0) - rspec-expectations (3.12.3) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.2) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-mocks (3.12.6) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-rails (6.1.0) - actionpack (>= 6.1) - activesupport (>= 6.1) - railties (>= 6.1) - rspec-core (~> 3.12) - rspec-expectations (~> 3.12) - rspec-mocks (~> 3.12) - rspec-support (~> 3.12) - rspec-support (3.12.1) - rubocop (1.62.1) + rspec-support (~> 3.13.0) + rspec-rails (7.1.0) + actionpack (>= 7.0) + activesupport (>= 7.0) + railties (>= 7.0) + rspec-core (~> 3.13) + rspec-expectations (~> 3.13) + rspec-mocks (~> 3.13) + rspec-support (~> 3.13) + rspec-support (3.13.2) + rubocop (1.69.2) json (~> 2.3) language_server-protocol (>= 3.17.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.36.2, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.31.2) - parser (>= 3.3.0.4) - rubocop-capybara (2.19.0) - rubocop (~> 1.41) - rubocop-factory_bot (2.24.0) - rubocop (~> 1.33) - rubocop-graphql (1.5.1) - rubocop (>= 0.90, < 2) - rubocop-performance (1.21.0) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.37.0) + parser (>= 3.3.1.0) + rubocop-factory_bot (2.26.1) + rubocop (~> 1.61) + rubocop-graphql (1.5.4) + rubocop (>= 1.50, < 2) + rubocop-packaging (0.5.2) + rubocop (>= 1.33, < 2.0) + rubocop-performance (1.23.0) rubocop (>= 1.48.1, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rails (2.24.1) + rubocop-rails (2.27.0) activesupport (>= 4.2.0) rack (>= 1.1) - rubocop (>= 1.33.0, < 2.0) + rubocop (>= 1.52.0, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) rubocop-rake (0.6.0) rubocop (~> 1.0) - rubocop-rspec (2.28.0) - rubocop (~> 1.40) - rubocop-capybara (~> 2.17) - rubocop-factory_bot (~> 2.22) - rubocop-rspec_rails (~> 2.28) - rubocop-rspec_rails (2.28.2) - rubocop (~> 1.40) + rubocop-rspec (3.3.0) + rubocop (~> 1.61) ruby-progressbar (1.13.0) - standard (1.35.1) + securerandom (0.4.1) + standard (1.43.0) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.0) - rubocop (~> 1.62.0) + rubocop (~> 1.69.1) standard-custom (~> 1.0.0) - standard-performance (~> 1.3) + standard-performance (~> 1.6) standard-custom (1.0.2) lint_roller (~> 1.0) rubocop (~> 1.50) - standard-performance (1.4.0) + standard-performance (1.6.0) lint_roller (~> 1.1) - rubocop-performance (~> 1.21.0) - stringio (3.1.0) - strscan (3.1.0) - thor (1.3.0) + rubocop-performance (~> 1.23.0) + stringio (3.1.2) + thor (1.3.2) + timeout (0.4.3) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (2.5.0) - webrick (1.8.2) - zeitwerk (2.6.12) + unicode-display_width (3.1.2) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) + uri (1.0.2) + useragent (0.16.11) + websocket-driver (0.7.6) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) + zeitwerk (2.7.1) PLATFORMS arm64-darwin-22 arm64-darwin-23 + arm64-darwin-24 x86_64-darwin-22 x86_64-linux DEPENDENCIES betterlint! - bundler pry - rake (>= 12.3.3) + rails (>= 6.1) + rake (>= 13) + rspec (~> 3.13) rspec-rails - standard + rubocop (~> 1.69, >= 1.69.2) + rubocop-packaging (~> 0.5, >= 0.5.2) + standard (>= 1.35.1, != 1.42.0, != 1.41.1) BUNDLED WITH 2.4.22 diff --git a/README.md b/README.md index 265df93..445f6c5 100644 --- a/README.md +++ b/README.md @@ -280,3 +280,25 @@ actions often indicates error handling (e.g. `422 Unprocessable Entity`). This cop requires you to explicitly provide an HTTP status code when rendering a response in the create, update, and destroy actions. When autocorrecting, this will automatically add `status: :unprocessable_entity` or `status: :ok` depending on what you're rendering. + +## Contributing + +### Appraisal + +Generate a new set of Appraisal gemfiles: + +```bash +BUNDLE_GEMFILE=appraisal_root.gemfile bundle update +BUNDLE_GEMFILE=appraisal_root.gemfile appraisal update +BUNDLE_GEMFILE=gemfiles/style.gemfile bundle exec rubocop -A --only Style/FrozenStringLiteralComment,Layout/EmptyLineAfterMagicComment +``` + +### Reproduce CI test failure + +Example: A test fails in the workflow for `rails-8-0`. + +You want to reproduce it similar to how it runs on CI: + +```shell +CI=true BUNDLE_GEMFILE=appraisal_root.gemfile appraisal "rails-8-0" bundle exec rake spec +``` diff --git a/Rakefile b/Rakefile index b0a9082..00072ad 100644 --- a/Rakefile +++ b/Rakefile @@ -1,18 +1,25 @@ # frozen_string_literal: true +require "bundler/gem_tasks" + begin - require 'bundler/setup' + require 'rubocop/rake_task' + + RuboCop::RakeTask.new rescue LoadError - puts 'You must `gem install bundler` and `bundle install` to run rake tasks' + task(:rubocop) do + warn("RuboCop is disabled") + end end -Bundler::GemHelper.install_tasks - -require 'rubocop/rake_task' -RuboCop::RakeTask.new +begin + require 'rspec/core/rake_task' -require 'rspec/core' -require 'rspec/core/rake_task' -RSpec::Core::RakeTask.new(:spec) + RSpec::Core::RakeTask.new(:spec) +rescue LoadError + task(:spec) do + warn("RSpec is disabled") + end +end task default: %i(rubocop spec) diff --git a/appraisal_root.gemfile b/appraisal_root.gemfile new file mode 100644 index 0000000..a9b7b41 --- /dev/null +++ b/appraisal_root.gemfile @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +# To update Appraisal-generated gemfiles/*.gemfile run: +# BUNDLE_GEMFILE=appraisal_root.gemfile bundle update +# BUNDLE_GEMFILE=appraisal_root.gemfile appraisal update +# BUNDLE_GEMFILE=gemfiles/style.gemfile bundle exec rubocop -A --only Style/FrozenStringLiteralComment,Layout/EmptyLineAfterMagicComment + +git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } + +source "https://rubygems.org" + +# Appraisal Root Gemfile is for running appraisal to generate the Appraisal Gemfiles +# in gemfiles/*gemfile. +# In GitHub Actions CI it is used to prepare the env to install an Appraisal gemfiles/*gemfile. +# +# Do not load the primary Gemfile for Appraisal Gemfiles generation, +# as it is tailored for local development, +# and includes things unrelated to running the spec suite. + +gem 'appraisal', '~> 2.5' + +gemspec diff --git a/appraisal_root.gemfile.lock b/appraisal_root.gemfile.lock new file mode 100644 index 0000000..ec1d06f --- /dev/null +++ b/appraisal_root.gemfile.lock @@ -0,0 +1,102 @@ +PATH + remote: . + specs: + betterlint (1.16.0) + rubocop (~> 1.62) + rubocop-factory_bot (~> 2.26) + rubocop-graphql (~> 1.5) + rubocop-performance (~> 1.21) + rubocop-rails (~> 2.24) + rubocop-rake (~> 0.6) + rubocop-rspec (>= 2.28) + +GEM + remote: https://rubygems.org/ + specs: + activesupport (8.0.1) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + ast (2.4.2) + base64 (0.2.0) + benchmark (0.4.0) + bigdecimal (3.1.8) + concurrent-ruby (1.3.4) + connection_pool (2.4.1) + drb (2.2.1) + i18n (1.14.6) + concurrent-ruby (~> 1.0) + json (2.9.1) + language_server-protocol (3.17.0.3) + logger (1.6.4) + minitest (5.25.4) + parallel (1.26.3) + parser (3.3.6.0) + ast (~> 2.4.1) + racc + racc (1.8.1) + rack (3.1.8) + rainbow (3.1.1) + rake (13.2.1) + regexp_parser (2.9.3) + rubocop (1.69.2) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.36.2, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.37.0) + parser (>= 3.3.1.0) + rubocop-factory_bot (2.26.1) + rubocop (~> 1.61) + rubocop-graphql (1.5.4) + rubocop (>= 1.50, < 2) + rubocop-performance (1.23.0) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rails (2.27.0) + activesupport (>= 4.2.0) + rack (>= 1.1) + rubocop (>= 1.52.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rake (0.6.0) + rubocop (~> 1.0) + rubocop-rspec (3.3.0) + rubocop (~> 1.61) + ruby-progressbar (1.13.0) + securerandom (0.4.1) + thor (1.3.2) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (3.1.2) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) + uri (1.0.2) + +PLATFORMS + arm64-darwin-24 + ruby + +DEPENDENCIES + appraisal (~> 2.5) + betterlint! + +BUNDLED WITH + 2.6.1 diff --git a/betterlint.gemspec b/betterlint.gemspec index 705164c..bb4a53d 100644 --- a/betterlint.gemspec +++ b/betterlint.gemspec @@ -24,10 +24,11 @@ Gem::Specification.new do |s| s.required_ruby_version = ">= 3.0" - s.add_dependency "rubocop", "~> 1.62.0" - s.add_dependency "rubocop-graphql", "~> 1.5.0" - s.add_dependency "rubocop-performance", "~> 1.21.0" - s.add_dependency "rubocop-rails", "~> 2.24.0" - s.add_dependency "rubocop-rake", "~> 0.6.0" - s.add_dependency "rubocop-rspec", "~> 2.28.0" + s.add_dependency "rubocop", "~> 1.62" + s.add_dependency "rubocop-factory_bot", "~> 2.26" + s.add_dependency "rubocop-graphql", "~> 1.5" + s.add_dependency "rubocop-performance", "~> 1.21" + s.add_dependency "rubocop-rails", "~> 2.24" + s.add_dependency "rubocop-rake", "~> 0.6" + s.add_dependency "rubocop-rspec", ">= 2.28" end diff --git a/config/default.yml b/config/default.yml index c2489d6..0f85969 100644 --- a/config/default.yml +++ b/config/default.yml @@ -2,6 +2,7 @@ require: - rubocop/cop/betterment.rb + - rubocop-factory_bot - rubocop-graphql - rubocop-performance - rubocop-rails @@ -239,9 +240,6 @@ Performance/RedundantMatch: RSpec/BeEq: Enabled: false -RSpec/Capybara/FeatureMethods: - Enabled: false - RSpec/ContextWording: Enabled: false diff --git a/gemfiles/rails_6_1.gemfile b/gemfiles/rails_6_1.gemfile new file mode 100644 index 0000000..f30d007 --- /dev/null +++ b/gemfiles/rails_6_1.gemfile @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal", "~> 2.5" +gem "mutex_m", "~> 0.2" +gem "rails", "~> 6.1.7.10" +gem "rake", ">= 13" +gem "rspec-rails" +gem "rubocop", "~> 1.69", ">= 1.69.2" +gem "standard", ">= 1.35.1", "!= 1.41.1", "!= 1.42.0" +gem "stringio", "~> 3.0" + +gemspec path: "../" diff --git a/gemfiles/rails_7_0.gemfile b/gemfiles/rails_7_0.gemfile new file mode 100644 index 0000000..fd614d9 --- /dev/null +++ b/gemfiles/rails_7_0.gemfile @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal", "~> 2.5" +gem "mutex_m", "~> 0.2" +gem "rails", "~> 7.0.8", ">= 7.0.8.7" +gem "rake", ">= 13" +gem "rspec-rails" +gem "rubocop", "~> 1.69", ">= 1.69.2" +gem "standard", ">= 1.35.1", "!= 1.41.1", "!= 1.42.0" +gem "stringio", "~> 3.0" + +gemspec path: "../" diff --git a/gemfiles/rails_7_1.gemfile b/gemfiles/rails_7_1.gemfile new file mode 100644 index 0000000..27e610f --- /dev/null +++ b/gemfiles/rails_7_1.gemfile @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal", "~> 2.5" +gem "mutex_m", "~> 0.2" +gem "rails", "~> 7.1.5", ">= 7.1.5.1" +gem "rake", ">= 13" +gem "rspec-rails" +gem "rubocop", "~> 1.69", ">= 1.69.2" +gem "standard", ">= 1.35.1", "!= 1.41.1", "!= 1.42.0" +gem "stringio", "~> 3.0" + +gemspec path: "../" diff --git a/gemfiles/rails_7_2.gemfile b/gemfiles/rails_7_2.gemfile new file mode 100644 index 0000000..90b51d6 --- /dev/null +++ b/gemfiles/rails_7_2.gemfile @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal", "~> 2.5" +gem "mutex_m", "~> 0.2" +gem "rails", "~> 7.2.2", ">= 7.2.2.1" +gem "rake", ">= 13" +gem "rspec-rails" +gem "rubocop", "~> 1.69", ">= 1.69.2" +gem "standard", ">= 1.35.1", "!= 1.41.1", "!= 1.42.0" +gem "stringio", "~> 3.0" + +gemspec path: "../" diff --git a/gemfiles/rails_8_0.gemfile b/gemfiles/rails_8_0.gemfile new file mode 100644 index 0000000..1a58bc5 --- /dev/null +++ b/gemfiles/rails_8_0.gemfile @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal", "~> 2.5" +gem "mutex_m", "~> 0.2" +gem "rails", "~> 8.0.1" +gem "rake", ">= 13" +gem "rspec-rails" +gem "rubocop", "~> 1.69", ">= 1.69.2" +gem "standard", ">= 1.35.1", "!= 1.41.1", "!= 1.42.0" +gem "stringio", "~> 3.0" + +gemspec path: "../" diff --git a/gemfiles/style.gemfile b/gemfiles/style.gemfile new file mode 100644 index 0000000..52eee03 --- /dev/null +++ b/gemfiles/style.gemfile @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal", "~> 2.5" +gem "mutex_m", "~> 0.2" +gem "rails", ">= 6.1" +gem "rake", ">= 13" +gem "rubocop", "~> 1.69", ">= 1.69.2" +gem "rubocop-packaging", "~> 0.5", ">= 0.5.2" +gem "standard", ">= 1.35.1", "!= 1.41.1", "!= 1.42.0" +gem "stringio", "~> 3.0" + +gemspec path: "../" diff --git a/lib/rubocop/cop/betterment/authorization_in_controller.rb b/lib/rubocop/cop/betterment/authorization_in_controller.rb index 804471b..9aa31d9 100644 --- a/lib/rubocop/cop/betterment/authorization_in_controller.rb +++ b/lib/rubocop/cop/betterment/authorization_in_controller.rb @@ -34,7 +34,7 @@ class AuthorizationInController < Cop PATTERN def initialize(config = nil, options = nil) - super(config, options) + super config = @config.for_cop(self) @unsafe_parameters = config.fetch("unsafe_parameters", []).map(&:to_sym) @unsafe_regex = Regexp.new config.fetch("unsafe_regex", ".*_id$") diff --git a/lib/rubocop/cop/betterment/non_standard_actions.rb b/lib/rubocop/cop/betterment/non_standard_actions.rb index 6c20243..109cb25 100644 --- a/lib/rubocop/cop/betterment/non_standard_actions.rb +++ b/lib/rubocop/cop/betterment/non_standard_actions.rb @@ -62,9 +62,8 @@ def check_raw_route(node) end end - # NOTE: The InternalAffairs/UndefinedConfig rule seems to have a bug where it can't fine these configs in config/default.yml def allowed_actions - @allowed_actions ||= cop_config['StandardActions'] + cop_config['AdditionalAllowedActions'] # rubocop:disable InternalAffairs/UndefinedConfig + @allowed_actions ||= cop_config['StandardActions'] + cop_config['AdditionalAllowedActions'] end def allowed_action?(action) diff --git a/lib/rubocop/cop/betterment/unsafe_job.rb b/lib/rubocop/cop/betterment/unsafe_job.rb index f715ced..1c6559b 100644 --- a/lib/rubocop/cop/betterment/unsafe_job.rb +++ b/lib/rubocop/cop/betterment/unsafe_job.rb @@ -14,7 +14,7 @@ class UnsafeJob < Cop MSG def initialize(config = nil, options = nil) - super(config, options) + super config = @config.for_cop(self) @sensitive_params = config.fetch("sensitive_params", []).map(&:to_sym) @class_regex = Regexp.new config.fetch("class_regex", ".*Job$") diff --git a/lib/rubocop/cop/betterment/unscoped_find.rb b/lib/rubocop/cop/betterment/unscoped_find.rb index fe3aa8e..4a12e0e 100644 --- a/lib/rubocop/cop/betterment/unscoped_find.rb +++ b/lib/rubocop/cop/betterment/unscoped_find.rb @@ -36,7 +36,7 @@ class UnscopedFind < Cop PATTERN def initialize(config = nil, options = nil) - super(config, options) + super config = @config.for_cop(self) @unauthenticated_models = config.fetch("unauthenticated_models", []).map(&:to_sym) end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9257147..6291be9 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,7 +4,7 @@ require 'rubocop/cop/betterment' require 'rubocop/rspec/support' require 'support/betterlint_config' -require 'pry' +require 'pry' unless ENV.fetch('CI', 'false').casecmp?('true') RSpec.configure do |config| config.include RuboCop::RSpec::ExpectOffense diff --git a/spec/standard_compliance_spec.rb b/spec/standard_compliance_spec.rb index 8e3d18b..73f2e2c 100644 --- a/spec/standard_compliance_spec.rb +++ b/spec/standard_compliance_spec.rb @@ -36,7 +36,7 @@ let(:default_config) do root = Gem.loaded_specs['rubocop'].full_gem_path path = File.expand_path('config/default.yml', root) - config = YAML.load_file(path, permitted_classes: [Regexp, Symbol]) + config = YAML.safe_load_file(path, permitted_classes: [Regexp, Symbol]) config.delete('AllCops') config end @@ -44,11 +44,11 @@ let(:standard_config) do root = Gem.loaded_specs['standard'].full_gem_path path = File.expand_path('config/base.yml', root) - YAML.load_file(path) + YAML.safe_load_file(path) end let(:betterlint_config) do - YAML.load_file('config/default.yml') + YAML.safe_load_file('config/default.yml') end it 'complies with standardrb with notable exceptions' do