query_packwerk 0.1.24 → 0.1.26
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 +9 -1
- data/lib/query_packwerk/cli.rb +14 -2
- data/lib/query_packwerk/file_cache.rb +35 -5
- data/lib/query_packwerk/version.rb +1 -1
- data/lib/query_packwerk/violation.rb +5 -5
- data/lib/query_packwerk/violations.rb +3 -1
- data/lib/query_packwerk.rb +1 -0
- metadata +15 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 98b9a4d914a8be019fa0b1795cd527ac2bfd4ce7931b1e5a204704b5bb503847
|
|
4
|
+
data.tar.gz: 209fa3baaa03c0687daca480890ff32ec6c2111c1c902c3088771614d4127c53
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 406b5e9039a31dda4264391d1287fa89684dc21a982521c212734c05055e727ecaea62b9ed234e296a151587fd9cbaee5dd99915573f5ccb90429509f12de046
|
|
7
|
+
data.tar.gz: e11dfd82ec7324d499e37c0214d7b12ad735d03732f996bcd8e8397f36f684fd99ff6e3b58fa73a1adbc63f7e87d4be455e62814bb690e51cca0d27d7d8d22dc
|
data/README.md
CHANGED
|
@@ -27,6 +27,14 @@ The easiest way to use QueryPackwerk is through its interactive console. This wi
|
|
|
27
27
|
query_packwerk console
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
To use the Packwerk context of another directory, pass it as an argument:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
query_packwerk console path/to/app
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Run `query_packwerk console --help` for usage details.
|
|
37
|
+
|
|
30
38
|
The CLI includes a welcome message with a list of commands. You can access it any time with `welcome`:
|
|
31
39
|
|
|
32
40
|
```
|
|
@@ -116,7 +124,7 @@ QueryPackwerk.anonymous_violation_counts_for("my_pack", threshold: 3)
|
|
|
116
124
|
|
|
117
125
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
118
126
|
|
|
119
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version,
|
|
127
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, bump `VERSION` in `lib/query_packwerk/version.rb` in your PR and run `bundle install` so `Gemfile.lock` matches. When the PR is merged and CI passes on `main`, the gem is tagged and published to RubyGems automatically. Merges that don't change the version don't publish anything.
|
|
120
128
|
|
|
121
129
|
## Contributing
|
|
122
130
|
|
data/lib/query_packwerk/cli.rb
CHANGED
|
@@ -8,12 +8,24 @@ module QueryPackwerk
|
|
|
8
8
|
class CLI < Thor
|
|
9
9
|
extend T::Sig
|
|
10
10
|
|
|
11
|
+
# Thor maps --help to its help command only as the first argument, so commands check options[:help].
|
|
12
|
+
class_option :help, type: :boolean, aliases: '-h', desc: 'Show this help message'
|
|
13
|
+
check_unknown_options!
|
|
14
|
+
|
|
11
15
|
default_command :console
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
sig { returns(T::Boolean) }
|
|
18
|
+
def self.exit_on_failure?
|
|
19
|
+
true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
desc 'console [DIRECTORY]', 'Query packwerk in DIRECTORY via console'
|
|
23
|
+
long_desc 'Query packwerk in DIRECTORY via console. DIRECTORY defaults to the current directory.'
|
|
15
24
|
sig { params(directory: String).void }
|
|
16
25
|
def console(directory = Dir.pwd)
|
|
26
|
+
return help('console') if options[:help]
|
|
27
|
+
raise Thor::Error, "Could not find directory #{directory.inspect}." unless File.directory?(directory)
|
|
28
|
+
|
|
17
29
|
QueryPackwerk::Console.start(directory)
|
|
18
30
|
end
|
|
19
31
|
end
|
|
@@ -9,7 +9,9 @@ module QueryPackwerk
|
|
|
9
9
|
class FileCache
|
|
10
10
|
extend T::Sig
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
# The files packwerk parses as Ruby, from Packwerk::Parsers::Factory
|
|
13
|
+
RUBY_FILE = T.let(/(\.(rb|rake|builder|gemspec|ru)|Gemfile|Rakefile)\z/, Regexp)
|
|
14
|
+
ERB_FILE = T.let(/\.erb\z/, Regexp)
|
|
13
15
|
|
|
14
16
|
sig { void }
|
|
15
17
|
def initialize
|
|
@@ -62,12 +64,18 @@ module QueryPackwerk
|
|
|
62
64
|
|
|
63
65
|
sig { params(file_name: String).returns(RuboCop::AST::Node) }
|
|
64
66
|
def get_file_ast(file_name)
|
|
65
|
-
@file_ast_cache[file_name] ||=
|
|
67
|
+
@file_ast_cache[file_name] ||= begin
|
|
68
|
+
source = processed_source(ruby_source(file_name))
|
|
69
|
+
warn_unparseable(file_name, source) unless source.valid_syntax?
|
|
70
|
+
|
|
71
|
+
# The AST is also nil for source with no code, like a template that's only HTML
|
|
72
|
+
source.ast || ast_from('x')
|
|
73
|
+
end
|
|
66
74
|
end
|
|
67
75
|
|
|
68
76
|
sig { params(file_name: String).returns(String) }
|
|
69
77
|
def get_file(file_name)
|
|
70
|
-
@file_cache[file_name] ||= if RUBY_FILE.match?(file_name) && File.exist?(file_name)
|
|
78
|
+
@file_cache[file_name] ||= if (RUBY_FILE.match?(file_name) || ERB_FILE.match?(file_name)) && File.exist?(file_name)
|
|
71
79
|
File.read(file_name)
|
|
72
80
|
else
|
|
73
81
|
'x'
|
|
@@ -84,7 +92,7 @@ module QueryPackwerk
|
|
|
84
92
|
absolute_const_node = ast_from(class_name)
|
|
85
93
|
relative_const_node = ast_from(class_name.delete_prefix('::'))
|
|
86
94
|
|
|
87
|
-
@file_const_cache[const_key] = get_file_ast(file_name).
|
|
95
|
+
@file_const_cache[const_key] = get_file_ast(file_name).each_node.select do |node|
|
|
88
96
|
next false unless node.const_type?
|
|
89
97
|
|
|
90
98
|
node == absolute_const_node || node == relative_const_node
|
|
@@ -133,10 +141,32 @@ module QueryPackwerk
|
|
|
133
141
|
|
|
134
142
|
private
|
|
135
143
|
|
|
144
|
+
# Blanks out everything in an ERB template except its Ruby code, so line numbers still match the template
|
|
145
|
+
sig { params(file_name: String).returns(String) }
|
|
146
|
+
def ruby_source(file_name)
|
|
147
|
+
source = get_file(file_name)
|
|
148
|
+
return source unless ERB_FILE.match?(file_name)
|
|
149
|
+
|
|
150
|
+
Herb.extract_ruby(source, semicolons: true, comments: false, preserve_positions: true)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
sig { params(string: String).returns(RuboCop::ProcessedSource) }
|
|
154
|
+
def processed_source(string)
|
|
155
|
+
RuboCop::ProcessedSource.new(string, RUBY_VERSION.to_f)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
sig { params(file_name: String, source: RuboCop::ProcessedSource).void }
|
|
159
|
+
def warn_unparseable(file_name, source)
|
|
160
|
+
error = source.diagnostics.find { |diagnostic| diagnostic.level == :error }
|
|
161
|
+
location = error ? "#{file_name}:#{error.location.line} (#{error.message})" : file_name
|
|
162
|
+
|
|
163
|
+
warn "Could not parse #{location}, so its violations are not included"
|
|
164
|
+
end
|
|
165
|
+
|
|
136
166
|
# Turns a string into a Ruby AST
|
|
137
167
|
sig { params(string: String).returns(RuboCop::AST::Node) }
|
|
138
168
|
def ast_from(string)
|
|
139
|
-
|
|
169
|
+
processed_source(string).ast
|
|
140
170
|
end
|
|
141
171
|
|
|
142
172
|
# We can find a constant, but by going up its parents we can find the full call chain.
|
|
@@ -9,8 +9,6 @@ module QueryPackwerk
|
|
|
9
9
|
class Violation
|
|
10
10
|
extend T::Sig
|
|
11
11
|
|
|
12
|
-
# This does not play nicely with ERB files which may have violations
|
|
13
|
-
RUBY_FILE = T.let(/\.(rb|rake)\z/, Regexp)
|
|
14
12
|
ALL_CAPS = T.let(/\A[A-Z_]+\z/, Regexp)
|
|
15
13
|
|
|
16
14
|
sig { returns(QueryPackwerk::Package) }
|
|
@@ -168,13 +166,15 @@ module QueryPackwerk
|
|
|
168
166
|
load_cache!
|
|
169
167
|
|
|
170
168
|
file_sources = files.flat_map do |file_name|
|
|
169
|
+
lines = @file_cache.get_file(file_name).lines
|
|
170
|
+
|
|
171
171
|
@file_cache.get_full_sources(file_name: file_name, class_name: class_name).map do |s|
|
|
172
172
|
line_number = s.loc.line
|
|
173
|
-
start_pos = line_number - start_offset
|
|
174
|
-
end_pos = line_number + end_offset
|
|
173
|
+
start_pos = [line_number - start_offset, 1].max
|
|
174
|
+
end_pos = [line_number + end_offset, lines.size].min
|
|
175
175
|
|
|
176
176
|
location = "#{file_name}:#{s.loc.line} (L#{start_pos}..#{end_pos})"
|
|
177
|
-
context =
|
|
177
|
+
context = lines[(start_pos - 1)...end_pos]
|
|
178
178
|
full_context = unindent((context || ['']).join)
|
|
179
179
|
|
|
180
180
|
[@file_cache.anonymize_arguments(s.source), "> #{location}\n\n#{full_context}"]
|
|
@@ -192,7 +192,9 @@ module QueryPackwerk
|
|
|
192
192
|
def sources_with_contexts(start_offset: 3, end_offset: 3)
|
|
193
193
|
load_sources!
|
|
194
194
|
|
|
195
|
-
deep_merge_hash_groups(@original_collection)
|
|
195
|
+
deep_merge_hash_groups(@original_collection) do |v|
|
|
196
|
+
[v.class_name, v.sources_with_contexts(start_offset:, end_offset:)]
|
|
197
|
+
end
|
|
196
198
|
end
|
|
197
199
|
|
|
198
200
|
sig { params(start_offset: Integer, end_offset: Integer).returns(String) }
|
data/lib/query_packwerk.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: query_packwerk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.26
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gusto Engineering
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: herb
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0.9'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0.9'
|
|
26
40
|
- !ruby/object:Gem::Dependency
|
|
27
41
|
name: packwerk
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|