Skip to content

Commit 5bda901

Browse files
committed
Modernize deprecation-tracking.md for next_rails v1.5.0
Replace the ~25-line manual Ruby merge script (obsolete as of v1.5.0) with the parallel-CI-native pattern: - `node_index:` option on `track_rspec` / `track_minitest`. Can be passed explicitly (`node_index: ENV["CI_NODE_INDEX"]`) or auto-detected from standard CI env vars (`CIRCLE_NODE_INDEX`, `BUILDKITE_PARALLEL_JOB`, `SEMAPHORE_JOB_INDEX`, `CI_NODE_INDEX`). - `deprecations merge --delete-shards` CLI command for the fan-in step that combines per-node shards into the canonical shitlist. - `DeprecationTracker.merge_shards(path, delete_shards: true)` programmatic form for custom CI flows. - `--next` flag for handling the next-Rails shitlist separately. The three-phase CI workflow (save per node → merge once → compare per node) is documented with runnable examples. Platform-specific env var mapping covers CircleCI, Buildkite, GitLab CI, Semaphore, and generic CI. Also adds: - Minimum `next_rails >= 1.5.0` callout at the top of Step 2, plus an updated Limitations note. - "Last-resort fallback" section at the bottom with `bundle exec rspec 2>&1 | grep 'DEPRECATION WARNING' | sort -u` for apps where `DeprecationTracker` won't configure (loses structure, compare mode, and regression gating — use the tracker when possible). - `deprecations merge --delete-shards` row in the Quick Reference. Preserves: Why This Matters, silenced-deprecations detection (Step 1), custom-deprecation-behavior approach for Minitest/parallel-fork and older Rails (Step 3, all four Rails version variants retained), Limitations, Maintenance, Quick Reference. Implements plan §12.
1 parent 12abde0 commit 5bda901

1 file changed

Lines changed: 118 additions & 42 deletions

File tree

dual-boot/references/deprecation-tracking.md

Lines changed: 118 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
## Why This Matters
88

9-
Deprecation warnings are your roadmap during a Rails upgrade — they tell you exactly what will break in the next version. If deprecations are silenced, you're flying blind. Before setting up dual-boot, you must ensure deprecation warnings are visible and tracked.
9+
Deprecation warnings are your roadmap during a Rails upgrade. They tell you exactly what will break in the next version. If deprecations are silenced, you're flying blind. Before setting up dual-boot, you must ensure deprecation warnings are visible and tracked.
1010

1111
---
1212

@@ -17,16 +17,16 @@ Check these files for silenced deprecations:
1717
### Check `config/environments/test.rb`
1818

1919
```ruby
20-
# BAD deprecations are invisible
20+
# BAD (deprecations are invisible)
2121
config.active_support.deprecation = :silence
2222

23-
# BAD all deprecation behaviors disabled (Rails 7.0+)
23+
# BAD (all deprecation behaviors disabled, Rails 7.0+)
2424
config.active_support.report_deprecations = false
2525
```
2626

2727
### Check `config/environments/development.rb`
2828

29-
Same patterns as above `:silence` or `report_deprecations = false`.
29+
Same patterns as above: `:silence` or `report_deprecations = false`.
3030

3131
### Check for Global Silencing
3232

@@ -60,19 +60,21 @@ If any of these are silencing deprecations, fix them before proceeding with the
6060

6161
The `next_rails` gem includes `DeprecationTracker` which captures all deprecation warnings during test runs and saves them to a file. This gives you a complete inventory without stopping on the first failure (unlike `:raise`).
6262

63+
> **Minimum `next_rails` version: 1.5.0** (released 2026-04-02). The parallel-CI-aware `node_index:` option and the `deprecations merge` CLI require v1.5.0. If your Gemfile currently pins an older version, update it before proceeding.
64+
6365
### Setup
6466

65-
Ensure `next_rails` is in your Gemfile at the **root level** (not inside a `group` block):
67+
Ensure `next_rails` is in your Gemfile at the **root level** (not inside a `group` block), with the version constraint:
6668

