reviewer 0.1.4 → 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.
- checksums.yaml +4 -4
- data/.alexignore +1 -0
- data/.github/FUNDING.yml +3 -0
- data/.github/workflows/main.yml +81 -11
- data/.github/workflows/release.yml +98 -0
- data/.gitignore +1 -1
- data/.inch.yml +3 -1
- data/.reek.yml +175 -0
- data/.reviewer.example.yml +27 -12
- data/.reviewer.future.yml +221 -0
- data/.reviewer.yml +191 -28
- data/.reviewer_stdout +0 -0
- data/.rubocop.yml +34 -1
- data/CHANGELOG.md +42 -2
- data/Gemfile +39 -1
- data/Gemfile.lock +294 -72
- data/README.md +315 -7
- data/RELEASING.md +190 -0
- data/Rakefile +117 -0
- data/dependency_decisions.yml +61 -0
- data/exe/fmt +1 -1
- data/exe/rvw +1 -1
- data/lib/reviewer/arguments/files.rb +60 -27
- data/lib/reviewer/arguments/keywords.rb +39 -43
- data/lib/reviewer/arguments/tags.rb +21 -14
- data/lib/reviewer/arguments.rb +107 -29
- data/lib/reviewer/batch/formatter.rb +87 -0
- data/lib/reviewer/batch.rb +46 -35
- data/lib/reviewer/capabilities.rb +81 -0
- data/lib/reviewer/command/string/env.rb +16 -6
- data/lib/reviewer/command/string/flags.rb +14 -5
- data/lib/reviewer/command/string.rb +53 -24
- data/lib/reviewer/command.rb +69 -39
- data/lib/reviewer/configuration/loader.rb +70 -0
- data/lib/reviewer/configuration.rb +14 -4
- data/lib/reviewer/context.rb +15 -0
- data/lib/reviewer/doctor/config_check.rb +46 -0
- data/lib/reviewer/doctor/environment_check.rb +58 -0
- data/lib/reviewer/doctor/formatter.rb +75 -0
- data/lib/reviewer/doctor/keyword_check.rb +85 -0
- data/lib/reviewer/doctor/opportunity_check.rb +88 -0
- data/lib/reviewer/doctor/report.rb +63 -0
- data/lib/reviewer/doctor/tool_inventory.rb +41 -0
- data/lib/reviewer/doctor.rb +28 -0
- data/lib/reviewer/history.rb +36 -12
- data/lib/reviewer/output/formatting.rb +40 -0
- data/lib/reviewer/output/printer.rb +105 -0
- data/lib/reviewer/output.rb +54 -65
- data/lib/reviewer/prompt.rb +38 -0
- data/lib/reviewer/report/formatter.rb +124 -0
- data/lib/reviewer/report.rb +100 -0
- data/lib/reviewer/runner/failed_files.rb +66 -0
- data/lib/reviewer/runner/formatter.rb +103 -0
- data/lib/reviewer/runner/guidance.rb +79 -0
- data/lib/reviewer/runner/result.rb +150 -0
- data/lib/reviewer/runner/strategies/captured.rb +232 -0
- data/lib/reviewer/runner/strategies/{verbose.rb → passthrough.rb} +15 -24
- data/lib/reviewer/runner.rb +179 -35
- data/lib/reviewer/session/formatter.rb +87 -0
- data/lib/reviewer/session.rb +208 -0
- data/lib/reviewer/setup/catalog.rb +233 -0
- data/lib/reviewer/setup/detector.rb +61 -0
- data/lib/reviewer/setup/formatter.rb +94 -0
- data/lib/reviewer/setup/gemfile_lock.rb +55 -0
- data/lib/reviewer/setup/generator.rb +54 -0
- data/lib/reviewer/setup/tool_block.rb +112 -0
- data/lib/reviewer/setup.rb +41 -0
- data/lib/reviewer/shell/result.rb +25 -11
- data/lib/reviewer/shell/timer.rb +47 -27
- data/lib/reviewer/shell.rb +46 -21
- data/lib/reviewer/tool/conversions.rb +20 -0
- data/lib/reviewer/tool/file_resolver.rb +54 -0
- data/lib/reviewer/tool/settings.rb +107 -56
- data/lib/reviewer/tool/test_file_mapper.rb +73 -0
- data/lib/reviewer/tool/timing.rb +78 -0
- data/lib/reviewer/tool.rb +88 -47
- data/lib/reviewer/tools.rb +47 -33
- data/lib/reviewer/version.rb +1 -1
- data/lib/reviewer.rb +114 -54
- data/reviewer.gemspec +21 -20
- data/structure.svg +1 -0
- metadata +113 -148
- data/.ruby-version +0 -1
- data/lib/reviewer/command/string/verbosity.rb +0 -51
- data/lib/reviewer/command/verbosity.rb +0 -65
- data/lib/reviewer/conversions.rb +0 -27
- data/lib/reviewer/guidance.rb +0 -73
- data/lib/reviewer/keywords/git/staged.rb +0 -48
- data/lib/reviewer/keywords/git.rb +0 -14
- data/lib/reviewer/keywords.rb +0 -9
- data/lib/reviewer/loader.rb +0 -59
- data/lib/reviewer/printer.rb +0 -25
- data/lib/reviewer/runner/strategies/quiet.rb +0 -90
data/README.md
CHANGED
|
@@ -1,18 +1,326 @@
|
|
|
1
|
+
# [Reviewer](https://github.com/garrettdimon/reviewer)
|
|
1
2
|
|
|
2
|
-
|
|
3
|
+
Frictionless code quality.
|
|
3
4
|
|
|
4
|
-
[](https://github.com/garrettdimon/reviewer/actions/workflows/main.yml)
|
|
6
|
+
[](https://codecov.io/gh/garrettdimon/reviewer)
|
|
7
|
+
[](https://rubygems.org/gems/reviewer)
|
|
5
8
|
|
|
6
|
-
|
|
9
|
+
Reviewer wraps your code quality tools — tests, linters, security audits, formatters — into a single command with a consistent interface. Configure once, run everywhere.
|
|
7
10
|
|
|
8
|
-
|
|
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.
|
|
9
12
|
|
|
10
|
-
|
|
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
|
+
```
|
|
24
|
+
|
|
25
|
+
**After:**
|
|
26
|
+
|
|
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
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
gem install reviewer
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Or add to your Gemfile:
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
gem 'reviewer'
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Requires Ruby 3.2+**
|
|
60
|
+
|
|
61
|
+
Then auto-generate `.reviewer.yml` from your `Gemfile.lock`:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
rvw init
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
Created .reviewer.yml
|
|
69
|
+
|
|
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.
|
|
11
319
|
|
|
12
320
|
## License
|
|
13
321
|
|
|
14
|
-
|
|
322
|
+
MIT License — see [LICENSE.txt](LICENSE.txt)
|
|
15
323
|
|
|
16
324
|
## Code of Conduct
|
|
17
325
|
|
|
18
|
-
|
|
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
|
data/exe/fmt
CHANGED