reviewer 0.1.5 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/workflows/main.yml +79 -11
  4. data/.github/workflows/release.yml +98 -0
  5. data/.gitignore +1 -1
  6. data/.inch.yml +3 -1
  7. data/.reek.yml +175 -0
  8. data/.reviewer.example.yml +7 -2
  9. data/.reviewer.yml +166 -40
  10. data/.rubocop.yml +34 -2
  11. data/CHANGELOG.md +42 -2
  12. data/Gemfile +39 -1
  13. data/Gemfile.lock +291 -70
  14. data/LICENSE.txt +20 -4
  15. data/README.md +310 -21
  16. data/RELEASING.md +190 -0
  17. data/Rakefile +117 -0
  18. data/dependency_decisions.yml +61 -0
  19. data/exe/fmt +1 -1
  20. data/exe/rvw +1 -1
  21. data/lib/reviewer/arguments/files.rb +47 -20
  22. data/lib/reviewer/arguments/keywords.rb +34 -41
  23. data/lib/reviewer/arguments/tags.rb +11 -11
  24. data/lib/reviewer/arguments.rb +100 -29
  25. data/lib/reviewer/batch/formatter.rb +87 -0
  26. data/lib/reviewer/batch.rb +32 -48
  27. data/lib/reviewer/capabilities.rb +81 -0
  28. data/lib/reviewer/command/string/env.rb +12 -6
  29. data/lib/reviewer/command/string/flags.rb +2 -4
  30. data/lib/reviewer/command/string.rb +47 -12
  31. data/lib/reviewer/command.rb +65 -10
  32. data/lib/reviewer/configuration/loader.rb +70 -0
  33. data/lib/reviewer/configuration.rb +6 -3
  34. data/lib/reviewer/context.rb +15 -0
  35. data/lib/reviewer/doctor/config_check.rb +46 -0
  36. data/lib/reviewer/doctor/environment_check.rb +58 -0
  37. data/lib/reviewer/doctor/formatter.rb +75 -0
  38. data/lib/reviewer/doctor/keyword_check.rb +85 -0
  39. data/lib/reviewer/doctor/opportunity_check.rb +88 -0
  40. data/lib/reviewer/doctor/report.rb +63 -0
  41. data/lib/reviewer/doctor/tool_inventory.rb +41 -0
  42. data/lib/reviewer/doctor.rb +28 -0
  43. data/lib/reviewer/history.rb +10 -17
  44. data/lib/reviewer/output/formatting.rb +40 -0
  45. data/lib/reviewer/output/printer.rb +70 -9
  46. data/lib/reviewer/output.rb +37 -78
  47. data/lib/reviewer/prompt.rb +38 -0
  48. data/lib/reviewer/report/formatter.rb +124 -0
  49. data/lib/reviewer/report.rb +100 -0
  50. data/lib/reviewer/runner/failed_files.rb +66 -0
  51. data/lib/reviewer/runner/formatter.rb +103 -0
  52. data/lib/reviewer/runner/guidance.rb +79 -0
  53. data/lib/reviewer/runner/result.rb +150 -0
  54. data/lib/reviewer/runner/strategies/captured.rb +98 -23
  55. data/lib/reviewer/runner/strategies/passthrough.rb +2 -11
  56. data/lib/reviewer/runner.rb +126 -40
  57. data/lib/reviewer/session/formatter.rb +87 -0
  58. data/lib/reviewer/session.rb +208 -0
  59. data/lib/reviewer/setup/catalog.rb +233 -0
  60. data/lib/reviewer/setup/detector.rb +61 -0
  61. data/lib/reviewer/setup/formatter.rb +94 -0
  62. data/lib/reviewer/setup/gemfile_lock.rb +55 -0
  63. data/lib/reviewer/setup/generator.rb +54 -0
  64. data/lib/reviewer/setup/tool_block.rb +112 -0
  65. data/lib/reviewer/setup.rb +41 -0
  66. data/lib/reviewer/shell/result.rb +14 -15
  67. data/lib/reviewer/shell/timer.rb +40 -35
  68. data/lib/reviewer/shell.rb +41 -12
  69. data/lib/reviewer/tool/conversions.rb +20 -0
  70. data/lib/reviewer/tool/file_resolver.rb +54 -0
  71. data/lib/reviewer/tool/settings.rb +88 -44
  72. data/lib/reviewer/tool/test_file_mapper.rb +73 -0
  73. data/lib/reviewer/tool/timing.rb +78 -0
  74. data/lib/reviewer/tool.rb +88 -69
  75. data/lib/reviewer/tools.rb +47 -33
  76. data/lib/reviewer/version.rb +1 -1
  77. data/lib/reviewer.rb +109 -50
  78. data/reviewer.gemspec +16 -19
  79. metadata +101 -142
  80. data/lib/reviewer/conversions.rb +0 -16
  81. data/lib/reviewer/guidance.rb +0 -77
  82. data/lib/reviewer/keywords/git/staged.rb +0 -64
  83. data/lib/reviewer/keywords/git.rb +0 -14
  84. data/lib/reviewer/keywords.rb +0 -9
  85. data/lib/reviewer/loader.rb +0 -59
  86. data/lib/reviewer/output/scrubber.rb +0 -48
  87. data/lib/reviewer/output/token.rb +0 -85