6769
```ruby
6870
# Gemfile
69-
# MUST be at root level not in :development or :test group
70-
gem 'next_rails'
71+
# MUST be at root level, not in :development or :test group
72+
gem 'next_rails', '>= 1.5.0'
7173
```
7274

7375
Then configure your test runner.
7476

75-
**RSpec** in `spec/spec_helper.rb` (or `spec/rails_helper.rb`):
77+
**RSpec**, in `spec/spec_helper.rb` (or `spec/rails_helper.rb`):
7678

7779
```ruby
7880
RSpec.configure do |config|
@@ -85,7 +87,7 @@ RSpec.configure do |config|
8587
end
8688
```
8789

88-
**Minitest** in `test/test_helper.rb`:
90+
**Minitest**, in `test/test_helper.rb`:
8991

9092
```ruby
9193
DeprecationTracker.track_minitest(
@@ -95,19 +97,19 @@ DeprecationTracker.track_minitest(
9597
)
9698
```
9799

98-
- **`shitlist_path`** where to save/read the deprecation inventory (required, no default)
99-
- **`mode`** defaults to `save` so it always tracks
100-
- **`transform_message`** strips the Rails root path from messages so the shitlist is portable across environments
100+
- **`shitlist_path`**: where to save/read the deprecation inventory (required, no default)
101+
- **`mode`**: defaults to `save` so it always tracks
102+
- **`transform_message`**: strips the Rails root path from messages so the shitlist is portable across environments
101103

102104
### Save the Deprecation Shitlist
103105

