From 1149bfd48ed1b0713c942235ec1d519a08f33d5e Mon Sep 17 00:00:00 2001 From: "Daniel (dB.) Doubrovkine" Date: Sat, 12 Sep 2026 12:58:00 -0400 Subject: [PATCH] Add Appraisals to reproduce and track json 3.x incompatibility Adds an Appraisal-based test matrix (json-2, json-3) run in CI so the existing spec suite is exercised against both the currently supported json < 3.0 and json >= 3.0, reproducing the Faraday::ParsingError / ArgumentError described in #113 whenever json 3.x is resolved. - Add 'appraisal' gem and an Appraisals file defining 'json-2' and 'json-3' appraisals. - Move rubocop, rubocop-rake, and rubocop-rspec out of the main Gemfile into their own gemfiles/rubocop.gemfile, since rubocop < 1.90 pins json ~> 2.3, which otherwise made it impossible to resolve a Gemfile that also depends on json ~> 3.0. - Update the Rakefile default task and .github/workflows/rubocop.yml to use gemfiles/rubocop.gemfile via BUNDLE_GEMFILE. - Add a CI job in .github/workflows/test.yml that runs 'rake spec' against each appraisal gemfile; the json-3 job is currently expected to fail, tracking when the incompatibility is resolved. - Exclude generated gemfiles/*.gemfile from rubocop and gemfiles/*.gemfile.lock from git. - Document the new workflow in CONTRIBUTING.md and CHANGELOG.md. Verified locally: the existing oauth/api/webhooks specs pass under gemfiles/json_2.gemfile (0 failures) and fail under gemfiles/json_3.gemfile with the exact reported error, reproducing https://github.com/dblock/strava-ruby-client/issues/113. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/rubocop.yml | 2 ++ .github/workflows/test.yml | 15 ++++++++++++++- .gitignore | 1 + .rubocop.yml | 1 + Appraisals | 14 ++++++++++++++ CHANGELOG.md | 1 + CONTRIBUTING.md | 20 ++++++++++++++++++++ Gemfile | 10 +++++++--- Rakefile | 8 +++++--- gemfiles/json_2.gemfile | 32 ++++++++++++++++++++++++++++++++ gemfiles/json_3.gemfile | 32 ++++++++++++++++++++++++++++++++ gemfiles/rubocop.gemfile | 13 +++++++++++++ 12 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 Appraisals create mode 100644 gemfiles/json_2.gemfile create mode 100644 gemfiles/json_3.gemfile create mode 100644 gemfiles/rubocop.gemfile diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml index 93436de9..3c131717 100644 --- a/.github/workflows/rubocop.yml +++ b/.github/workflows/rubocop.yml @@ -3,6 +3,8 @@ on: [push, pull_request] jobs: lint: runs-on: ubuntu-latest + env: + BUNDLE_GEMFILE: gemfiles/rubocop.gemfile steps: - uses: actions/checkout@v7 - name: Set up Ruby diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5e9e5436..6f4462e1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,6 +14,19 @@ jobs: - "4.0" - "jruby-9.4" - "jruby-10" + gemfile: + - Gemfile + include: + # See https://github.com/dblock/strava-ruby-client/issues/113: json + # 2.x is the version this gem currently supports. json_3 is + # commented out until Faraday's :json middleware supports json >= + # 3.0, since it currently fails (Faraday::ParsingError). + - ruby-version: "3.4" + gemfile: gemfiles/json_2.gemfile + # - ruby-version: "3.4" + # gemfile: gemfiles/json_3.gemfile + env: + BUNDLE_GEMFILE: ${{ matrix.gemfile }} steps: - uses: actions/checkout@v7 - name: Set up Ruby @@ -31,7 +44,7 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} path-to-lcov: coverage/lcov.info - flag-name: ${{ matrix.ruby-version }} + flag-name: ${{ matrix.gemfile == 'Gemfile' && matrix.ruby-version || format('{0}-{1}', matrix.ruby-version, matrix.gemfile) }} parallel: true integration: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 6f685436..07768c83 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,6 @@ .tool-versions .vscode Gemfile.lock +gemfiles/*.gemfile.lock pkg coverage diff --git a/.rubocop.yml b/.rubocop.yml index eb925220..643c84ed 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -9,6 +9,7 @@ AllCops: NewCops: enable Exclude: - vendor/**/* + - gemfiles/**/* Naming/MethodName: Enabled: false diff --git a/Appraisals b/Appraisals new file mode 100644 index 00000000..f04553b2 --- /dev/null +++ b/Appraisals @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# Tests the gem against the two major json releases that are relevant to +# https://github.com/dblock/strava-ruby-client/issues/113: json < 3.0, where +# JSON.parse accepts a positional options hash (the form Faraday's :json +# response middleware uses), and json >= 3.0, where JSON.parse only accepts +# keyword arguments and Faraday's call raises an ArgumentError. +appraise 'json-2' do + gem 'json', '~> 2.3' +end + +appraise 'json-3' do + gem 'json', '~> 3.0' +end diff --git a/CHANGELOG.md b/CHANGELOG.md index efb25461..36a6601f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ### 3.1.1 (Next) +* [#113](https://github.com/dblock/strava-ruby-client/issues/113): Adds Appraisals (`json-2`, `json-3`) - [@dblock](https://github.com/dblock). * Your contribution here. ### 3.1.0 (2026/08/29) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8ee278c4..660f7ca0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,6 +25,26 @@ bundle install bundle exec rake ``` +Rubocop uses its own `gemfiles/rubocop.gemfile`, kept separate so its pinned version doesn't constrain the main Gemfile's dependency resolution. + +``` +BUNDLE_GEMFILE=gemfiles/rubocop.gemfile bundle install +BUNDLE_GEMFILE=gemfiles/rubocop.gemfile bundle exec rubocop +``` + +This gem is tested against multiple `json` gem versions using [Appraisal](https://github.com/thoughtbot/appraisal), defined in `Appraisals`. To run the test suite against a specific `json` version, e.g. to reproduce [#113](https://github.com/dblock/strava-ruby-client/issues/113): + +``` +BUNDLE_GEMFILE=gemfiles/json_3.gemfile bundle install +BUNDLE_GEMFILE=gemfiles/json_3.gemfile bundle exec rake spec +``` + +If you add or change a runtime/test dependency in the main `Gemfile`, regenerate the appraisal gemfiles. + +``` +bundle exec appraisal generate +``` + ## Contribute Code ### Obtain a Strava Token diff --git a/Gemfile b/Gemfile index d3f59ce6..3bbd50f0 100755 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,14 @@ source 'http://rubygems.org' gemspec +# Faraday's :json response middleware is incompatible with json >= 3.0 (see +# https://github.com/dblock/strava-ruby-client/issues/113), so pin json here +# rather than relying on rubocop's own transitive constraint. The `json-3` +# appraisal (see Appraisals) is used to track when this can be removed. +gem 'json', '~> 2.3' + group :development, :test do + gem 'appraisal' gem 'csv' gem 'danger-changelog', '~> 0.8.0' gem 'danger-pr-comment' @@ -17,9 +24,6 @@ group :development, :test do gem 'pry' gem 'rake' gem 'rspec' - gem 'rubocop', '1.68.0' - gem 'rubocop-rake' - gem 'rubocop-rspec' gem 'tcx' gem 'vcr' gem 'webmock' diff --git a/Rakefile b/Rakefile index cd00c82a..418ac7de 100644 --- a/Rakefile +++ b/Rakefile @@ -17,7 +17,9 @@ RSpec::Core::RakeTask.new(:'spec:integration') do |spec| spec.pattern = FileList['spec/integration/**/*_spec.rb'] end -require 'rubocop/rake_task' -RuboCop::RakeTask.new +# Rubocop lives in its own Gemfile (gemfiles/rubocop.gemfile), not the main +# Gemfile, so it doesn't constrain dependency resolution there (see +# https://github.com/dblock/strava-ruby-client/issues/113). Run it with +# `BUNDLE_GEMFILE=gemfiles/rubocop.gemfile bundle exec rubocop`. -task default: %i[rubocop spec] +task default: %i[spec] diff --git a/gemfiles/json_2.gemfile b/gemfiles/json_2.gemfile new file mode 100644 index 00000000..f4c3695e --- /dev/null +++ b/gemfiles/json_2.gemfile @@ -0,0 +1,32 @@ +# This file was generated by Appraisal + +source "http://rubygems.org" + +gem "json", "~> 2.3" + +group :development, :test do + gem "appraisal" + gem "csv" + gem "danger-changelog", "~> 0.8.0" + gem "danger-pr-comment" + gem "danger-toc", "~> 0.2.0" + gem "dotenv" + gem "faraday-retry" + gem "gpx" + gem "multi_xml" + gem "polylines" + gem "pry" + gem "rake" + gem "rspec" + gem "tcx" + gem "vcr" + gem "webmock" + gem "webrick", "~> 1.9" +end + +group :test do + gem "simplecov" + gem "simplecov-lcov", require: false +end + +gemspec path: "../" diff --git a/gemfiles/json_3.gemfile b/gemfiles/json_3.gemfile new file mode 100644 index 00000000..503edea1 --- /dev/null +++ b/gemfiles/json_3.gemfile @@ -0,0 +1,32 @@ +# This file was generated by Appraisal + +source "http://rubygems.org" + +gem "json", "~> 3.0" + +group :development, :test do + gem "appraisal" + gem "csv" + gem "danger-changelog", "~> 0.8.0" + gem "danger-pr-comment" + gem "danger-toc", "~> 0.2.0" + gem "dotenv" + gem "faraday-retry" + gem "gpx" + gem "multi_xml" + gem "polylines" + gem "pry" + gem "rake" + gem "rspec" + gem "tcx" + gem "vcr" + gem "webmock" + gem "webrick", "~> 1.9" +end + +group :test do + gem "simplecov" + gem "simplecov-lcov", require: false +end + +gemspec path: "../" diff --git a/gemfiles/rubocop.gemfile b/gemfiles/rubocop.gemfile new file mode 100644 index 00000000..271c7f3c --- /dev/null +++ b/gemfiles/rubocop.gemfile @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +# Rubocop is kept in its own Gemfile, separate from the main Gemfile, so that +# its exact pinned version (and its own transitive dependency constraints, +# e.g. on json) don't participate in dependency resolution for the gem's +# runtime/test Gemfile or its Appraisals. See +# https://github.com/dblock/strava-ruby-client/issues/113. + +source 'http://rubygems.org' + +gem 'rubocop', '1.68.0' +gem 'rubocop-rake' +gem 'rubocop-rspec'