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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f7d66cf2c7a6c883fbbab59a805b1d1bca62867022e42d9b68d0b788525e831
4
- data.tar.gz: c327399cf8c239da46f383de404cd15843d8ce220b5538f1905d72f4e082a59f
3
+ metadata.gz: b1dbcd9b168a06cc8d5f26e0a096f19496c3cebdc8864fb56d966741b8a42577
4
+ data.tar.gz: 39e428c763e8d8cac6d9743d40d5d654bfaec56362d41e338a6dac974dff27cf
5
5
  SHA512:
6
- metadata.gz: a69935a6c878eba22f3050041d044855b9f98eefa8dc3ab55bd33ca2ecce12efc5abffb2dd0fbc57edfd7736fe5ee4eb985694e4fbcddbc63f2e231bf48483a4
7
- data.tar.gz: 58839858c8a5adcf8db74ef9d8c5ed2999c4459854a85c330b107ce3a933d08cb024cacc6c1303c638ab12ae1f5bc6559c14922ac13d17f41a5609631220fc28
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
- The recommended setup uses [`mise`](https://mise.jdx.dev/), which
53
- provisions both Ruby 4.0 and Rigor pinned per project:
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 and whose directory exists on
54
- # disk. Returns `[]` when no lockfile is resolvable,
55
- # when the YAML is unreadable, or when the collection
56
- # path doesn't exist.
57
- def self.discover(lockfile_path:, project_root: Dir.pwd, auto_detect: true)
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
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rigor
4
- VERSION = "0.1.13"
4
+ VERSION = "0.1.14"
5
5
  end
@@ -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. Note this for Phase 6: if triage
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 7 escalation — `rbs collection install`, or `dependencies.source_inference:`, or open a Rigor issue. |
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` pulls community RBS for the gem if it
146
- exists. Re-run triage afterwards.
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rigortype
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.13
4
+ version: 0.1.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rigor contributors