104-
Run the test suite with `DEPRECATION_TRACKER=save` to collect all deprecation warnings (substitute your project's test command e.g. `bundle exec rspec`, `bin/rails test`, `bin/test`):
106+
Run the test suite with `DEPRECATION_TRACKER=save` to collect all deprecation warnings (substitute your project's test command, e.g. `bundle exec rspec`, `bin/rails test`, `bin/test`):
105107

106108
```bash
107109
DEPRECATION_TRACKER=save bundle exec rspec
108110
```
109111

110-
This generates `spec/support/deprecation_warning.shitlist.json` a JSON file listing every unique deprecation warning found during the run.
112+
This generates `spec/support/deprecation_warning.shitlist.json`, a JSON file listing every unique deprecation warning found during the run.
111113

112114
> **Note:** Make sure the directory for `shitlist_path` exists before running.
113115
@@ -117,55 +119,116 @@ Once you have a shitlist, run with `DEPRECATION_TRACKER=save` after fixing depre
117119

118120
### Workflow
119121

120-
1. `DEPRECATION_TRACKER=save bundle exec rspec` generates initial shitlist
122+
1. `DEPRECATION_TRACKER=save bundle exec rspec` generates the initial shitlist
121123
2. Review the shitlist to understand the scope of deprecations
122124
3. Fix one category of deprecation (e.g., `update_attributes`)
123-
4. `DEPRECATION_TRACKER=save bundle exec rspec` updates the shitlist (it should shrink)
125+
4. `DEPRECATION_TRACKER=save bundle exec rspec` updates the shitlist (it should shrink)
124126
5. Repeat until the shitlist is empty
125127
6. Commit the shitlist file so the team tracks progress together
126128

127129
### CI with Parallel Test Execution
128130

129-
`DeprecationTracker` does not natively support parallel execution. When CI splits tests across multiple nodes/containers (e.g., CircleCI parallelism, parallel_tests gem), each node only sees its subset of deprecations. You need to collect and merge the results.
131+
Starting with `next_rails` v1.5.0, `DeprecationTracker` has native support for parallel CI. When CI splits tests across multiple nodes or containers (CircleCI parallelism, Buildkite, GitLab, Semaphore, GitHub Actions matrix), each node writes its own shard file. A one-off merge step fans the shards back into the canonical shitlist.
130132

131-
**Strategy: Save per-node, merge after**
133+
#### Configure `node_index:`
132134

133-
1. On each CI node, run with `DEPRECATION_TRACKER=save` as normal
134-
2. Each node produces its own `spec/support/deprecation_warning.shitlist.json`
135-
3. After all nodes finish, collect and merge the shitlist files:
135+
Pass a `node_index:` value to `track_rspec` or `track_minitest`. When set, the tracker writes to `<shitlist_path>.node-<index>.json` instead of the canonical file, so parallel nodes don't clobber each other.
136136

137-
```bash
138-
# Example merge script (run after all parallel nodes complete)
139-
# Collects shitlist JSON files from each node and merges them
137+
You can pass `node_index:` explicitly, or omit it and rely on auto-detection from standard CI environment variables:
140138

141-
require 'json'
139+
| CI platform | Env var auto-detected |
140+
|---|---|
141+
| CircleCI | `CIRCLE_NODE_INDEX` |
142+
| Buildkite | `BUILDKITE_PARALLEL_JOB` |
143+
| GitLab CI | `CI_NODE_INDEX` |
144+
| Semaphore | `SEMAPHORE_JOB_INDEX` |
145+
| Generic | `CI_NODE_INDEX` |
142146

143-
shitlists = Dir.glob("node-*/spec/support/deprecation_warning.shitlist.json")
144-
merged = {}
147+
**RSpec** with explicit `node_index:`:
145148

146-
shitlists.each do |file|
147-
data = JSON.parse(File.read(file))
148-
data.each do |key, values|
149-
merged[key] ||= []
150-
merged[key] = (merged[key] + values).uniq
149+
```ruby
150+
RSpec.configure do |config|
151+
if ENV["DEPRECATION_TRACKER"]
152+
DeprecationTracker.track_rspec(
153+
config,
154+
shitlist_path: "spec/support/deprecation_warning.shitlist.json",
155+
mode: ENV.fetch("DEPRECATION_TRACKER", "save"),
156+
node_index: ENV["CI_NODE_INDEX"],
157+
transform_message: -> (message) { message.gsub("#{Rails.root}/", "") }
158+
)
151159
end
152160
end
161+
```
153162

154-
File.write("spec/support/deprecation_warning.shitlist.json", JSON.pretty_generate(merged))
163+
If your CI sets one of the env vars in the table above, you can drop the `node_index:` argument entirely and let auto-detection handle it.
164+
165+
**Minitest** with the same pattern:
166+
167+
```ruby
168+
DeprecationTracker.track_minitest(
169+
shitlist_path: "test/support/deprecation_warning.shitlist.json",
170+
mode: ENV.fetch("DEPRECATION_TRACKER", "save"),
171+
node_index: ENV["CI_NODE_INDEX"],
172+
transform_message: -> (message) { message.gsub("#{Rails.root}/", "") }
173+
)
174+
```
175+
176+
#### Three-phase CI workflow
177+
178+
**Save phase** (each parallel node writes a shard):
179+
180+
```bash
181+
DEPRECATION_TRACKER=save CI_NODE_INDEX=$NODE bundle exec rspec <subset>
155182
```
156183

157-
The exact collection mechanism depends on your CI setup:
158-
- **CircleCI**: Use `persist_to_workspace` / `attach_workspace` to gather shitlists from parallel containers
159-
- **GitHub Actions**: Use `upload-artifact` / `download-artifact` across matrix jobs
160-
- **parallel_tests gem**: Each process writes to the same filesystem, so you can glob the results directly
184+
Each node produces a file like `spec/support/deprecation_warning.shitlist.node-0.json`, `...node-1.json`, etc.
185+
186+
**Merge phase** (runs once after all nodes finish, with shards gathered into one workspace):
187+
188+
```bash
189+
deprecations merge --delete-shards
190+
```
161191

162-
> **Tip:** Run the initial `DEPRECATION_TRACKER=save` locally (non-parallel) to generate the first complete shitlist. Use the parallel merge strategy in CI for ongoing regression checks.
192+
This fans all shards into the canonical `spec/support/deprecation_warning.shitlist.json` and removes the per-node files. To merge the next-Rails shitlist (the shards produced when running under the next Rails version in dual-boot), pass `--next`:
193+
194+
```bash
195+
deprecations merge --next --delete-shards
196+
```
197+
198+
For custom flows, call the programmatic form directly:
199+
200+
```ruby
201+
DeprecationTracker.merge_shards(
202+
"spec/support/deprecation_warning.shitlist.json",
203+
delete_shards: true
204+
)
205+
```
206+
207+
**Compare phase** (each parallel node compares its subset against the merged canonical file):
208+
209+
```bash
210+
DEPRECATION_TRACKER=compare CI_NODE_INDEX=$NODE bundle exec rspec <subset>
211+
```
212+
213+
The shard/canonical split means compare mode works per-node without false positives: each node only checks deprecations it actually emits, and the canonical shitlist already contains every deprecation from every node.
214+
215+
#### Gathering shards between phases
216+
217+
The exact mechanism to move shards from save-phase nodes into the merge job depends on your CI:
218+
219+
- **CircleCI**: use `persist_to_workspace` in each parallel container, `attach_workspace` in the merge job
220+
- **GitHub Actions**: use `upload-artifact` from each matrix job, `download-artifact` in the merge job
221+
- **Buildkite**: use pipeline artifacts (`buildkite-agent artifact upload` / `download`)
222+
- **GitLab CI**: use job artifacts with `dependencies:` in the merge job
223+
- **parallel_tests gem (single host)**: each process writes to the same filesystem, so the merge step can run immediately without any artifact shuffle
224+
225+
> **Tip:** Run the initial `DEPRECATION_TRACKER=save` locally without `node_index:` to generate the first complete canonical shitlist. Use the parallel sharded workflow in CI for ongoing regression checks.
163226
164227
### Limitations
165228

166-
- **Not compatible with `minitest/parallel_fork`** — use standard minitest or RSpec
167-
- **No native parallel support** — requires manual merge when using CI parallelism (see above)
168-
- **Supports RSpec (`track_rspec`) and Minitest (`track_minitest`)** — other frameworks require manual integration
229+
- **Not compatible with `minitest/parallel_fork`.** Use standard Minitest, RSpec, or the custom deprecation behavior approach below.
230+
- **Requires `next_rails` >= 1.5.0** for native parallel CI support. Older versions need a manual merge script.
231+
- **Supports RSpec (`track_rspec`) and Minitest (`track_minitest`).** Other frameworks require manual integration.
169232

170233
---
171234

@@ -197,7 +260,7 @@ config.active_support.deprecation = :log
197260
```ruby
198261
# config/initializers/custom_deprecation_behavior.rb
199262
ActiveSupport::Deprecation.behavior = ->(message, callstack, deprecation_horizon, gem_name) {
200-
# Deprecations you've already fixed raise if they reappear
263+
# Deprecations you've already fixed, raise if they reappear
201264
disallowed = [
202265
"update_attributes",
203266
/before_filter/,
@@ -277,4 +340,17 @@ After completing the upgrade:
277340
| Current behavior | `grep -rn "active_support.deprecation" .` |
278341
| Custom behaviors | `grep -rn "Deprecation.behavior" .` |
279342
| Save deprecation inventory | `DEPRECATION_TRACKER=save bundle exec rspec` |
343+
| Merge parallel shards | `deprecations merge --delete-shards` |
280344
| Existing warnings | `bundle exec rspec 2>&1 \| grep "DEPRECATION WARNING"` |
345+
346+
---
347+
348+
## Last-resort fallback
349+
350+
If `DeprecationTracker` won't install or configure on your app (rare, see Limitations), you can capture deprecations by tailing the test log:
351+
352+
```bash
353+
bundle exec rspec 2>&1 | grep "DEPRECATION WARNING" | sort -u
354+
```
355+
356+
This loses structure, compare mode, and regression gating. Use `DeprecationTracker` when possible.

0 commit comments

Comments
 (0)