Release 9.0.0: drop legacy endpoints, Ruby 3.4 + Rails 8.1 support#283
Merged
Conversation
This is a maintenance / release-prep pass that drops legacy Castle API endpoints, modernizes the runtime/dev tooling, and lines us up with the same standards we just applied to castle_devise 0.6.0. BREAKING: - Drop /v1/track, /v1/authenticate, all device endpoints (approve_device, get_device, get_devices_for_user, report_device), and all impersonation endpoints (start/end_impersonation), plus their Castle::Client helpers and the Castle::ImpersonationFailed error. - Drop support for Ruby < 3.2. Modernization: - .ruby-version -> 3.4.6, add .tool-versions, drop .ruby-gemset. - Bump version to 9.0.0; modernize gemspec metadata (source_code_uri/changelog_uri/bug_tracker_uri/rubygems_mfa_required). - Replace appraisal with hand-maintained gemfiles for Rails 7.0 / 7.1 / 7.2 / 8.0 / 8.1. - CircleCI matrix: Ruby 3.2/3.3/3.4 x Rails 7.0/7.1/7.2/8.0/8.1 + dedicated rubocop job. - .rubocop.yml: target Ruby 3.2, drop dead `prettier` inherit, add rubocop-rake. - Drop deprecated `coveralls_reborn` and `byebug` deps; rely on simplecov + stdlib `debug`. - Update Rails integration test + README to use the modern Risk API.
- Rewrite README around the actually-supported endpoints (Risk, Filter,
Log, Lists, List Items, Webhooks). Move the noisy header allow/deny
and IP-detection options into a dedicated `Advanced configuration`
section so the basic config block stays focused on api_secret /
failover_strategy / request_timeout. Add Quick start, Errors table,
Upgrading-to-9.0 migration table, and Contributing notes.
- Ruby 3.4 unbundled `ostruct` and `base64` from stdlib:
- require 'ostruct' in spec_helper (still used by response specs)
- require 'base64' from lib/castle.rb so webhook signature
computation keeps working without ActiveSupport in scope
- declare `base64 ~> 0.2` as a runtime gem dep so 3.5+ stays green
- lib/castle/core/process_webhook.rb no longer relies on AS's
`String#blank?` (it was only working accidentally via Rails
autoloading); use `nil? || empty?`.
- Modernize .rubocop.yml: switch `require:` -> `plugins:`, exclude
spec dirs from `Style/OpenStructUse`, silence stylistic noise on
pre-existing patterns, document inline disables for
`Lint/MissingSuper` (RequestError) and `Lint/StructNewOverride`
(`Castle::Command`).
- Rakefile gets a `desc` line on the `test` alias.
- Skip the Rails integration spec gracefully when Rails isn't loaded
(so `bundle exec rspec` works under the root Gemfile).
- Refresh Gemfile.lock for the new toolchain.
- castle/support/hanami targeted Hanami 1.x and the framework's 2.x rewrite has obsoleted `Hanami::Application` + `controller.prepare`, so the helper has been silently broken for years. - castle/support/padrino has negligible real-world adoption and the helper is a 3-line wrapper that anyone still on Padrino can paste inline (documented in the README). Rails and Sinatra remain first-class. CHANGELOG entry added under 9.0.0.
`~> 3.13` on rspec and `~> 3.26` on webmock were added in the previous commit but aren't required and don't match the sibling castle_devise Gemfile. For a gem's own dev/test Gemfile we want to track latest by default; the runtime gemspec is what consumers actually depend on. Rails pins (`~> 7.0.0`, `~> 7.1.0`, ...) stay in place because that's the test-matrix axis.
- #279: `Castle::API::Filter`/`Risk`/`Log` failover handlers crashed with NoMethodError when `options[:user]` was missing, e.g. for the recommended `$login.$attempted` filter payload that has no user block. Switch to `options.dig(:user, :id)`, fall back to `matching_user_id` for /v1/filter, and add a regression spec. - Drop the Coditsu CI integration (.coditsu/ci.yml + the `coditsu` CircleCI job + the .gitignore line). The service was wound down a while ago and the job was effectively a no-op. - bin/lint: rewrite to mirror ../web — RuboCop -A first, Prettier second; auto-bootstrap node_modules and asdf-shimmed Ruby so the plugin-ruby parser doesn't pick up macOS system Ruby 2.6. - Add package.json (prettier 3.8.3 + @prettier/plugin-ruby 4.0.4), refresh .prettierrc/.prettierignore, ignore /node_modules/. - .rubocop.yml: exclude node_modules from inspection.
- Castle::API::ListItems::CreateBatch (POST /v1/lists/{id}/items/batch)
plus Castle::Client#create_batch_list_items.
- Castle::API::Privacy::RequestData (POST /v1/privacy/users) and
Castle::API::Privacy::DeleteData (DELETE /v1/privacy/users) plus
matching Castle::Client#request_user_data and #delete_user_data.
Closes #261. The deprecated path-based privacy endpoints
(POST/DELETE /v1/privacy/users/{id}) are intentionally not exposed.
- README and CHANGELOG updated for the new surface and to call out
the #279 failover-handler fix.
868f1b7 to
4cfa67f
Compare
CircleCI's repo-level checkout SSH key for castle/castle-ruby has been unauthorized since June 2025, blocking every build (including the rack CVE merges into develop) at the `Checkout code` step. Rather than rotate that key, switch to GitHub Actions — same pattern as castle_devise — so CI uses the built-in GITHUB_TOKEN and there is no per-repo deploy key to manage. - .github/workflows/specs.yml: Ruby 3.2/3.3/3.4 x Rails 7.0/7.1/7.2/ 8.0/8.1 matrix running bundle exec rspec - .github/workflows/lint.yml: single rubocop job on Ruby 3.4 - Drop .circleci/config.yml and the badge in the README
Real fixes uncovered during a release-readiness pass: - Client do-not-track path mirrored the #279 bug: `Client#filter`/ `#risk`/`#log` did `options[:user][:id]` unconditionally when tracking was disabled. Switch all three to a shared helper that uses `options.dig(:user, :id)` with a `matching_user_id` fallback, and add a regression spec for each action. - Per-call `config:` now reaches `GetConnection.call(config)`, so a custom `Castle::Configuration` actually drives host/port/SSL/ timeout — not just the request body. Spec asserts both the resolved address and the timeout values. - `GetConnection` now sets `open_timeout` in addition to `read_timeout`, so a slow TCP/TLS handshake hits the configured budget instead of Net::HTTP's 60 s default. - Backfill `Castle::API::Risk` and `Castle::API::Log` API specs (they were `pending` placeholders). Each covers the success path and the three failover branches, including the missing-`:user` regression. Housekeeping: - Drop the Appraisal-generated `gemfiles/rails_8.gemfile` and stale `gemfiles/*.gemfile.lock` artifacts left over from the pre-modernization branch. - Add `nodejs` and `yarn` to `.tool-versions` so contributors get the Node toolchain that `bin/lint` needs without manually installing it. - Trim the CHANGELOG matrix line to match the actual nine combos in `.github/workflows/specs.yml`. - Gemspec `summary` is now an actual one-liner; `description` describes the surface area users will install for. - Fix two long-standing comment typos (`Namesapce`, `ednpoints`) and the missing default arg on `Client#unarchive_list_item`. - README contributing block points at `bin/lint` instead of `bundle exec rubocop` and clarifies which command does what.
Use Castle's own product framing instead of "Castle protects your users from account compromise". The previous text predated several major product lines; the new copy is plain, factual, and lists the gem's actual responsibilities without enumerating individual endpoints.
@prettier/plugin-ruby is fully functional with prettier 3.x. The crash CodeRabbit's hint guarded against (non-deterministic load order via `Dir['...']`) doesn't apply on our supported runtimes either: Ruby 3.0+ already returns Dir.glob results sorted by default, and the gem requires Ruby >= 3.2 — RuboCop's Lint/RedundantDirGlobSort enforces that we don't add a redundant `.sort` call.
zuchmanski
approved these changes
May 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
9.0.0 — release prep
This is the consolidated
9.0.0release ofcastle-rb. The goal is to (a) bring the gem onto the modern Ruby/Rails matrix, (b) trim the API surface to only what the Castle HTTP API currently supports, and (c) close the two open GitHub issues against this repo.Public API
Removed (breaking)
Castle::API::TrackandCastle::Client#track— superseded byCastle::API::LogandCastle::API::Risk.Castle::API::AuthenticateandCastle::Client#authenticate— superseded byCastle::API::Risk.ApproveDevice,GetDevice,GetDevicesForUser,ReportDevice) — no longer part of the Castle public API.StartImpersonation,EndImpersonation) and theCastle::ImpersonationFailederror class.castle/support/hanami(only ever covered the long-EOL Hanami 1.x architecture) andcastle/support/padrino(negligible adoption, trivial to inline). README documents the three-line replacement.Added
Castle::API::ListItems::CreateBatch—POST /v1/lists/{list_id}/items/batch. Mirrored on the client asCastle::Client#create_batch_list_items.Castle::API::Privacy::RequestDataandCastle::API::Privacy::DeleteData— currentPOST/DELETE /v1/privacy/usersendpoints, taking{ identifier:, identifier_type: }. Mirrored on the client as#request_user_data/#delete_user_data. The deprecated path-based variants (/v1/privacy/users/{id}) are intentionally not exposed.Bug fixes
Castle::API::Risk/Filter/Logfailover handlers no longer raiseNoMethodErrorwhenoptions[:user]is missing. Closes Error handler in Filter.call assumes presence of deprecated user[:id] node #279.Filteradditionally falls back tomatching_user_id.Issues closed
user[:id].Tooling and infrastructure
< 3.2(required_ruby_version >= 3.2in the gemspec).appraisalwith hand-maintainedgemfiles/rails_*.gemfilefor each supported Rails version..ruby-gemsetto asdf-style.tool-versions..rubocop.yml(drop deprecatedprettier-rubocopinherit, target Ruby 3.2, addrubocop-rake).byebugwith stdlibdebug; dropcoveralls_rebornin favor ofsimplecovdirectly.bin/lintdriving RuboCop + Prettier; addpackage.jsonwithprettierand@prettier/plugin-ruby..coditsu/ci.ymland the matching CircleCI job).source_code_uri,changelog_uri,bug_tracker_uri,rubygems_mfa_required).base64andostruct, both of which left the default gems in Ruby 3.4+.Documentation