data/README.md CHANGED
@@ -1,37 +1,326 @@
1
1
  # [Reviewer](https://github.com/garrettdimon/reviewer)
2
- by [Garrett Dimon](https://garrettdimon.com)
3
2
 
4
- ---
3
+ Frictionless code quality.
5
4
 
6
- **Note:** As of December 2021, Reviewer is a work in progress. While it's working great reviewing its own code, it's not quite ready for wider usage. Once, it's ready, it will provide more helpful installation and usage details.
5
+ [![build](https://github.com/garrettdimon/reviewer/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/garrettdimon/reviewer/actions/workflows/main.yml)
6
+ [![coverage](https://img.shields.io/codecov/c/github/garrettdimon/reviewer?token=UuXUlQAA2e)](https://codecov.io/gh/garrettdimon/reviewer)
7
+ [![gem version](https://img.shields.io/gem/v/reviewer)](https://rubygems.org/gems/reviewer)
8
+
9
+ Reviewer wraps your code quality tools — tests, linters, security audits, formatters — into a single command with a consistent interface. Configure once, run everywhere.
10
+
11
+ Reviewer works with any command-line tool but is built for Ruby projects. Auto-setup detects tools from `Gemfile.lock`, and file mapping supports Minitest and RSpec conventions.
12
+
13
+ ## Before & After
14
+
15
+ **Before** — five separate commands, each with their own flags:
16
+
17
+ ```
18
+ bundle exec bundle-audit check --no-update
19
+ bundle exec rake test
20
+ bundle exec rubocop --parallel
21
+ bundle exec fasterer
22
+ bundle exec reek lib/
23
+ ```
7
24
 
8
- ---
25
+ **After:**
9
26
 
10
- *With Reviewer, you can seamlessly use multiple automated code review tools with orders of magnitude less friction so you can use them more frequently and consistently.*
27
+ ```
28
+ rvw
29
+ ```
30
+
31
+ ```
32
+ Bundle Audit Review Gem Dependencies for Security Issues
33
+ ↳ bundle exec bundle-audit check --no-update
34
+ Success 0.8s
35
+
36
+ Minitest Unit Tests & Coverage
37
+ ↳ bundle exec rake test
38
+ Success 4.2s
39
+
40
+ RuboCop Review Ruby Syntax & Formatting for Consistency
41
+ ↳ bundle exec rubocop --parallel
42
+ Success 1.1s
43
+
44
+ ✓ ~6.1 seconds for 3 tools
45
+ ```
46
+
47
+ ## Install & Setup
11
48
 
12
- So instead of remembering and typing...
13
49
  ```bash
14
- $ yarn audit --level moderate
15
- $ bundle exec bundle-audit check --no-update
16
- $ bundle exec rubocop --parallel
17
- $ bundle exec erblint --lint-all --enable-all-linters
18
- $ yarn stylelint .
19
- $ yarn eslint .
50
+ gem install reviewer
20
51
  ```
21
- ...you could just type...
52
+
53
+ Or add to your Gemfile:
54
+
55
+ ```ruby
56
+ gem 'reviewer'
22
57
  ```
23
- $ rvw
58
+
59
+ **Requires Ruby 3.2+**
60
+
61
+ Then auto-generate `.reviewer.yml` from your `Gemfile.lock`:
62
+
63
+ ```bash
64
+ rvw init
24
65
  ```
25
66
 
26
- That's just the tip of the iceberg, though. For the full story on Reviewer's capabilities and benefits, the [Overview](https://github.com/garrettdimon/reviewer/wiki/Overview) is the best place to start. Or if you'd like to see how it's configured under the hood, the [Configuration Instructions](https://github.com/garrettdimon/reviewer/wiki/Configuration) go even deeper.
67
+ ```
68
+ Created .reviewer.yml
27
69
 
28
- [![build](https://github.com/garrettdimon/reviewer/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/garrettdimon/reviewer/actions/workflows/main.yml)
29
- [![coverage](https://img.shields.io/codecov/c/github/garrettdimon/reviewer?token=UuXUlQAA2e)](https://codecov.io/gh/garrettdimon/reviewer)
30
- [![last commit](https://img.shields.io/github/last-commit/garrettdimon/reviewer/main)](https://github.com/garrettdimon/reviewer/commits/main)
31
- [![gem version](https://img.shields.io/gem/v/reviewer)](https://rubygems.org/gems/reviewer)
70
+ Detected tools:
71
+ Bundle Audit bundler-audit in Gemfile.lock
72
+ RuboCop rubocop in Gemfile.lock, .rubocop.yml
73
+ Minitest minitest in Gemfile.lock, test/ directory
74
+
75
+ Configure further: https://github.com/garrettdimon/reviewer#configuration
76
+ Run `rvw` to review your code.
77
+ ```
78
+
79
+ Now run it:
80
+
81
+ ```bash
82
+ rvw
83
+ ```
84
+
85
+ ## Usage
86
+
87
+ ### Run a single tool without remembering its flags
88
+
89
+ ```bash
90
+ rvw rubocop
91
+ ```
92
+
93
+ Instead of `bundle exec rubocop --parallel`, use the YAML key. Reviewer applies your configured flags and options automatically.
94
+
95
+ ### Run a subset of tools by tag
96
+
97
+ ```bash
98
+ rvw security
99
+ ```
100
+
101
+ Instead of maintaining lists of which tools to run in which context, tag them in `.reviewer.yml` and filter on the fly:
102
+
103
+ ```
104
+ rvw security ─── bundle-audit check --no-update
105
+ └── brakeman --no-pager -q
106
+ ```
107
+
108
+ Tags work as positional args or with `-t ruby`. Tag ideas: language (`ruby`, `css`), purpose (`security`, `syntax`), speed (`fast`, `slow`), context (`ci`, `pr`).
109
+
110
+ ### Review only staged files
111
+
112
+ ```bash
113
+ rvw staged
114
+ ```
115
+
116
+ Instead of figuring out each tool's syntax for targeting files, use a keyword. Reviewer resolves git status, filters by each tool's file pattern, maps source files to test files, and applies each tool's file-passing syntax:
117
+
118
+ ```
119
+ rvw staged ─── rubocop lib/reviewer.rb lib/reviewer/batch.rb
120
+ ├── rake test TEST=test/reviewer_test.rb test/reviewer/batch_test.rb
121
+ └── fasterer lib/reviewer.rb lib/reviewer/batch.rb
122
+ ```
123
+
124
+ One command. Three tools. Each gets only its relevant files in its expected format.
125
+
126
+ Also: `unstaged`, `modified`, `untracked`.
127
+
128
+ ### Target specific files
129
+
130
+ ```bash
131
+ rvw -f app/models/user.rb,test/models/user_test.rb
132
+ ```
133
+
134
+ Pass files once. Reviewer handles whether the tool expects a flag, a bare path, or something else.
135
+
136
+ ### Re-run only what failed
137
+
138
+ ```bash
139
+ rvw failed
140
+ ```
141
+
142
+ Reviewer tracks which tools failed. Fix the issue, re-run only those.
143
+
144
+ ### Combine everything
145
+
146
+ ```bash
147
+ rvw rubocop staged
148
+ rvw -t ruby modified
149
+ rvw tests -f test/models/user_test.rb
150
+ ```
151
+
152
+ Tools, tags, keywords, and files compose naturally.
153
+
154
+ ### Auto-fix with formatters
155
+
156
+ ```bash
157
+ fmt
158
+ fmt rubocop staged
159
+ ```
160
+
161
+ Same interface as `rvw`, but runs the `format` command for each tool. Only tools with a `format` command configured will run.
162
+
163
+ ### Output formats
164
+
165
+ | Flag | Format | Use case |
166
+ |------|--------|----------|
167
+ | _(default)_ | Streaming | Development — see output as it runs |
168
+ | `--format summary` | Summary | Quick pass/fail with timing per tool |
169
+ | `-j` / `--json` | JSON | CI, scripting, agent integration |
170
+ | `-r` / `--raw` | Raw | Force direct output, no capturing |
171
+
172
+ ## Configuration
173
+
174
+ ### Minimal example
175
+
176
+ The only requirement is a `review` command:
177
+
178
+ ```yaml
179
+ rubocop:
180
+ commands:
181
+ review: bundle exec rubocop --parallel
182
+ ```
183
+
184
+ ### Full example
185
+
186
+ ```yaml
187
+ rubocop:
188
+ name: RuboCop
189
+ description: Review Ruby syntax and formatting for consistency
190
+ tags: [ruby, syntax]
191
+ commands:
192
+ install: bundle exec gem install rubocop
193
+ prepare: bundle exec rubocop --regenerate-todo
194
+ review: bundle exec rubocop --parallel
195
+ format: bundle exec rubocop --auto-correct
196
+ files:
197
+ flag: ""
198
+ separator: " "
199
+ pattern: "*.rb"
200
+ map_to_tests: minitest
201
+ links:
202
+ home: https://rubocop.org
203
+ install: https://docs.rubocop.org/rubocop/installation.html
204
+ env:
205
+ RUBOCOP_OPTS: --color
206
+ flags:
207
+ color:
208
+ ```
209
+
210
+ ### Options reference
211
+
212
+ | Option | Description |
213
+ |--------|-------------|
214
+ | `name` | Display name |
215
+ | `description` | What the tool does |
216
+ | `tags` | Categories for filtering (`[ruby, security]`) |
217
+ | `skip_in_batch` | Set `true` to exclude from `rvw` but still run with `rvw tool_name` |
218
+ | `commands.review` | Command to run for `rvw` **(required)** |
219
+ | `commands.format` | Command to run for `fmt` |
220
+ | `commands.install` | Command to install the tool |
221
+ | `commands.prepare` | Command to run before review (cached 6 hours) |
222
+ | `commands.max_exit_status` | Treat exit codes up to this value as success |
223
+ | `files.review` | Command to use instead of `commands.review` when files are scoped |
224
+ | `files.format` | Command to use instead of `commands.format` when files are scoped |
225
+ | `files.flag` | CLI flag for passing files (empty string = bare paths) |
226
+ | `files.separator` | How to join multiple file paths (default: space) |
227
+ | `files.pattern` | Glob pattern to filter files (e.g., `*.rb`) |
228
+ | `files.map_to_tests` | Map source files to test files (`minitest` or `rspec`) |
229
+ | `links.home` | Project homepage |
230
+ | `links.install` | Installation instructions |
231
+ | `env` | Environment variables to set when running |
232
+ | `flags` | CLI flags to append to the review command |
233
+
234
+ ### File-scoped commands
235
+
236
+ Some tools use different commands for running the full suite vs. targeting specific files. Use `files.review` (or `files.format`) to specify an alternative command when files are passed:
237
+
238
+ ```yaml
239
+ tests:
240
+ commands:
241
+ review: bundle exec rake test
242
+ files:
243
+ review: bundle exec ruby -Itest
244
+ pattern: "*_test.rb"
245
+ map_to_tests: minitest
246
+ ```
247
+
248
+ `rvw` runs `bundle exec rake test` (full suite). `rvw staged` or `rvw tests -f test/models/user_test.rb` runs `bundle exec ruby -Itest` with the resolved files appended. The standard `files.flag` and `files.separator` still apply when appending files to the file-scoped command.
249
+
250
+ ### Notes
251
+
252
+ - **Tool ordering** — Tools run in the order they appear in `.reviewer.yml`. Put fast tools first for quicker feedback.
253
+ - **Environment variables** — Use `env` for things like `TESTOPTS: --seed=$SEED`. The `$SEED` placeholder is replaced with a consistent random seed across runs.
254
+ - **Flags** — Keys with no value become boolean flags (`color:` becomes `--color`). Keys with values become `--key value`.
255
+ - **Prepare caching** — The `prepare` command only runs if it hasn't been run in the last 6 hours, saving time on commands like `bundle-audit update`.
256
+
257
+ ## Workflows
258
+
259
+ ### Pre-commit
260
+
261
+ Review only what you're about to commit:
262
+
263
+ ```bash
264
+ rvw staged
265
+ ```
266
+
267
+ ### Pull request
268
+
269
+ Review everything that changed:
270
+
271
+ ```bash
272
+ rvw modified
273
+ ```
274
+
275
+ ### CI
276
+
277
+ Full review with JSON output for parsing:
278
+
279
+ ```bash
280
+ rvw --json
281
+ ```
282
+
283
+ Reviewer exits `0` when all tools pass, or with the highest exit status from any failing tool. Skipped and missing tools don't affect the exit code. This means `rvw` works directly as a CI gate — no wrapper script needed.
284
+
285
+ ### Development
286
+
287
+ Run the full suite:
288
+
289
+ ```bash
290
+ rvw
291
+ ```
292
+
293
+ ### Hotfix
294
+
295
+ Run just security and tests on changed files:
296
+
297
+ ```bash
298
+ rvw -t security modified
299
+ rvw tests modified
300
+ ```
301
+
302
+ ### After a failure
303
+
304
+ Fix the issue, then re-run only what failed:
305
+
306
+ ```bash
307
+ rvw failed
308
+ ```
309
+
310
+ ## Agent Integration
311
+
312
+ For AI agents and automation tools, use `--capabilities` to discover available tools:
313
+
314
+ ```bash
315
+ rvw --capabilities
316
+ ```
317
+
318
+ This outputs JSON describing all configured tools, keywords, and common scenarios.
32
319
 
33
320
  ## License
34
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
321
+
322
+ MIT License — see [LICENSE.txt](LICENSE.txt)
35
323
 
36
324
  ## Code of Conduct
37
- Everyone interacting in the Reviewer project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/reviewer/blob/master/CODE_OF_CONDUCT.md).
325
+
326
+ See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)
data/RELEASING.md ADDED
@@ -0,0 +1,190 @@
1
+ # Releasing Reviewer
2
+
3
+ ## Quick Reference
4
+
5
+ ```bash
6
+ # 1. Update version and changelog
7
+ # 2. Commit and push
8
+ git add -A && git commit -m "Release vX.Y.Z" && git push
9
+
10
+ # 3. Tag and push
11
+ git tag vX.Y.Z && git push origin vX.Y.Z
12
+
13
+ # Done - automation handles the rest
14
+ ```
15
+
16
+ ## How It Works
17
+
18
+ Releases are fully automated via GitHub Actions:
19
+
20
+ 1. **Branch protection** requires CI to pass before merging to `main`
21
+ 2. When you push a version tag, the release workflow:
22
+ - Validates the tag points to a commit on `main` (ensures CI passed)
23
+ - Builds and publishes the gem to RubyGems
24
+ - Creates a GitHub Release with changelog excerpt
25
+
26
+ No redundant test runs. If it's on `main`, it already passed CI.
27
+
28
+ ## Release Steps
29
+
30
+ ### 1. Update Version
31
+
32
+ Edit `lib/reviewer/version.rb`:
33
+
34
+ ```ruby
35
+ VERSION = 'X.Y.Z'
36
+ ```
37
+
38
+ ### 2. Update CHANGELOG
39
+
40
+ Move items from `[Unreleased]` to a new version section:
41
+
42
+ ```markdown
43
+ ## [Unreleased]
44
+
45
+ ## [X.Y.Z] - YYYY-MM-DD
46
+
47
+ ### Added
48
+ - New feature description
49
+
50
+ ### Fixed
51
+ - Bug fix description
52
+ ```
53
+
54
+ ### 3. Commit, Tag, Push
55
+
56
+ ```bash
57
+ git add lib/reviewer/version.rb CHANGELOG.md
58
+ git commit -m "Release vX.Y.Z"
59
+ git push origin main
60
+ git tag vX.Y.Z
61
+ git push origin vX.Y.Z
62
+ ```
63
+
64
+ ### 4. Verify
65
+
66
+ - Watch the [Actions tab](https://github.com/garrettdimon/reviewer/actions) for workflow completion
67
+ - Check [RubyGems](https://rubygems.org/gems/reviewer) for the new version
68
+ - Check [GitHub Releases](https://github.com/garrettdimon/reviewer/releases) for the release page
69
+
70
+ ## Versioning Policy
71
+
72
+ Follow [Semantic Versioning](https://semver.org/):
73
+
74
+ - **MAJOR** (x.0.0): Breaking changes to public API or configuration
75
+ - **MINOR** (0.x.0): New features, deprecations
76
+ - **PATCH** (0.0.x): Bug fixes, documentation
77
+
78
+ ### What's a Breaking Change?
79
+
80
+ - Removing or renaming public classes/methods
81
+ - Changing method signatures incompatibly
82
+ - Changing default configuration behavior
83
+ - Dropping Ruby version support
84
+
85
+ ## Local Tools
86
+
87
+ ### Full Preflight Check
88
+
89
+ Run all checks before pushing:
90
+
91
+ ```bash
92
+ bundle exec rake release:preflight
93
+ ```
94
+
95
+ This runs tests, security audit, and release validation in sequence.
96
+
97
+ ### Individual Tasks
98
+
99
+ | Task | Purpose |
100
+ |------|---------|
101
+ | `rake release:preflight` | Run all checks (test, audit, check) |
102
+ | `rake release:check` | Validate version format, changelog entry, git state |
103
+ | `rake release:audit` | Check for vulnerable dependencies |
104
+ | `rake release:dry_run` | Build gem locally, show contents and size |
105
+ | `rake test` | Run test suite |
106
+
107
+ ### Preview a Release
108
+
109
+ Before tagging, preview what the gem will contain:
110
+
111
+ ```bash
112
+ bundle exec rake release:dry_run
113
+ ```
114
+
115
+ This builds the gem, displays its contents and size, then cleans up.
116
+
117
+ ## One-Time Setup
118
+
119
+ ### RubyGems Trusted Publishing
120
+
121
+ 1. Go to [rubygems.org/profile/oidc/pending_trusted_publishers](https://rubygems.org/profile/oidc/pending_trusted_publishers)
122
+ 2. Add trusted publisher:
123
+ - **Gem name:** `reviewer`
124
+ - **Repository owner:** `garrettdimon`
125
+ - **Repository name:** `reviewer`
126
+ - **Workflow filename:** `release.yml`
127
+ - **Environment:** `rubygems`
128
+
129
+ ### GitHub Environment
130
+
131
+ 1. Go to repository Settings > Environments
132
+ 2. Create environment named `rubygems`
133
+ 3. (Optional) Add required reviewers for extra safety
134
+
135
+ ### Repository Ruleset
136
+
137
+ 1. Go to repository Settings > Rules > Rulesets
138
+ 2. Edit the `main` ruleset (or create one targeting the default branch)
139
+ 3. Enable "Require status checks to pass" with these checks:
140
+ - `Security`
141
+ - `Test (Ruby 3.2)`
142
+ - `Test (Ruby 3.3)`
143
+ - `Test (Ruby 3.4)`
144
+ - `Test (Ruby 4.0)`
145
+ - `Changelog`
146
+ - `Version`
147
+
148
+ ## Troubleshooting
149
+
150
+ ### Release workflow fails with "must point to commit on main"
151
+
152
+ You tagged a commit that isn't on the main branch. Delete the tag and re-tag a commit on main:
153
+
154
+ ```bash
155
+ git tag -d vX.Y.Z # Delete local tag
156
+ git push origin :vX.Y.Z # Delete remote tag
157
+ git checkout main
158
+ git pull
159
+ git tag vX.Y.Z
160
+ git push origin vX.Y.Z
161
+ ```
162
+
163
+ ### "You do not have permission to push to this gem"
164
+
165
+ The RubyGems trusted publisher isn't configured, or the environment name doesn't match. Check the one-time setup steps above.
166
+
167
+ ### Forgot to update CHANGELOG
168
+
169
+ Delete the tag, update CHANGELOG, amend the commit, re-tag:
170
+
171
+ ```bash
172
+ git tag -d vX.Y.Z
173
+ git push origin :vX.Y.Z
174
+ # Update CHANGELOG.md
175
+ git add CHANGELOG.md
176
+ git commit --amend --no-edit
177
+ git push --force-with-lease origin main
178
+ git tag vX.Y.Z
179
+ git push origin vX.Y.Z
180
+ ```
181
+
182
+ ## Manual Release (Fallback)
183
+
184
+ If automation fails and you need to publish manually:
185
+
186
+ ```bash
187
+ bundle exec rake release
188
+ ```
189
+
190
+ This builds the gem and pushes to RubyGems using your local credentials.
data/Rakefile CHANGED
@@ -10,3 +10,120 @@ Rake::TestTask.new(:test) do |t|
10
10
  end
11
11
 
12
12
  task default: :test
13
+
14
+ # rubocop:disable Metrics/BlockLength
15
+ namespace :release do
16
+ desc 'Run bundle-audit to check for vulnerable dependencies'
17
+ task :audit do
18
+ puts 'Running security audit...'
19
+ sh 'bundle exec bundle-audit check --update'
20
+ end
21
+
22
+ desc 'Validate version, changelog, and git state before release'
23
+ task :check do
24
+ require_relative 'lib/reviewer/version'
25
+ errors = ReleaseChecker.new(Reviewer::VERSION).validate
26
+ if errors.any?
27
+ puts "\nRelease check failed:"
28
+ errors.each { |e| puts " - #{e}" }
29
+ exit 1
30
+ else
31
+ puts 'All release checks passed.'
32
+ end
33
+ end
34
+
35
+ desc 'Run all pre-release checks (tests, audit, release:check)'
36
+ task preflight: %i[test audit check] do
37
+ puts "\nAll preflight checks passed. Ready to release."
38
+ end
39
+
40
+ desc 'Build gem locally and show contents (dry run)'
41
+ task :dry_run do
42
+ require_relative 'lib/reviewer/version'
43
+ DryRun.new(Reviewer::VERSION).run
44
+ end
45
+ end
46
+ # rubocop:enable Metrics/BlockLength
47
+
48
+ # Validates release readiness
49
+ class ReleaseChecker
50
+ def initialize(version)
51
+ @version = version
52
+ @errors = []
53
+ end
54
+
55
+ def validate
56
+ puts "Checking release readiness for v#{@version}..."
57
+ check_version_format
58
+ check_changelog
59
+ check_git_clean
60
+ check_main_branch
61
+ @errors
62
+ end
63
+
64
+ private
65
+
66
+ def check_version_format
67
+ return if @version.match?(/\A\d+\.\d+\.\d+\z/)
68
+
69
+ @errors << "Version '#{@version}' is not valid semver (expected X.Y.Z)"
70
+ end
71
+
72
+ def check_changelog
73
+ changelog = File.read('CHANGELOG.md')
74
+ return if changelog.include?("[#{@version}]")
75
+
76
+ @errors << "CHANGELOG.md has no entry for version #{@version}"
77
+ end
78
+
79
+ def check_git_clean
80
+ return if `git status --porcelain`.empty?
81
+
82
+ @errors << 'Working directory has uncommitted changes'
83
+ end
84
+
85
+ def check_main_branch
86
+ current_branch = `git branch --show-current`.strip
87
+ return if current_branch == 'main'
88
+
89
+ @errors << "Not on main branch (currently on '#{current_branch}')"
90
+ end
91
+ end
92
+
93
+ # Builds gem and displays contents without publishing
94
+ class DryRun
95
+ def initialize(version)
96
+ @version = version
97
+ @gem_file = "reviewer-#{version}.gem"
98
+ end
99
+
100
+ def run
101
+ build || abort('Gem build failed')
102
+ show_contents
103
+ show_size
104
+ ensure
105
+ cleanup if File.exist?(@gem_file)
106
+ end
107
+
108
+ private
109
+
110
+ def build
111
+ puts "Building #{@gem_file}..."
112
+ system 'gem build reviewer.gemspec --silent'
113
+ end
114
+
115
+ def show_contents
116
+ puts "\nGem contents:"
117
+ system "tar -tf #{@gem_file}"
118
+ end
119
+
120
+ def show_size
121
+ size = File.size(@gem_file)
122
+ puts "\nGem size: #{(size / 1024.0).round(1)} KB"
123
+ end
124
+
125
+ def cleanup
126
+ File.delete(@gem_file)
127
+ puts "Cleaned up #{@gem_file}"
128
+ end
129
+ end
@@ -0,0 +1,61 @@
1
+ ---
2
+ - - :permit
3
+ - MIT
4
+ - :who:
5
+ :why: Standard permissive license
6
+ :versions: []
7
+ :when: 2025-12-22 19:15:00.637887000 Z
8
+ - - :permit
9
+ - Apache 2.0
10
+ - :who:
11
+ :why: Standard permissive license
12
+ :versions: []
13
+ :when: 2025-12-22 19:15:01.182813000 Z
14
+ - - :permit
15
+ - Simplified BSD
16
+ - :who:
17
+ :why: Standard permissive license
18
+ :versions: []
19
+ :when: 2025-12-22 19:15:01.670547000 Z
20
+ - - :permit
21
+ - Simplified BSD, ruby
22
+ - :who:
23
+ :why: Ruby standard library license
24
+ :versions: []
25
+ :when: 2025-12-22 19:15:02.138662000 Z
26
+ - - :permit
27
+ - ruby
28
+ - :who:
29
+ :why: Ruby license
30
+ :versions: []
31
+ :when: 2025-12-22 19:15:02.611904000 Z
32
+ - - :permit
33
+ - same as ruby's
34
+ - :who:
35
+ :why: Ruby license variant
36
+ :versions: []
37
+ :when: 2025-12-22 19:15:03.091953000 Z
38
+ - - :permit
39
+ - ISC
40
+ - :who:
41
+ :why: Standard permissive license
42
+ :versions: []
43
+ :when: 2025-12-22 19:15:03.570618000 Z
44
+ - - :permit
45
+ - BSD
46
+ - :who:
47
+ :why: Standard permissive license
48
+ :versions: []
49
+ :when: 2025-12-22 19:15:04.055284000 Z
50
+ - - :permit
51
+ - GPL-3.0-or-later
52
+ - :who:
53
+ :why: Acceptable for development tools
54
+ :versions: []
55
+ :when: 2025-12-22 19:15:12.998894000 Z
56
+ - - :approve
57
+ - brakeman
58
+ - :who:
59
+ :why: Development-only security scanner with custom license
60
+ :versions: []
61
+ :when: 2025-12-22 19:15:13.454537000 Z