rigortype 0.1.13 → 0.1.14
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/README.md +12 -2
- data/lib/rigor/environment/rbs_collection_discovery.rb +29 -7
- data/lib/rigor/environment/rbs_loader.rb +17 -0
- data/lib/rigor/environment.rb +13 -3
- data/lib/rigor/version.rb +1 -1
- data/sig/rigor/environment.rbs +1 -0
- data/skills/rigor-project-init/references/01-detect.md +42 -4
- data/skills/rigor-project-init/references/02-configure.md +17 -0
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +4 -3
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b1dbcd9b168a06cc8d5f26e0a096f19496c3cebdc8864fb56d966741b8a42577
|
|
4
|
+
data.tar.gz: 39e428c763e8d8cac6d9743d40d5d654bfaec56362d41e338a6dac974dff27cf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6cd6a4d0b52fcd2e1e2bacddd6120154ccbe075ef86615c38d7e6d1d942ea158f55abaeb03f01c0a23315a187076912315af98ce74faa4c174c4f6c5c98eac74
|
|
7
|
+
data.tar.gz: 1d7b600e3dd97a38bf1ac08231a7aa07635210723adf349c014d9100ccc2b0a868deb8a6479d99207e1c0b5e4cf07c158f0f0cb3bda325ec5c7d143e4f5ae90b
|
data/README.md
CHANGED
|
@@ -49,8 +49,18 @@ Rigor is a tool, not a library — install it independently, **not** in
|
|
|
49
49
|
your project's `Gemfile`. It runs on Ruby 4.0, regardless of which Ruby
|
|
50
50
|
version your project targets.
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
**Using an AI coding agent?** Hand it this prompt and it will detect
|
|
53
|
+
your environment, install Rigor, and kick off the project-init Skill
|
|
54
|
+
automatically:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
Install Rigor in this project by following the instructions at
|
|
58
|
+
https://raw.githubusercontent.com/rigortype/rigor/refs/heads/master/docs/install.md
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Manual install** — the recommended path uses
|
|
62
|
+
[`mise`](https://mise.jdx.dev/), which provisions both Ruby 4.0 and
|
|
63
|
+
Rigor pinned per project:
|
|
54
64
|
|
|
55
65
|
```sh
|
|
56
66
|
mise use ruby@4.0
|
|
@@ -34,6 +34,20 @@ module Rigor
|
|
|
34
34
|
# `sig/`), and `local` (a user-managed RBS dir) — all
|
|
35
35
|
# produce a directory under the collection root and are
|
|
36
36
|
# admitted.
|
|
37
|
+
#
|
|
38
|
+
# The `source.type` filter alone is NOT sufficient: gems
|
|
39
|
+
# that were extracted from Ruby's stdlib into standalone
|
|
40
|
+
# default gems (e.g. `cgi`, `logger`, `base64`, `csv`,
|
|
41
|
+
# `bigdecimal`) are published in `ruby/gem_rbs_collection`
|
|
42
|
+
# under a `git` source type, yet rigor ALSO loads them
|
|
43
|
+
# from its bundled stdlib via `DEFAULT_LIBRARIES`. Loading
|
|
44
|
+
# both copies triggers the very
|
|
45
|
+
# `RBS::DuplicatedDeclarationError` this module exists to
|
|
46
|
+
# avoid (observed on a Rails 8 app: `.gem_rbs_collection/
|
|
47
|
+
# cgi/0.5/` vs the bundled `stdlib/cgi`). The
|
|
48
|
+
# `skip_gem_names:` parameter lets the caller pass the set
|
|
49
|
+
# of library names rigor already loads so those gems are
|
|
50
|
+
# dropped regardless of `source.type`.
|
|
37
51
|
SKIPPED_SOURCE_TYPES = Set["stdlib"].freeze
|
|
38
52
|
|
|
39
53
|
DEFAULT_COLLECTION_PATH = ".gem_rbs_collection"
|
|
@@ -47,14 +61,21 @@ module Rigor
|
|
|
47
61
|
# @param auto_detect [Boolean] when true and
|
|
48
62
|
# `lockfile_path:` is nil, look for
|
|
49
63
|
# `<project_root>/rbs_collection.lock.yaml`.
|
|
64
|
+
# @param skip_gem_names [Array<String>, Set<String>] gem
|
|
65
|
+
# names rigor already loads from its bundled stdlib (the
|
|
66
|
+
# merged `DEFAULT_LIBRARIES + libraries:` set). Entries
|
|
67
|
+
# whose `name` is in this set are dropped regardless of
|
|
68
|
+
# `source.type` to avoid `RBS::DuplicatedDeclarationError`
|
|
69
|
+
# on stdlib-extracted default gems. Defaults to empty.
|
|
50
70
|
# @return [Array<Pathname>] every
|
|
51
71
|
# `<collection_path>/<gem-name>/<gem-version>/`
|
|
52
72
|
# directory listed in the lockfile whose entry has a
|
|
53
|
-
# non-skipped source type
|
|
54
|
-
#
|
|
55
|
-
# when
|
|
56
|
-
#
|
|
57
|
-
|
|
73
|
+
# non-skipped source type, whose `name` is not in
|
|
74
|
+
# `skip_gem_names:`, and whose directory exists on disk.
|
|
75
|
+
# Returns `[]` when no lockfile is resolvable, when the
|
|
76
|
+
# YAML is unreadable, or when the collection path
|
|
77
|
+
# doesn't exist.
|
|
78
|
+
def self.discover(lockfile_path:, project_root: Dir.pwd, auto_detect: true, skip_gem_names: [])
|
|
58
79
|
resolved = resolve_lockfile_path(
|
|
59
80
|
lockfile_path: lockfile_path,
|
|
60
81
|
project_root: project_root,
|
|
@@ -68,7 +89,7 @@ module Rigor
|
|
|
68
89
|
collection_root = resolve_collection_root(resolved, data)
|
|
69
90
|
return [] unless collection_root.directory?
|
|
70
91
|
|
|
71
|
-
gem_paths_from(collection_root, data)
|
|
92
|
+
gem_paths_from(collection_root, data, skip_gem_names.to_set)
|
|
72
93
|
end
|
|
73
94
|
|
|
74
95
|
# Returns the resolved lockfile path (`Pathname`) or `nil`
|
|
@@ -105,7 +126,7 @@ module Rigor
|
|
|
105
126
|
end
|
|
106
127
|
private_class_method :resolve_collection_root
|
|
107
128
|
|
|
108
|
-
def self.gem_paths_from(collection_root, data)
|
|
129
|
+
def self.gem_paths_from(collection_root, data, skip_gem_names)
|
|
109
130
|
Array(data["gems"]).filter_map do |entry|
|
|
110
131
|
next unless entry.is_a?(Hash)
|
|
111
132
|
|
|
@@ -115,6 +136,7 @@ module Rigor
|
|
|
115
136
|
name = entry["name"]
|
|
116
137
|
version = entry["version"]
|
|
117
138
|
next if name.nil? || version.nil?
|
|
139
|
+
next if skip_gem_names.include?(name.to_s)
|
|
118
140
|
|
|
119
141
|
gem_root = collection_root + name.to_s + version.to_s
|
|
120
142
|
gem_root if gem_root.directory?
|
|
@@ -122,6 +122,23 @@ module Rigor
|
|
|
122
122
|
Pathname(File.join(VENDORED_GEM_SIGS_ROOT, gem_dir))
|
|
123
123
|
end
|
|
124
124
|
end
|
|
125
|
+
|
|
126
|
+
# Gem names whose RBS ships under
|
|
127
|
+
# `data/vendored_gem_sigs/<gem>/`. The directory walk is
|
|
128
|
+
# the source of truth (the `README.md` sibling is not a
|
|
129
|
+
# gem and is excluded). Callers building the RBS env use
|
|
130
|
+
# this set to drop the matching `rbs collection install`
|
|
131
|
+
# directory before it double-declares against the
|
|
132
|
+
# vendored copy — the same hazard `DEFAULT_LIBRARIES`
|
|
133
|
+
# creates for stdlib-extracted gems. See
|
|
134
|
+
# `RbsCollectionDiscovery`'s `skip_gem_names:`.
|
|
135
|
+
def vendored_gem_names
|
|
136
|
+
return [] unless File.directory?(VENDORED_GEM_SIGS_ROOT)
|
|
137
|
+
|
|
138
|
+
Dir.children(VENDORED_GEM_SIGS_ROOT).reject do |child|
|
|
139
|
+
File.file?(File.join(VENDORED_GEM_SIGS_ROOT, child))
|
|
140
|
+
end
|
|
141
|
+
end
|
|
125
142
|
end
|
|
126
143
|
|
|
127
144
|
attr_reader :libraries, :signature_paths, :cache_store, :virtual_rbs
|
data/lib/rigor/environment.rb
CHANGED
|
@@ -263,11 +263,22 @@ module Rigor
|
|
|
263
263
|
# resulting `rbs_collection.lock.yaml` and feed each
|
|
264
264
|
# gem's `<collection_path>/<name>/<version>/` directory
|
|
265
265
|
# into `signature_paths:`. Stdlib-typed entries are
|
|
266
|
-
# skipped (already covered by `DEFAULT_LIBRARIES`)
|
|
266
|
+
# skipped (already covered by `DEFAULT_LIBRARIES`), as
|
|
267
|
+
# are gems whose RBS rigor already loads from another
|
|
268
|
+
# source — stdlib-extracted default gems (`cgi`,
|
|
269
|
+
# `logger`, …, shipped by the collection under a `git`
|
|
270
|
+
# source) and the `data/vendored_gem_sigs/` bundle
|
|
271
|
+
# (`redis`, `nokogiri`, `pg`, …). `skip_gem_names:`
|
|
272
|
+
# passes both sets so the collection copy doesn't
|
|
273
|
+
# double-declare against rigor's bundled RBS (the
|
|
274
|
+
# `RBS::DuplicatedDeclarationError` hazard).
|
|
275
|
+
merged_libraries = (DEFAULT_LIBRARIES + libraries.map(&:to_s)).uniq
|
|
276
|
+
skip_gem_names = merged_libraries + RbsLoader.vendored_gem_names
|
|
267
277
|
collection_paths = RbsCollectionDiscovery.discover(
|
|
268
278
|
lockfile_path: rbs_collection_lockfile,
|
|
269
279
|
project_root: root,
|
|
270
|
-
auto_detect: rbs_collection_auto_detect
|
|
280
|
+
auto_detect: rbs_collection_auto_detect,
|
|
281
|
+
skip_gem_names: skip_gem_names
|
|
271
282
|
).map(&:to_s)
|
|
272
283
|
# ADR-25 — RBS signature directories contributed by loaded
|
|
273
284
|
# plugins via their manifest `signature_paths:`. Resolved
|
|
@@ -278,7 +289,6 @@ module Rigor
|
|
|
278
289
|
# degrades through the same O7 failure-memo path.
|
|
279
290
|
plugin_sig_paths = plugin_registry ? plugin_registry.signature_paths.map(&:to_s) : []
|
|
280
291
|
loader_signature_paths = resolved_paths + plugin_sig_paths + gem_sig_paths + collection_paths
|
|
281
|
-
merged_libraries = (DEFAULT_LIBRARIES + libraries.map(&:to_s)).uniq
|
|
282
292
|
# ADR-32 WD4 + WD5 — invoke each loaded plugin's
|
|
283
293
|
# `source_rbs_synthesizer` once per project source file
|
|
284
294
|
# and collect non-nil `[filename, rbs_source]` pairs.
|
data/lib/rigor/version.rb
CHANGED
data/sig/rigor/environment.rbs
CHANGED
|
@@ -51,6 +51,7 @@ module Rigor
|
|
|
51
51
|
def self.reset_default!: () -> void
|
|
52
52
|
def self.build_env_for: (libraries: Array[String], signature_paths: Array[String | _ToPath]) -> untyped
|
|
53
53
|
def self.vendored_gem_sig_paths: () -> Array[Pathname]
|
|
54
|
+
def self.vendored_gem_names: () -> Array[String]
|
|
54
55
|
|
|
55
56
|
def initialize: (?libraries: Array[String], ?signature_paths: Array[String | _ToPath], ?cache_store: untyped?) -> void
|
|
56
57
|
def class_known?: (String | Symbol name) -> bool
|
|
@@ -35,13 +35,51 @@ Also note per-gem markers that have their own plugin: `devise`,
|
|
|
35
35
|
`rbs_collection.auto_detect: true` (the default).
|
|
36
36
|
- **Lockfile present, `.gem_rbs_collection/` absent** → the lockfile
|
|
37
37
|
was generated but the gems were never downloaded. Rigor loads the
|
|
38
|
-
lockfile but finds no RBS files.
|
|
39
|
-
reports `gem-without-rbs` hints for gems that appear in
|
|
40
|
-
`rbs_collection.yaml`, run `rbs collection install` first and
|
|
41
|
-
re-run triage.
|
|
38
|
+
lockfile but finds no RBS files. **Act now — see below.**
|
|
42
39
|
- **Both absent** → note it; Phase 6's triage may recommend
|
|
43
40
|
`rbs collection install` if `gem-without-rbs` hints appear.
|
|
44
41
|
|
|
42
|
+
### RBS collection — install if absent
|
|
43
|
+
|
|
44
|
+
When `rbs_collection.lock.yaml` is present **and** `.gem_rbs_collection/`
|
|
45
|
+
is absent, the collection was configured (e.g. by Steep or `rbs_rails`)
|
|
46
|
+
but never installed. Installing it now — before writing the config or
|
|
47
|
+
running triage — gives Rigor community RBS for dozens of gems and avoids
|
|
48
|
+
a triage → config-fix → re-triage cycle.
|
|
49
|
+
|
|
50
|
+
Offer to run:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
bundle exec rbs collection install
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Use `bundle exec` when `rbs` appears in `Gemfile` or `Gemfile.lock`
|
|
57
|
+
(the common case — Steep / rbs_rails workflows put it there). If `rbs`
|
|
58
|
+
is not in the Gemfile, use the bare `rbs collection install` instead.
|
|
59
|
+
|
|
60
|
+
Ask the user for permission before running, since this installs files
|
|
61
|
+
into `.gem_rbs_collection/` (a generated directory that belongs in
|
|
62
|
+
`.gitignore`). Only proceed with their confirmation.
|
|
63
|
+
|
|
64
|
+
After installation, verify with:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
ls .gem_rbs_collection/
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The directory should contain RBS gem subdirectories. Continue to
|
|
71
|
+
Phase 2 — the collection will be auto-detected by Rigor at analysis
|
|
72
|
+
time.
|
|
73
|
+
|
|
74
|
+
**Note: RBS collision after install.** Some gems (e.g. `cgi`, `logger`,
|
|
75
|
+
`base64`) were extracted from Ruby's stdlib into standalone gems from
|
|
76
|
+
Ruby 3.3 onwards. When these appear in both `.gem_rbs_collection/` and
|
|
77
|
+
Rigor's bundled stdlib, `rigor triage` may print a
|
|
78
|
+
`RBS::DuplicatedDeclarationError`. If this happens, note the error and
|
|
79
|
+
continue — plugin-based diagnostics are unaffected. File a Rigor issue
|
|
80
|
+
at <https://github.com/rigortype/rigor/issues> so the engine can
|
|
81
|
+
deduplicate stdlib gems from the collection automatically.
|
|
82
|
+
|
|
45
83
|
### Path scope
|
|
46
84
|
|
|
47
85
|
Note the conventional source roots so Phase 4 can set `paths:`:
|
|
@@ -18,6 +18,23 @@ config as `.rigor.dist.yml` lets a contributor opt out locally (for
|
|
|
18
18
|
example, run without the baseline) without touching the committed
|
|
19
19
|
file.
|
|
20
20
|
|
|
21
|
+
## Selecting `target_ruby`
|
|
22
|
+
|
|
23
|
+
Read the project's declared Ruby version from `.ruby-version`, the
|
|
24
|
+
`Gemfile` `ruby "…"` line, or `.tool-versions`. Use the **minimum**
|
|
25
|
+
version of the declared range (e.g. `>= 3.2.0, < 3.5.0` → `3.2`).
|
|
26
|
+
|
|
27
|
+
Then apply this decision table — **do not ask the user**; act
|
|
28
|
+
automatically and emit the message described:
|
|
29
|
+
|
|
30
|
+
| Declared minimum | `target_ruby` to write | Action |
|
|
31
|
+
| --- | --- | --- |
|
|
32
|
+
| 4.x | `"4.0"` (or exact patch) | Write as-is. |
|
|
33
|
+
| 3.3 – 3.x | `"3.3"` (or exact minor) | Write as-is. |
|
|
34
|
+
| 3.0 – 3.2 | `"3.3"` | Write `"3.3"` automatically. Emit: *"Project targets Ruby X.Y, but Prism (the parser bundled with rigortype) supports Ruby 3.3 as its minimum. Setting `target_ruby: \"3.3\"` — syntax parsing is compatible across 3.0–3.3, but RBS type definitions in `.gem_rbs_collection/` or community libraries may be authored for 3.3+ and could produce type-level false positives on APIs that changed between 3.0 and 3.3."* |
|
|
35
|
+
| 2.x | — | **Stop.** Emit: *"Project targets Ruby 2.x. Rigor's bundled Prism parser does not support Ruby 2.x syntax (removed keyword argument syntax, pattern matching differences). Rigor cannot reliably analyse this project. Consider upgrading the project to Ruby 3.3+ before onboarding Rigor."* Do not write a config; do not continue the skill. |
|
|
36
|
+
| Unknown / not declared | `"4.0"` (Rigor's default) | Write as-is; note that the default was used. |
|
|
37
|
+
|
|
21
38
|
## Severity profile — follows the mode
|
|
22
39
|
|
|
23
40
|
The `severity_profile:` key re-stamps every rule's severity. Set it
|
|
@@ -49,7 +49,7 @@ Use the three sections like this:
|
|
|
49
49
|
| Hint `id` | Cause | Where this skill handles it |
|
|
50
50
|
| --- | --- | --- |
|
|
51
51
|
| `activesupport-core-ext` | ActiveSupport core-class monkey-patches not loaded. | Go back to Phase 3/4: add `rigor-activesupport-core-ext` to `plugins:` (it is an RBS-bundle plugin), re-run triage. This is a config gap, not a bug. |
|
|
52
|
-
| `gem-without-rbs` | A dependency ships no RBS. | Phase
|
|
52
|
+
| `gem-without-rbs` | A dependency ships no RBS. | If `rbs_collection.lock.yaml` was present and Phase 1 installed the collection, re-run `rigor triage` — the hint may shrink or disappear. Otherwise: Phase 8 escalation — `bundle exec rbs collection install`, or `dependencies.source_inference:`, or open a Rigor issue. |
|
|
53
53
|
| `project-monkey-patch` | An in-project monkey-patch / refinement Rigor did not see. | Phase 7 escalation — register the defining file via `pre_eval:`, or (if it is a DSL) write a project plugin. |
|
|
54
54
|
| `activerecord-relation-misinference` | An ActiveRecord relation inferred as `Array`. | Ensure `rigor-activerecord` is enabled (Phase 3). If it persists, it is an engine gap — open a Rigor issue. |
|
|
55
55
|
| `systemic-file-cluster` | One file × one rule, large count. | Acknowledge mode: a clean baseline bucket. Strict mode: a single fix may clear many — review that file first. |
|
|
@@ -142,8 +142,9 @@ cheapest first:
|
|
|
142
142
|
If triage reports `gem-without-rbs`, a dependency ships no type
|
|
143
143
|
information and Rigor has no built-in coverage. In order:
|
|
144
144
|
|
|
145
|
-
1. `rbs collection install`
|
|
146
|
-
|
|
145
|
+
1. `bundle exec rbs collection install` (bare `rbs collection install`
|
|
146
|
+
if `rbs` is not in the project's Gemfile) — pulls community RBS for
|
|
147
|
+
the gem if it exists. Re-run triage afterwards.
|
|
147
148
|
2. `dependencies.source_inference:` in `.rigor.dist.yml` — opt the
|
|
148
149
|
gem into Rigor inferring `Dynamic`-typed returns from its source.
|
|
149
150
|
3. If the gem is widely used and genuinely warrants first-class
|