packwerk 2.3.0 → 3.0.1
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/.github/workflows/ci.yml +2 -5
- data/.ruby-version +1 -1
- data/Gemfile +0 -1
- data/Gemfile.lock +5 -95
- data/README.md +2 -7
- data/RESOLVING_VIOLATIONS.md +3 -8
- data/TROUBLESHOOT.md +2 -25
- data/UPGRADING.md +12 -0
- data/USAGE.md +136 -54
- data/dev.yml +1 -1
- data/exe/packwerk +4 -0
- data/gemfiles/Gemfile-rails-6-1 +1 -1
- data/lib/packwerk/application_validator.rb +54 -285
- data/lib/packwerk/association_inspector.rb +2 -0
- data/lib/packwerk/cache.rb +6 -5
- data/lib/packwerk/checker.rb +54 -0
- data/lib/packwerk/cli/result.rb +11 -0
- data/lib/packwerk/cli.rb +55 -40
- data/lib/packwerk/configuration.rb +61 -40
- data/lib/packwerk/const_node_inspector.rb +2 -0
- data/lib/packwerk/constant_context.rb +8 -0
- data/lib/packwerk/constant_discovery.rb +5 -6
- data/lib/packwerk/constant_name_inspector.rb +2 -0
- data/lib/packwerk/disable_sorbet.rb +41 -0
- data/lib/packwerk/extension_loader.rb +24 -0
- data/lib/packwerk/file_processor.rb +3 -1
- data/lib/packwerk/files_for_processing.rb +25 -12
- data/lib/packwerk/formatters/default_offenses_formatter.rb +77 -0
- data/lib/packwerk/formatters/progress_formatter.rb +31 -12
- data/lib/packwerk/generators/configuration_file.rb +7 -2
- data/lib/packwerk/generators/root_package.rb +5 -1
- data/lib/packwerk/generators/templates/package.yml +0 -10
- data/lib/packwerk/graph.rb +10 -2
- data/lib/packwerk/node.rb +1 -1
- data/lib/packwerk/node_helpers.rb +14 -7
- data/lib/packwerk/node_processor.rb +2 -0
- data/lib/packwerk/node_processor_factory.rb +6 -4
- data/lib/packwerk/node_visitor.rb +10 -1
- data/lib/packwerk/offense_collection.rb +26 -18
- data/lib/packwerk/offenses_formatter.rb +59 -2
- data/lib/packwerk/package.rb +7 -35
- data/lib/packwerk/package_set.rb +1 -1
- data/lib/packwerk/package_todo.rb +19 -20
- data/lib/packwerk/parse_run.rb +27 -34
- data/lib/packwerk/parsed_constant_definitions.rb +28 -5
- data/lib/packwerk/parsers/erb.rb +23 -4
- data/lib/packwerk/parsers/factory.rb +11 -2
- data/lib/packwerk/parsers/parser_interface.rb +1 -1
- data/lib/packwerk/parsers/ruby.rb +13 -3
- data/lib/packwerk/parsers.rb +6 -2
- data/lib/packwerk/{application_load_paths.rb → rails_load_paths.rb} +6 -4
- data/lib/packwerk/reference.rb +7 -1
- data/lib/packwerk/reference_checking/checkers/dependency_checker.rb +29 -6
- data/lib/packwerk/reference_checking/reference_checker.rb +1 -1
- data/lib/packwerk/reference_extractor.rb +24 -12
- data/lib/packwerk/reference_offense.rb +2 -2
- data/lib/packwerk/run_context.rb +7 -10
- data/lib/packwerk/spring_command.rb +9 -2
- data/lib/packwerk/unresolved_reference.rb +9 -1
- data/lib/packwerk/validator/result.rb +18 -0
- data/lib/packwerk/validator.rb +90 -0
- data/lib/packwerk/validators/dependency_validator.rb +154 -0
- data/lib/packwerk/version.rb +1 -1
- data/lib/packwerk.rb +64 -26
- data/packwerk.gemspec +4 -2
- data/sorbet/rbi/gems/{zeitwerk@2.6.0.rbi → zeitwerk@2.6.4.rbi} +291 -228
- data/sorbet/rbi/shims/minitest/test.rb +8 -0
- data/sorbet/rbi/shims/packwerk/reference.rbi +33 -0
- data/sorbet/rbi/shims/packwerk/unresolved_reference.rbi +33 -0
- data/sorbet/rbi/shims/parser.rbi +13 -0
- metadata +34 -15
- data/lib/packwerk/formatters/offenses_formatter.rb +0 -52
- data/lib/packwerk/reference_checking/checkers/checker.rb +0 -34
- data/lib/packwerk/reference_checking/checkers/privacy_checker.rb +0 -76
- data/lib/packwerk/result.rb +0 -9
- data/lib/packwerk/sanity_checker.rb +0 -8
- data/lib/packwerk/violation_type.rb +0 -11
- data/sorbet/rbi/gems/html_tokenizer@0.0.7.rbi +0 -46
- data/sorbet/rbi/gems/mini_portile2@2.8.0.rbi +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9af43a5e2d624ca5d6b38bfa3c9e65d1d12b1b9cc148a153563ebd55d3b6c82a
|
4
|
+
data.tar.gz: 6dee0dac16640632fb272c72ebe024a36c6a85ba23748b2ab5886279cccd863c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 197f933a60a46aba470ee66a2c04bbabcf1ff3c3386665551dbb7b8241b9357d73e7576de7edd7c322249b8d45cfc0b282616e0f9a5a665d4560964632a66806
|
7
|
+
data.tar.gz: ec903a206f47df277d6187afc5c17a9cfa1591446afb249f2ba62171f7c56ba8687ce403041db451eb8ea0dba842feb26cc7c32e9ff600b93e60e49eafb4584f
|
data/.github/workflows/ci.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
name: CI
|
2
2
|
|
3
|
-
on: [push]
|
3
|
+
on: [push, pull_request]
|
4
4
|
|
5
5
|
jobs:
|
6
6
|
tests:
|
@@ -12,13 +12,10 @@ jobs:
|
|
12
12
|
- gemfiles/Gemfile-rails-6-0
|
13
13
|
- gemfiles/Gemfile-rails-6-1
|
14
14
|
ruby:
|
15
|
-
- 2.6
|
16
15
|
- 2.7
|
17
16
|
- 3.0
|
18
17
|
- 3.1
|
19
|
-
|
20
|
-
- ruby: 2.6
|
21
|
-
gemfile: Gemfile
|
18
|
+
- 3.2
|
22
19
|
env:
|
23
20
|
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
24
21
|
name: "Tests: Ruby ${{ matrix.ruby }} ${{ matrix.gemfile }}"
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.
|
1
|
+
3.2.1
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
packwerk (
|
5
|
-
activesupport (>=
|
4
|
+
packwerk (3.0.1)
|
5
|
+
activesupport (>= 6.0)
|
6
6
|
ast
|
7
7
|
better_html
|
8
8
|
bundler
|
@@ -15,31 +15,6 @@ PATH
|
|
15
15
|
GEM
|
16
16
|
remote: https://rubygems.org/
|
17
17
|
specs:
|
18
|
-
actioncable (7.0.3.1)
|
19
|
-
actionpack (= 7.0.3.1)
|
20
|
-
activesupport (= 7.0.3.1)
|
21
|
-
nio4r (~> 2.0)
|
22
|
-
websocket-driver (>= 0.6.1)
|
23
|
-
actionmailbox (7.0.3.1)
|
24
|
-
actionpack (= 7.0.3.1)
|
25
|
-
activejob (= 7.0.3.1)
|
26
|
-
activerecord (= 7.0.3.1)
|
27
|
-
activestorage (= 7.0.3.1)
|
28
|
-
activesupport (= 7.0.3.1)
|
29
|
-
mail (>= 2.7.1)
|
30
|
-
net-imap
|
31
|
-
net-pop
|
32
|
-
net-smtp
|
33
|
-
actionmailer (7.0.3.1)
|
34
|
-
actionpack (= 7.0.3.1)
|
35
|
-
actionview (= 7.0.3.1)
|
36
|
-
activejob (= 7.0.3.1)
|
37
|
-
activesupport (= 7.0.3.1)
|
38
|
-
mail (~> 2.5, >= 2.5.4)
|
39
|
-
net-imap
|
40
|
-
net-pop
|
41
|
-
net-smtp
|
42
|
-
rails-dom-testing (~> 2.0)
|
43
18
|
actionpack (7.0.3.1)
|
44
19
|
actionview (= 7.0.3.1)
|
45
20
|
activesupport (= 7.0.3.1)
|
@@ -47,34 +22,12 @@ GEM
|
|
47
22
|
rack-test (>= 0.6.3)
|
48
23
|
rails-dom-testing (~> 2.0)
|
49
24
|
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
50
|
-
actiontext (7.0.3.1)
|
51
|
-
actionpack (= 7.0.3.1)
|
52
|
-
activerecord (= 7.0.3.1)
|
53
|
-
activestorage (= 7.0.3.1)
|
54
|
-
activesupport (= 7.0.3.1)
|
55
|
-
globalid (>= 0.6.0)
|
56
|
-
nokogiri (>= 1.8.5)
|
57
25
|
actionview (7.0.3.1)
|
58
26
|
activesupport (= 7.0.3.1)
|
59
27
|
builder (~> 3.1)
|
60
28
|
erubi (~> 1.4)
|
61
29
|
rails-dom-testing (~> 2.0)
|
62
30
|
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
63
|
-
activejob (7.0.3.1)
|
64
|
-
activesupport (= 7.0.3.1)
|
65
|
-
globalid (>= 0.3.6)
|
66
|
-
activemodel (7.0.3.1)
|
67
|
-
activesupport (= 7.0.3.1)
|
68
|
-
activerecord (7.0.3.1)
|
69
|
-
activemodel (= 7.0.3.1)
|
70
|
-
activesupport (= 7.0.3.1)
|
71
|
-
activestorage (7.0.3.1)
|
72
|
-
actionpack (= 7.0.3.1)
|
73
|
-
activejob (= 7.0.3.1)
|
74
|
-
activerecord (= 7.0.3.1)
|
75
|
-
activesupport (= 7.0.3.1)
|
76
|
-
marcel (~> 1.0)
|
77
|
-
mini_mime (>= 1.1.0)
|
78
31
|
activesupport (7.0.3.1)
|
79
32
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
80
33
|
i18n (>= 1.6, < 2)
|
@@ -95,10 +48,7 @@ GEM
|
|
95
48
|
constant_resolver (0.2.0)
|
96
49
|
crass (1.0.6)
|
97
50
|
diff-lcs (1.5.0)
|
98
|
-
digest (3.1.0)
|
99
51
|
erubi (1.11.0)
|
100
|
-
globalid (1.0.0)
|
101
|
-
activesupport (>= 5.0)
|
102
52
|
i18n (1.12.0)
|
103
53
|
concurrent-ruby (~> 1.0)
|
104
54
|
json (2.6.2)
|
@@ -109,37 +59,16 @@ GEM
|
|
109
59
|
m (1.6.0)
|
110
60
|
method_source (>= 0.6.7)
|
111
61
|
rake (>= 0.9.2.2)
|
112
|
-
mail (2.7.1)
|
113
|
-
mini_mime (>= 0.1.1)
|
114
|
-
marcel (1.0.2)
|
115
62
|
method_source (1.0.0)
|
116
|
-
mini_mime (1.1.2)
|
117
63
|
mini_portile2 (2.8.0)
|
118
64
|
minitest (5.16.2)
|
119
65
|
minitest-focus (1.3.1)
|
120
66
|
minitest (>= 4, < 6)
|
121
67
|
mocha (1.14.0)
|
122
|
-
net-imap (0.2.3)
|
123
|
-
digest
|
124
|
-
net-protocol
|
125
|
-
strscan
|
126
|
-
net-pop (0.1.1)
|
127
|
-
digest
|
128
|
-
net-protocol
|
129
|
-
timeout
|
130
|
-
net-protocol (0.1.3)
|
131
|
-
timeout
|
132
|
-
net-smtp (0.3.1)
|
133
|
-
digest
|
134
|
-
net-protocol
|
135
|
-
timeout
|
136
68
|
netrc (0.11.0)
|
137
|
-
nio4r (2.5.8)
|
138
69
|
nokogiri (1.13.8)
|
139
70
|
mini_portile2 (~> 2.8.0)
|
140
71
|
racc (~> 1.4)
|
141
|
-
nokogiri (1.13.8-x86_64-darwin)
|
142
|
-
racc (~> 1.4)
|
143
72
|
parallel (1.22.1)
|
144
73
|
parser (3.1.2.1)
|
145
74
|
ast (~> 2.4.1)
|
@@ -151,20 +80,6 @@ GEM
|
|
151
80
|
rack (2.2.4)
|
152
81
|
rack-test (2.0.2)
|
153
82
|
rack (>= 1.3)
|
154
|
-
rails (7.0.3.1)
|
155
|
-
actioncable (= 7.0.3.1)
|
156
|
-
actionmailbox (= 7.0.3.1)
|
157
|
-
actionmailer (= 7.0.3.1)
|
158
|
-
actionpack (= 7.0.3.1)
|
159
|
-
actiontext (= 7.0.3.1)
|
160
|
-
actionview (= 7.0.3.1)
|
161
|
-
activejob (= 7.0.3.1)
|
162
|
-
activemodel (= 7.0.3.1)
|
163
|
-
activerecord (= 7.0.3.1)
|
164
|
-
activestorage (= 7.0.3.1)
|
165
|
-
activesupport (= 7.0.3.1)
|
166
|
-
bundler (>= 1.15.0)
|
167
|
-
railties (= 7.0.3.1)
|
168
83
|
rails-dom-testing (2.0.3)
|
169
84
|
activesupport (>= 4.2.0)
|
170
85
|
nokogiri (>= 1.6)
|
@@ -232,7 +147,6 @@ GEM
|
|
232
147
|
sorbet-runtime (>= 0.5.9204)
|
233
148
|
thor (>= 0.19.2)
|
234
149
|
spring (4.0.0)
|
235
|
-
strscan (3.0.4)
|
236
150
|
syntax_tree (3.3.0)
|
237
151
|
prettier_print
|
238
152
|
tapioca (0.9.2)
|
@@ -246,7 +160,6 @@ GEM
|
|
246
160
|
thor (>= 1.2.0)
|
247
161
|
yard-sorbet
|
248
162
|
thor (1.2.1)
|
249
|
-
timeout (0.3.0)
|
250
163
|
tzinfo (2.0.5)
|
251
164
|
concurrent-ruby (~> 1.0)
|
252
165
|
unicode-display_width (2.2.0)
|
@@ -254,15 +167,12 @@ GEM
|
|
254
167
|
diff-lcs (~> 1.3)
|
255
168
|
parser (>= 3.1.0)
|
256
169
|
webrick (1.7.0)
|
257
|
-
websocket-driver (0.7.5)
|
258
|
-
websocket-extensions (>= 0.1.0)
|
259
|
-
websocket-extensions (0.1.5)
|
260
170
|
yard (0.9.28)
|
261
171
|
webrick (~> 1.7.0)
|
262
172
|
yard-sorbet (0.6.1)
|
263
173
|
sorbet-runtime (>= 0.5)
|
264
174
|
yard (>= 0.9)
|
265
|
-
zeitwerk (2.6.
|
175
|
+
zeitwerk (2.6.4)
|
266
176
|
|
267
177
|
PLATFORMS
|
268
178
|
ruby
|
@@ -276,7 +186,7 @@ DEPENDENCIES
|
|
276
186
|
minitest-focus
|
277
187
|
mocha
|
278
188
|
packwerk!
|
279
|
-
|
189
|
+
railties
|
280
190
|
rake
|
281
191
|
rubocop-performance
|
282
192
|
rubocop-shopify
|
@@ -288,4 +198,4 @@ DEPENDENCIES
|
|
288
198
|
zeitwerk
|
289
199
|
|
290
200
|
BUNDLED WITH
|
291
|
-
2.
|
201
|
+
2.4.8
|
data/README.md
CHANGED
@@ -1,10 +1,5 @@
|
|
1
1
|
# Packwerk [](https://github.com/Shopify/packwerk/actions?query=workflow%3ACI)
|
2
2
|
|
3
|
-
### ⚠️ While Shopify is actively using `packwerk`, we consider it feature complete.
|
4
|
-
We are keeping `packwerk` compatible with current versions of Ruby and Rails, but will accept feature requests only in rare cases. Please submit bug fixes though!
|
5
|
-
|
6
|
-
---
|
7
|
-
|
8
3
|
> "I know who you are and because of that I know what you do."
|
9
4
|
> This knowledge is a dependency that raises the cost of change.
|
10
5
|
|
@@ -15,14 +10,13 @@ Packwerk is a Ruby gem used to enforce boundaries and modularize Rails applicati
|
|
15
10
|
Packwerk can be used to:
|
16
11
|
* Combine groups of files into packages
|
17
12
|
* Define package-level constant visibility (i.e. have publicly accessible constants)
|
18
|
-
* Enforce privacy (inbound) and dependency (outbound) boundaries between packages
|
19
13
|
* Help existing codebases to become more modular without obstructing development
|
20
14
|
|
21
15
|
## Prerequisites
|
22
16
|
|
23
17
|
Packwerk needs [Zeitwerk](https://github.com/fxn/zeitwerk) enabled, which comes with Rails 6.
|
24
18
|
|
25
|
-
Packwerk supports MRI versions 2.
|
19
|
+
Packwerk supports MRI versions 2.7 and above.
|
26
20
|
|
27
21
|
## Demo
|
28
22
|
|
@@ -67,6 +61,7 @@ Various third parties have built tooling on top of packwerk. Here's a selection
|
|
67
61
|
- https://github.com/Gusto/packwerk-vscode integrates packwerk into Visual Studio Code so you can see violations right in your editor
|
68
62
|
- https://github.com/Gusto/stimpack sets up Rails autoloading, as well as `rspec` and `FactoryBot` integration, for packages arranged in a flat list. Stimpack is quite convenient, but for autoloading we recommend to use `Rails::Engine`s instead.
|
69
63
|
- https://github.com/rubyatscale/danger-packwerk integrates packwerk with [danger.systems](https://danger.systems) to provide packwerk feedback as Github inline PR comments
|
64
|
+
- https://github.com/rubyatscale/packwerk-extensions contains extensions for packwerk, including a checker for packwerk that allows you to enforce public API boundaries. This was originally extracted from `packwerk` itself.
|
70
65
|
|
71
66
|
## Development
|
72
67
|
|
data/RESOLVING_VIOLATIONS.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
Violations can be [recorded as a deprecation](#recording-violations) or (better!) [eliminated](#eliminating-violations).
|
4
4
|
|
5
5
|
## Recording Violations
|
6
|
-
💡 New
|
6
|
+
💡 New dependency violations are never hard-blocked. There are many very valid reasons to run `bin/packwerk update-todo`, adding new violations to `package_todo.yml` files. Even if you feel your reason might not be "valid," if your judgement says adding the violation and shipping your change will produce positive impact, trust your gut.
|
7
7
|
|
8
8
|
### Emergency Fixes
|
9
9
|
❔ Is it a revert or is there a lot of urgency because you are fixing a production bug impacting customers?
|
@@ -31,7 +31,7 @@ Violations can be [recorded as a deprecation](#recording-violations) or (better!
|
|
31
31
|
➡️ Stay strong. Eliminate the violation. It is important to build a sustainable business that optimizes for the long-term. Look for advocates such as from mentors or within your team who can help you justify improving system design.
|
32
32
|
|
33
33
|
## Eliminating Violations
|
34
|
-
💡 Dependency
|
34
|
+
💡 Dependency violations are Packwerk's signal that what we've stated about the desired system design (what
|
35
35
|
packages exist and what lives in them, the dependencies in a package.yml, and the public interface in the package's public folder) doesn't match the reality of our system.
|
36
36
|
If what we've stated about our system design doesn't feel right, then the violations won't make sense either! Make sure to think through system design before addressing a violation.
|
37
37
|
|
@@ -40,12 +40,7 @@ If what we've stated about our system design doesn't feel right, then the violat
|
|
40
40
|
|
41
41
|
If not, find a better place for it to live.
|
42
42
|
|
43
|
-
Otherwise, follow the guide for eliminating [
|
44
|
-
|
45
|
-
### Privacy Violations
|
46
|
-
💡 Packwerk thinks something is a privacy violation if you're referencing a constant, class, or module defined in the private implementation (i.e. not the public folder) of another package. We care about these because we want to make sure we only use parts of a package that have been exposed as public API.
|
47
|
-
|
48
|
-
An explicit and implementation-hiding public API is a cornerstone of well-modularized code.
|
43
|
+
Otherwise, follow the guide for eliminating [Dependency Violations](#dependency-violations).
|
49
44
|
|
50
45
|
#### Use Existing Public Interface
|
51
46
|
❔ Does the package you're using expose public API in its public folder that supports your use case?
|
data/TROUBLESHOOT.md
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
* [Troubleshooting violations](#Troubleshooting-violations)
|
4
4
|
* [Feedback loop](#Feedback-loop)
|
5
|
-
* [Package Privacy violation](#Package-Privacy-violation)
|
6
|
-
* [Interpreting Privacy violation](#Interpreting-Privacy-violation)
|
7
5
|
* [Package Dependency violation](#Package-Dependency-violation)
|
8
6
|
* [Interpreting Dependency violation](#Interpreting-Dependency-violation)
|
9
7
|
|
@@ -16,32 +14,11 @@ You can specify folders or packages in Packwerk commands for a shorter run time:
|
|
16
14
|
|
17
15
|
bin/packwerk check components/your_package
|
18
16
|
|
19
|
-
|
20
|
-
|
21
|
-
_Note: You cannot specify folders or packages for `bin/packwerk validate` because the command runs for the entire application._
|
17
|
+
_Note: You cannot specify folders or packages for `bin/packwerk validate` and `bin/packwerk update-todo` because the
|
18
|
+
command runs for the entire application._
|
22
19
|
|
23
20
|

|
24
21
|
|
25
|
-
### Package Privacy violation
|
26
|
-
A constant that is private to its package has been referenced from outside of the package. Constants are declared private in their package’s `package.yml`.
|
27
|
-
|
28
|
-
See: [USAGE.md - Enforcing privacy boundary](USAGE.md#Enforcing-privacy-boundary)
|
29
|
-
|
30
|
-
#### Interpreting Privacy violation
|
31
|
-
|
32
|
-
> /Users/JaneDoe/src/github.com/sample-project/user/app/controllers/labels_controller.rb:170:30
|
33
|
-
> Privacy violation: '::Billing::CarrierInvoiceTransaction' is private to 'billing' but referenced from 'user'.
|
34
|
-
> Is there a public entrypoint in 'billing/app/public/' that you can use instead?
|
35
|
-
>
|
36
|
-
> Inference details: 'Billing::CarrierInvoiceTransaction' refers to ::Billing::CarrierInvoiceTransaction which seems to be defined in billing/app/models/billing/carrier_invoice_transaction.rb.
|
37
|
-
|
38
|
-
There has been a privacy violation of the package `billing` in the package `user`, through the use of the constant `Billing::CarrierInvoiceTransaction` in the file `user/app/controllers/labels_controller.rb`.
|
39
|
-
|
40
|
-
##### Suggestions
|
41
|
-
You may be accessing the implementation of a piece of functionality that is supposed to be accessed through a public interface on the package. Try to use the public interface instead. A package’s public interface should be defined in its `app/public` folder and documented.
|
42
|
-
|
43
|
-
The functionality you’re looking for may not be intended to be reused across packages at all. If there is no public interface for it but you have a good reason to use it from outside of its package, find the people responsible for the package and discuss a solution with them.
|
44
|
-
|
45
22
|
### Package Dependency violation
|
46
23
|
A constant defined in a package A is referenced from a package B that doesn’t define a dependency on A. Packages define their dependencies in their `package.yml`.
|
47
24
|
|
data/UPGRADING.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
# Upgrading from 2.x to 3.0
|
2
|
+
|
3
|
+
In Packwerk 3.0, we've made two notable changes:
|
4
|
+
|
5
|
+
## Renaming deprecated_references to package_todo
|
6
|
+
|
7
|
+
The `update-deprecations` subcommand has been renamed to `update-todo`. Old `deprecated_references.yml` files will be automatically deleted and replaced with `package_todo.yml` files when you run `update-todo`. This behaviour has been in Packwerk [since 2.3.0](https://github.com/Shopify/packwerk/releases/tag/v2.3.0), and automatic deletion will be removed in the next release.
|
8
|
+
|
9
|
+
### Removal of privacy checking
|
10
|
+
|
11
|
+
Privacy checking via `enforce_privacy` has been removed. Developers are encouraged to focus on leveraging Packwerk for dependency checking. For those who still need privacy checks, please use [Gusto's extension gem](https://github.com/rubyatscale/packwerk-extensions).
|
12
|
+
|
1
13
|
# Upgrading from 1.x to 2.0
|
2
14
|
|
3
15
|
With Packwerk 2.0, we made a few changes to simplify the setup. Updating will require removing some previously necessary files and configuration.
|
data/USAGE.md
CHANGED
@@ -13,14 +13,15 @@
|
|
13
13
|
* [Defining packages](#defining-packages)
|
14
14
|
* [Package metadata](#package-metadata)
|
15
15
|
* [Types of boundary checks](#types-of-boundary-checks)
|
16
|
-
* [Enforcing privacy boundary](#enforcing-privacy-boundary)
|
17
|
-
* [Using public folders](#using-public-folders)
|
18
16
|
* [Enforcing dependency boundary](#enforcing-dependency-boundary)
|
17
|
+
* [Using strict mode](#using-strict-mode)
|
19
18
|
* [Checking for violations](#checking-for-violations)
|
20
19
|
* [Resolving new violations](#resolving-new-violations)
|
21
20
|
* [Understanding how to respond to new violations](#understanding-how-to-respond-to-new-violations)
|
22
21
|
* [Recording existing violations](#recording-existing-violations)
|
23
22
|
* [Understanding the package todo file](#understanding-the-package-todo-file)
|
23
|
+
* [Understanding the list of deprecated references](#understanding-the-list-of-deprecated-references)
|
24
|
+
* [Loading extensions](#loading-extensions)
|
24
25
|
|
25
26
|
## What problem does Packwerk solve?
|
26
27
|
|
@@ -143,50 +144,10 @@ Example:
|
|
143
144
|
|
144
145
|
## Types of boundary checks
|
145
146
|
|
146
|
-
Packwerk
|
147
|
-
|
148
|
-
#### Enforcing privacy boundary
|
149
|
-
|
150
|
-
A package's privacy boundary is violated when there is a reference to the package's private constants from a source outside the package.
|
151
|
-
|
152
|
-
There are two ways you can enforce privacy for your package:
|
153
|
-
|
154
|
-
1. Enforce privacy for all external sources
|
155
|
-
|
156
|
-
```yaml
|
157
|
-
# components/merchandising/package.yml
|
158
|
-
enforce_privacy: true # will make everything private that is not in
|
159
|
-
# the components/merchandising/app/public folder
|
160
|
-
```
|
161
|
-
|
162
|
-
Setting `enforce_privacy` to true will make all references to private constants in your package a violation.
|
163
|
-
|
164
|
-
2. Enforce privacy for specific constants
|
165
|
-
|
166
|
-
```yaml
|
167
|
-
# components/merchandising/package.yml
|
168
|
-
enforce_privacy:
|
169
|
-
- "::Merchandising::Product"
|
170
|
-
- "::SomeNamespace" # enforces privacy for the namespace and
|
171
|
-
# everything nested in it
|
172
|
-
```
|
173
|
-
|
174
|
-
It will be a privacy violation when a file outside of the `components/merchandising` package tries to reference `Merchandising::Product`.
|
175
|
-
|
176
|
-
##### Using public folders
|
177
|
-
You may enforce privacy either way mentioned above and still expose a public API for your package by placing constants in the public folder, which by default is `app/public`. The constants in the public folder will be made available for use by the rest of the application.
|
178
|
-
|
179
|
-
##### Defining your own public folder
|
180
|
-
|
181
|
-
You may prefer to override the default public folder, you can do so on a per-package basis by defining a `public_path`.
|
182
|
-
|
183
|
-
Example:
|
184
|
-
|
185
|
-
```yaml
|
186
|
-
public_path: my/custom/path/
|
187
|
-
```
|
147
|
+
Packwerk ships with dependency boundary checking only. Other checking support may be added by extension gems.
|
188
148
|
|
189
149
|
#### Enforcing dependency boundary
|
150
|
+
|
190
151
|
A package's dependency boundary is violated whenever it references a constant in some package that has not been declared as a dependency.
|
191
152
|
|
192
153
|
Specify `enforce_dependencies: true` to start enforcing the dependencies of a package. The intentional dependencies of the package are specified as a list under a `dependencies:` key.
|
@@ -202,6 +163,15 @@ dependencies:
|
|
202
163
|
|
203
164
|
It will be a dependency violation when `components/shop_identity` tries to reference a constant that is not within `components/platform` or itself.
|
204
165
|
|
166
|
+
#### Using strict mode
|
167
|
+
|
168
|
+
Once there are no more violations in a package, you can turn on `strict` mode, which will prevent new violations from being added to the package's `package_todo.yml`. To use this, simply change `enforce_dependencies: true` to `enforce_dependencies: strict` in your `package.yml`.
|
169
|
+
|
170
|
+
Then, when you run `bin/packwerk check`, new violations will cause the following error to be displayed:
|
171
|
+
```
|
172
|
+
packs/referencing_package cannot have dependency violations on packs/defining_package because strict mode is enabled for dependency violations in packs/referencing_package/package.yml
|
173
|
+
```
|
174
|
+
|
205
175
|
## Checking for violations
|
206
176
|
|
207
177
|
After enforcing the boundary checks for a package, you may execute:
|
@@ -225,9 +195,10 @@ In order to keep the package system valid at each version of the application, we
|
|
225
195
|
See: [TROUBLESHOOT.md - Sample violations](TROUBLESHOOT.md#Sample-violations)
|
226
196
|
|
227
197
|
## Resolving new violations
|
198
|
+
|
228
199
|
### Understanding how to respond to new violations
|
229
200
|
|
230
|
-
When you have a new dependency
|
201
|
+
When you have a new dependency violation, what do you do?
|
231
202
|
|
232
203
|
See: [RESOLVING_VIOLATIONS.md](RESOLVING_VIOLATIONS.md)
|
233
204
|
|
@@ -239,19 +210,12 @@ If so, you will want to stop the bleeding and prevent more violations from occur
|
|
239
210
|
|
240
211
|
bin/packwerk update-todo
|
241
212
|
|
242
|
-
Similar to `bin/packwerk check`, you may also run `bin/packwerk update-todo` on folders or packages:
|
243
|
-
|
244
|
-
bin/packwerk update-todo components/your_package
|
245
|
-
|
246
213
|

|
247
214
|
|
248
|
-
_Note: Changing dependencies or enabling dependencies will not require a full update of the codebase, only the package that changed. On the other hand, changing or enabling privacy will require a full update of the codebase._
|
249
|
-
|
250
215
|
`bin/packwerk update-todo` should only be run to record existing violations and to remove violations that have been worked off. Running `bin/packwerk update-todo` to resolve a violation should be the very last resort.
|
251
216
|
|
252
217
|
See: [TROUBLESHOOT.md - Troubleshooting violations](TROUBLESHOOT.md#Troubleshooting_violations)
|
253
218
|
|
254
|
-
|
255
219
|
### Understanding the package todo file
|
256
220
|
|
257
221
|
The package TODO list is called `package_todo.yml` and can be found in the package folder. The list outlines the constant violations of the package, where the violation is located, and the file defining the violation.
|
@@ -271,7 +235,125 @@ Above is an example of a constant violation entry in `package_todo.yml`.
|
|
271
235
|
|
272
236
|
* `components/merchant` - package where the constant violation is found
|
273
237
|
* `::Checkouts::Core::CheckoutId` - violated constant in question
|
274
|
-
* `dependency` - type of violation,
|
238
|
+
* `dependency` - type of violation, typically dependency
|
275
239
|
* `components/merchant/app/public/merchant/generate_order.rb` - path to the file containing the violated constant
|
276
240
|
|
277
|
-
Violations exist within the package that makes a violating reference.
|
241
|
+
Violations exist within the package that makes a violating reference.
|
242
|
+
|
243
|
+
# Loading Extensions
|
244
|
+
|
245
|
+
You can optionally specify ruby files that you'd like to be loaded with `packwerk` by specifying a `require` directive in `packwerk.yml`:
|
246
|
+
```yml
|
247
|
+
require:
|
248
|
+
- ./path/to/file.rb
|
249
|
+
- my_gem
|
250
|
+
```
|
251
|
+
|
252
|
+
`packwerk` will directly call `require` with these paths.
|
253
|
+
You can prefix local files with a dot to define them relative to `packwerk.yml`, or you can use absolute paths.
|
254
|
+
You can also reference the name of a gem.
|
255
|
+
|
256
|
+
## Examples
|
257
|
+
|
258
|
+
### Custom Offense Formatter
|
259
|
+
|
260
|
+
While `packwerk` ships with its own offense formatter, you may specify a custom one in your configuration file via the `offenses_formatter:` key. Your custom formatter will be used when `bin/packwerk check` is run.
|
261
|
+
|
262
|
+
Firstly, you'll need to create an `OffensesFormatter` class that includes `Packwerk::OffensesFormatter`. You can use [`Packwerk::Formatters::OffensesFormatter`](lib/packwerk/formatters/offenses_formatter.rb) as a point of reference for this. Then, in the `require` directive described above, you'll want to tell `packwerk` about it:
|
263
|
+
```ruby
|
264
|
+
# ./path/to/file.rb
|
265
|
+
class MyOffensesFormatter
|
266
|
+
include Packwerk::OffensesFormatter
|
267
|
+
# implement the `OffensesFormatter` interface
|
268
|
+
|
269
|
+
def identifier
|
270
|
+
'my_offenses_formatter'
|
271
|
+
end
|
272
|
+
end
|
273
|
+
```
|
274
|
+
|
275
|
+
Then in `packwerk.yml`, you can set the `formatter` to the identifier for your class:
|
276
|
+
```yml
|
277
|
+
offenses_formatter: my_offenses_formatter
|
278
|
+
```
|
279
|
+
|
280
|
+
You can also pass in a formatter on the command line:
|
281
|
+
```
|
282
|
+
bin/packwerk check --offenses-formatter=my_offenses_formatter
|
283
|
+
```
|
284
|
+
|
285
|
+
### Custom Checkers
|
286
|
+
|
287
|
+
Packwerk ships with a way to analyze dependencies and also supports custom checkers from extension gems.
|
288
|
+
|
289
|
+
Custom checkers will allow references to constants to be analyzed in new ways, and for those invalid references to show up as violations in `package_todo.yml`.
|
290
|
+
|
291
|
+
To create a custom checker, you'll first need to create a checker class that includes `Packwerk::Checker`. You can use [`Packwerk::ReferenceChecking::Checkers::DependencyChecker`](lib/packwerk/reference_checking/checkers/dependency_checker.rb) as a point of reference for this. Here is an example:
|
292
|
+
|
293
|
+
```ruby
|
294
|
+
# ./path/to/file.rb
|
295
|
+
class MyChecker
|
296
|
+
include Packwerk::Checker
|
297
|
+
# implement the `Checker` interface
|
298
|
+
|
299
|
+
sig { override.returns(String) }
|
300
|
+
def violation_type
|
301
|
+
'my_custom_violation_type'
|
302
|
+
end
|
303
|
+
|
304
|
+
sig { override.params(listed_offense: ReferenceOffense).returns(T::Boolean) }
|
305
|
+
def strict_mode_violation?(listed_offense)
|
306
|
+
# This will allow "strict mode" to be supported in your checker
|
307
|
+
referencing_package = listed_offense.reference.package
|
308
|
+
referencing_package.config["enforce_custom"] == "strict"
|
309
|
+
end
|
310
|
+
|
311
|
+
sig { override.params(reference: Reference).returns(T::Boolean) }
|
312
|
+
def invalid_reference?(reference)
|
313
|
+
# your logic here
|
314
|
+
end
|
315
|
+
|
316
|
+
sig { override.params(reference: Reference).returns(String) }
|
317
|
+
def message(reference)
|
318
|
+
# your message here
|
319
|
+
end
|
320
|
+
end
|
321
|
+
```
|
322
|
+
|
323
|
+
Then, in the `require` directive described above, you'll want to tell `packwerk` about it:
|
324
|
+
|
325
|
+
```yml
|
326
|
+
require:
|
327
|
+
- ./path/to/file.rb
|
328
|
+
```
|
329
|
+
|
330
|
+
### Custom Validators
|
331
|
+
|
332
|
+
Similar to checkers, you can define your own validator to be executed when `bin/packwerk validate` is invoked. This can be used to support your custom checker (by specifying permitted keys) or to provide any other validations you want to impose on packages.
|
333
|
+
|
334
|
+
To create a custom validator, you'll first need to create a validator class that includes `Packwerk::Validator`. You can use [`Packwerk::Validators::DependencyValidator`](lib/packwerk/validators/dependency_validator.rb) as a point of reference for this. Here is an example:
|
335
|
+
|
336
|
+
```ruby
|
337
|
+
# ./path/to/file.rb
|
338
|
+
class MyValidator
|
339
|
+
include Packwerk::Validator
|
340
|
+
# implement the `Validator` interface
|
341
|
+
|
342
|
+
sig { override.returns(T::Array[String]) }
|
343
|
+
def permitted_keys
|
344
|
+
['enforce_my_custom_checker']
|
345
|
+
end
|
346
|
+
|
347
|
+
sig { override.params(package_set: PackageSet, configuration: Configuration).returns(ApplicationValidator::Result) }
|
348
|
+
def call(package_set, configuration)
|
349
|
+
# your logic here
|
350
|
+
end
|
351
|
+
end
|
352
|
+
```
|
353
|
+
|
354
|
+
Then, in the `require` directive described above, you'll want to tell `packwerk` about it:
|
355
|
+
|
356
|
+
```yml
|
357
|
+
require:
|
358
|
+
- ./path/to/file.rb
|
359
|
+
```
|
data/dev.yml
CHANGED
data/exe/packwerk
CHANGED
data/gemfiles/Gemfile-rails-6-1
CHANGED
@@ -7,7 +7,7 @@ gemspec path: ".."
|
|
7
7
|
# Specify the same dependency sources as the application Gemfile
|
8
8
|
|
9
9
|
gem("spring")
|
10
|
-
gem("rails", "~> 6.1
|
10
|
+
gem("rails", "~> 6.1")
|
11
11
|
gem("constant_resolver", require: false)
|
12
12
|
gem("sorbet-runtime", require: false)
|
13
13
|
gem("rubocop-performance", require: false)
|