packwerk 3.0.0 → 3.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -2
- data/exe/packwerk +4 -1
- data/lib/packwerk/application_validator.rb +3 -0
- data/lib/packwerk/association_inspector.rb +17 -4
- data/lib/packwerk/checker.rb +16 -7
- data/lib/packwerk/cli.rb +14 -177
- data/lib/packwerk/commands/base_command.rb +69 -0
- data/lib/packwerk/commands/check_command.rb +62 -0
- data/lib/packwerk/commands/help_command.rb +33 -0
- data/lib/packwerk/commands/init_command.rb +42 -0
- data/lib/packwerk/commands/lazy_loaded_entry.rb +37 -0
- data/lib/packwerk/commands/update_todo_command.rb +60 -0
- data/lib/packwerk/commands/uses_parse_run.rb +92 -0
- data/lib/packwerk/commands/validate_command.rb +46 -0
- data/lib/packwerk/commands/version_command.rb +18 -0
- data/lib/packwerk/commands.rb +54 -0
- data/lib/packwerk/configuration.rb +8 -1
- data/lib/packwerk/const_node_inspector.rb +2 -2
- data/lib/packwerk/constant_name_inspector.rb +2 -2
- data/lib/packwerk/file_processor.rb +12 -1
- data/lib/packwerk/formatters/default_offenses_formatter.rb +3 -3
- data/lib/packwerk/formatters/progress_formatter.rb +11 -0
- data/lib/packwerk/generators/templates/package.yml +2 -2
- data/lib/packwerk/generators/templates/packwerk.yml.erb +1 -1
- data/lib/packwerk/offense_collection.rb +32 -12
- data/lib/packwerk/offenses_formatter.rb +14 -5
- data/lib/packwerk/package.rb +1 -1
- data/lib/packwerk/package_todo.rb +86 -69
- data/lib/packwerk/parse_run.rb +42 -82
- data/lib/packwerk/parsers/factory.rb +3 -3
- data/lib/packwerk/parsers/ruby.rb +9 -2
- data/lib/packwerk/reference_checking/checkers/dependency_checker.rb +3 -3
- data/lib/packwerk/reference_extractor.rb +29 -1
- data/lib/packwerk/reference_offense.rb +1 -1
- data/lib/packwerk/run_context.rb +15 -4
- data/lib/packwerk/spring_command.rb +0 -2
- data/lib/packwerk/validator.rb +19 -5
- data/lib/packwerk/version.rb +1 -1
- data/lib/packwerk.rb +5 -28
- data/sorbet/config +1 -0
- data/sorbet/rbi/gems/actionpack@7.0.3.1.rbi +3280 -3450
- data/sorbet/rbi/gems/actionview@7.0.3.1.rbi +2322 -1782
- data/sorbet/rbi/gems/activesupport@7.0.3.1.rbi +2654 -3268
- data/sorbet/rbi/gems/ast@2.4.2.rbi +535 -6
- data/sorbet/rbi/gems/better_html@2.0.1.rbi +529 -0
- data/sorbet/rbi/gems/builder@3.2.4.rbi +4 -4
- data/sorbet/rbi/gems/byebug@11.1.3.rbi +32 -4
- data/sorbet/rbi/gems/concurrent-ruby@1.1.10.rbi +1750 -1840
- data/sorbet/rbi/gems/constant_resolver@0.2.0.rbi +15 -15
- data/sorbet/rbi/gems/crass@1.0.6.rbi +489 -5
- data/sorbet/rbi/gems/erubi@1.11.0.rbi +24 -21
- data/sorbet/rbi/gems/i18n@1.12.0.rbi +395 -395
- data/sorbet/rbi/gems/json@2.6.2.rbi +70 -77
- data/sorbet/rbi/gems/language_server-protocol@3.16.0.3.rbi +1 -1
- data/sorbet/rbi/gems/loofah@2.18.0.rbi +134 -134
- data/sorbet/rbi/gems/m@1.6.0.rbi +60 -60
- data/sorbet/rbi/gems/method_source@1.1.0.rbi +303 -0
- data/sorbet/rbi/gems/minitest-focus@1.3.1.rbi +22 -28
- data/sorbet/rbi/gems/minitest@5.16.2.rbi +384 -396
- data/sorbet/rbi/gems/mocha@1.14.0.rbi +589 -589
- data/sorbet/rbi/gems/netrc@0.11.0.rbi +37 -32
- data/sorbet/rbi/gems/{nokogiri@1.13.8.rbi → nokogiri@1.15.3.rbi} +1869 -1030
- data/sorbet/rbi/gems/{parallel@1.22.1.rbi → parallel@1.24.0.rbi} +85 -82
- data/sorbet/rbi/gems/parser@3.3.1.0.rbi +7320 -0
- data/sorbet/rbi/gems/prettier_print@0.1.0.rbi +1 -1
- data/sorbet/rbi/gems/prism@0.27.0.rbi +36983 -0
- data/sorbet/rbi/gems/{racc@1.6.0.rbi → racc@1.7.1.rbi} +42 -33
- data/sorbet/rbi/gems/rack-test@2.0.2.rbi +148 -338
- data/sorbet/rbi/gems/rack@2.2.4.rbi +1079 -1130
- data/sorbet/rbi/gems/rails-dom-testing@2.0.3.rbi +354 -22
- data/sorbet/rbi/gems/rails-html-sanitizer@1.4.3.rbi +113 -259
- data/sorbet/rbi/gems/railties@7.0.3.1.rbi +642 -638
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +109 -99
- data/sorbet/rbi/gems/rake@13.0.6.rbi +714 -599
- data/sorbet/rbi/gems/{rbi@0.0.15.rbi → rbi@0.1.12.rbi} +865 -801
- data/sorbet/rbi/gems/regexp_parser@2.5.0.rbi +853 -870
- data/sorbet/rbi/gems/rexml@3.2.5.rbi +480 -477
- data/sorbet/rbi/gems/rubocop-ast@1.21.0.rbi +1621 -1622
- data/sorbet/rbi/gems/rubocop-performance@1.14.3.rbi +507 -526
- data/sorbet/rbi/gems/rubocop-shopify@2.9.0.rbi +1 -1
- data/sorbet/rbi/gems/rubocop-sorbet@0.6.11.rbi +186 -203
- data/sorbet/rbi/gems/rubocop@1.34.1.rbi +8126 -8367
- data/sorbet/rbi/gems/{ruby-lsp@0.2.1.rbi → ruby-lsp@0.2.3.rbi} +2 -2
- data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +1235 -4
- data/sorbet/rbi/gems/smart_properties@1.17.0.rbi +90 -90
- data/sorbet/rbi/gems/spoom@1.3.2.rbi +4420 -0
- data/sorbet/rbi/gems/spring@4.0.0.rbi +104 -104
- data/sorbet/rbi/gems/syntax_tree@3.3.0.rbi +1 -1
- data/sorbet/rbi/gems/{tapioca@0.9.2.rbi → tapioca@0.13.3.rbi} +1596 -1253
- data/sorbet/rbi/gems/{thor@1.2.1.rbi → thor@1.3.1.rbi} +1047 -652
- data/sorbet/rbi/gems/tzinfo@2.0.5.rbi +531 -513
- data/sorbet/rbi/gems/unicode-display_width@2.2.0.rbi +13 -13
- data/sorbet/rbi/gems/{yard-sorbet@0.6.1.rbi → yard-sorbet@0.8.1.rbi} +132 -92
- data/sorbet/rbi/gems/{yard@0.9.28.rbi → yard@0.9.36.rbi} +3158 -3067
- data/sorbet/rbi/gems/zeitwerk@2.6.4.rbi +149 -145
- metadata +36 -84
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -27
- data/.github/pull_request_template.md +0 -28
- data/.github/workflows/ci.yml +0 -65
- data/.github/workflows/cla.yml +0 -22
- data/.gitignore +0 -13
- data/.rubocop.yml +0 -75
- data/.ruby-version +0 -1
- data/CODEOWNERS +0 -1
- data/CODE_OF_CONDUCT.md +0 -76
- data/CONTRIBUTING.md +0 -17
- data/Gemfile +0 -27
- data/Gemfile.lock +0 -201
- data/RESOLVING_VIOLATIONS.md +0 -81
- data/Rakefile +0 -13
- data/TROUBLESHOOT.md +0 -45
- data/UPGRADING.md +0 -54
- data/USAGE.md +0 -367
- data/bin/console +0 -15
- data/bin/m +0 -29
- data/bin/rake +0 -29
- data/bin/rubocop +0 -29
- data/bin/setup +0 -8
- data/bin/srb +0 -29
- data/bin/tapioca +0 -29
- data/dev.yml +0 -32
- data/docs/cohesion.png +0 -0
- data/gemfiles/Gemfile-rails-6-0 +0 -22
- data/gemfiles/Gemfile-rails-6-1 +0 -22
- data/lib/packwerk/cli/result.rb +0 -11
- data/packwerk.gemspec +0 -58
- data/shipit.rubygems.yml +0 -5
- data/sorbet/rbi/gems/actioncable@7.0.3.1.rbi +0 -2754
- data/sorbet/rbi/gems/actionmailbox@7.0.3.1.rbi +0 -1496
- data/sorbet/rbi/gems/actionmailer@7.0.3.1.rbi +0 -2362
- data/sorbet/rbi/gems/actiontext@7.0.3.1.rbi +0 -1569
- data/sorbet/rbi/gems/activejob@7.0.3.1.rbi +0 -2553
- data/sorbet/rbi/gems/activemodel@7.0.3.1.rbi +0 -5999
- data/sorbet/rbi/gems/activerecord@7.0.3.1.rbi +0 -37832
- data/sorbet/rbi/gems/activestorage@7.0.3.1.rbi +0 -2321
- data/sorbet/rbi/gems/better_html@1.0.16.rbi +0 -317
- data/sorbet/rbi/gems/coderay@1.1.3.rbi +0 -8
- data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +0 -1079
- data/sorbet/rbi/gems/digest@3.1.0.rbi +0 -189
- data/sorbet/rbi/gems/globalid@1.0.0.rbi +0 -572
- data/sorbet/rbi/gems/mail@2.7.1.rbi +0 -2490
- data/sorbet/rbi/gems/marcel@1.0.2.rbi +0 -220
- data/sorbet/rbi/gems/method_source@1.0.0.rbi +0 -76
- data/sorbet/rbi/gems/mini_mime@1.1.2.rbi +0 -170
- data/sorbet/rbi/gems/net-imap@0.2.3.rbi +0 -2147
- data/sorbet/rbi/gems/net-pop@0.1.1.rbi +0 -926
- data/sorbet/rbi/gems/net-protocol@0.1.3.rbi +0 -11
- data/sorbet/rbi/gems/net-smtp@0.3.1.rbi +0 -1108
- data/sorbet/rbi/gems/nio4r@2.5.8.rbi +0 -292
- data/sorbet/rbi/gems/parser@3.1.2.1.rbi +0 -9029
- data/sorbet/rbi/gems/pry@0.14.1.rbi +0 -8
- data/sorbet/rbi/gems/rails@7.0.3.1.rbi +0 -8
- data/sorbet/rbi/gems/spoom@1.1.11.rbi +0 -2181
- data/sorbet/rbi/gems/strscan@3.0.4.rbi +0 -8
- data/sorbet/rbi/gems/timeout@0.3.0.rbi +0 -142
- data/sorbet/rbi/gems/unparser@0.6.5.rbi +0 -4529
- data/sorbet/rbi/gems/webrick@1.7.0.rbi +0 -2582
- data/sorbet/rbi/gems/websocket-driver@0.7.5.rbi +0 -993
- data/sorbet/rbi/gems/websocket-extensions@0.1.5.rbi +0 -71
data/USAGE.md
DELETED
@@ -1,367 +0,0 @@
|
|
1
|
-
# Packwerk usage
|
2
|
-
|
3
|
-
## Table of Contents
|
4
|
-
|
5
|
-
* [What problem does Packwerk solve?](#what-problem-does-packwerk-solve)
|
6
|
-
* [What is a package?](#what-is-a-package)
|
7
|
-
* [Package principles](#package-principles)
|
8
|
-
* [Getting started](#getting-started)
|
9
|
-
* [Setting up Spring](#setting-up-spring)
|
10
|
-
* [Configuring Packwerk](#configuring-packwerk)
|
11
|
-
* [Using a custom ERB parser](#using-a-custom-erb-parser)
|
12
|
-
* [Validating the package system](#validating-the-package-system)
|
13
|
-
* [Defining packages](#defining-packages)
|
14
|
-
* [Package metadata](#package-metadata)
|
15
|
-
* [Types of boundary checks](#types-of-boundary-checks)
|
16
|
-
* [Enforcing dependency boundary](#enforcing-dependency-boundary)
|
17
|
-
* [Using strict mode](#using-strict-mode)
|
18
|
-
* [Checking for violations](#checking-for-violations)
|
19
|
-
* [Resolving new violations](#resolving-new-violations)
|
20
|
-
* [Understanding how to respond to new violations](#understanding-how-to-respond-to-new-violations)
|
21
|
-
* [Recording existing violations](#recording-existing-violations)
|
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)
|
25
|
-
|
26
|
-
## What problem does Packwerk solve?
|
27
|
-
|
28
|
-
Large applications need clear boundaries to avoid turning into a [ball of mud](https://en.wikipedia.org/wiki/Big_ball_of_mud). However, Ruby does not provide a good solution to enforcing boundaries between code.
|
29
|
-
|
30
|
-
Packwerk is a gem that can be used to enforce boundaries between groups of code we call packages.
|
31
|
-
|
32
|
-
## What is a package?
|
33
|
-
|
34
|
-
A package is a folder containing autoloaded code. To decide whether code belongs together in a package, these are some design best practices:
|
35
|
-
|
36
|
-
- We should package things together that have high functional [cohesion](https://en.wikipedia.org/wiki/Cohesion_(computer_science)).
|
37
|
-
- Packages should be relatively loosely coupled to each other.
|
38
|
-
|
39
|
-

|
40
|
-
|
41
|
-
### Package principles
|
42
|
-
|
43
|
-
Package principles help to guide the organization of classes in a large system. These principles can also be applied to packages in large and complex codebases.
|
44
|
-
|
45
|
-
The [package principles](https://en.wikipedia.org/wiki/Package_principles) page on Wikipedia does a good job explaining what well designed packages look like.
|
46
|
-
|
47
|
-
## Getting started
|
48
|
-
|
49
|
-
After including Packwerk in the Gemfile, you will first want to generate a binstub:
|
50
|
-
You can do this by running `bundle binstub packwerk`, which will generate a [binstub](https://bundler.io/man/bundle-binstubs.1.html#DESCRIPTION) at `bin/packwerk`.
|
51
|
-
|
52
|
-
Then, you can generate the necessary files to get Packwerk running by executing:
|
53
|
-
|
54
|
-
bin/packwerk init
|
55
|
-
|
56
|
-
Here is a list of files generated:
|
57
|
-
|
58
|
-
| File | Location | Description |
|
59
|
-
|-----------------------------|--------------|------------|
|
60
|
-
| Packwerk configuration | packwerk.yml | See [Setting up the configuration file](#configuring-packwerk) |
|
61
|
-
| Root package | package.yml | A package for the root folder |
|
62
|
-
|
63
|
-
After that, you may begin creating packages for your application. See [Defining packages](#Defining-packages)
|
64
|
-
|
65
|
-
### Setting up Spring
|
66
|
-
|
67
|
-
[Spring](https://github.com/rails/spring) is a preloader for Rails. Because `packwerk` loads `Rails`, it can be sped up dramatically by enabling spring. Packwerk supports the usage of Spring.
|
68
|
-
Firstly, spring needs to know about the packwerk spring command when spring is loading. To do that, add `require 'packwerk/spring_command'` to `config/spring.rb` in your application.
|
69
|
-
Secondly, to enable Spring, first run `bin/spring binstub packwerk` which will "springify" the generated binstub.
|
70
|
-
|
71
|
-
|
72
|
-
## Configuring Packwerk
|
73
|
-
|
74
|
-
Packwerk reads from the `packwerk.yml` configuration file in the root directory. Packwerk will run with the default configuration if any of these settings are not specified.
|
75
|
-
|
76
|
-
| Key | Default value | Description |
|
77
|
-
|----------------------|-------------------------------------------|--------------|
|
78
|
-
| include | **/*.{rb,rake,erb} | list of patterns for folder paths to include |
|
79
|
-
| exclude | {bin,node_modules,script,tmp,vendor}/**/* | list of patterns for folder paths to exclude |
|
80
|
-
| package_paths | **/ | a single pattern or a list of patterns to find package configuration files, see: [Defining packages](#Defining-packages) |
|
81
|
-
| custom_associations | N/A | list of custom associations, if any |
|
82
|
-
| parallel | true | when true, fork code parsing out to subprocesses |
|
83
|
-
| cache | false | when true, caches the results of parsing files |
|
84
|
-
| cache_directory | tmp/cache/packwerk | the directory that will hold the packwerk cache |
|
85
|
-
|
86
|
-
### Using a custom ERB parser
|
87
|
-
|
88
|
-
You can specify a custom ERB parser if needed. For example, if you're using `<%graphql>` tags from https://github.com/github/graphql-client in your ERBs, you can use a custom parser subclass to comment them out so that Packwerk can parse the rest of the file:
|
89
|
-
|
90
|
-
```ruby
|
91
|
-
class CustomParser < Packwerk::Parsers::Erb
|
92
|
-
def parse_buffer(buffer, file_path:)
|
93
|
-
preprocessed_source = buffer.source
|
94
|
-
|
95
|
-
# Comment out <%graphql ... %> tags. They won't contain any object
|
96
|
-
# references anyways.
|
97
|
-
preprocessed_source = preprocessed_source.gsub(/<%graphql/, "<%#")
|
98
|
-
|
99
|
-
preprocessed_buffer = Parser::Source::Buffer.new(file_path)
|
100
|
-
preprocessed_buffer.source = preprocessed_source
|
101
|
-
super(preprocessed_buffer, file_path: file_path)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
Packwerk::Parsers::Factory.instance.erb_parser_class = CustomParser
|
106
|
-
```
|
107
|
-
|
108
|
-
## Using the cache
|
109
|
-
Packwerk ships with an cache to help speed up file parsing. You can turn this on by setting `cache: true` in `packwerk.yml`.
|
110
|
-
|
111
|
-
This will write to `tmp/cache/packwerk`.
|
112
|
-
|
113
|
-
## Validating the package system
|
114
|
-
|
115
|
-
There are some criteria that an application must meet in order to have a valid package system. These criteria include having a valid autoload path cache, package definition files, and application folder structure. The dependency graph within the package system also has to be acyclic.
|
116
|
-
|
117
|
-
We recommend setting up the package system validation for your Rails application in a CI step (or through a test suite for Ruby projects) separate from `bin/packwerk check`.
|
118
|
-
|
119
|
-
Use the following command to validate the application:
|
120
|
-
|
121
|
-
bin/packwerk validate
|
122
|
-
|
123
|
-

|
124
|
-
|
125
|
-
## Defining packages
|
126
|
-
|
127
|
-
You can create a `package.yml` in any folder to make it a package. The package name is the path to the folder from the project root.
|
128
|
-
|
129
|
-
_Note: It is helpful to define a namespace that corresponds to the package name and contains at least all the public constants of the package. This makes it more obvious which package a constant is defined in._
|
130
|
-
|
131
|
-
### Package metadata
|
132
|
-
|
133
|
-
Package metadata can be included in the `package.yml`. Metadata won't be validated, and can thus be anything. We recommend including information on ownership and stewardship of the package.
|
134
|
-
|
135
|
-
Example:
|
136
|
-
```yaml
|
137
|
-
# components/sales/package.yml
|
138
|
-
metadata:
|
139
|
-
stewards:
|
140
|
-
- "@Shopify/sales"
|
141
|
-
slack_channels:
|
142
|
-
- "#sales"
|
143
|
-
```
|
144
|
-
|
145
|
-
## Types of boundary checks
|
146
|
-
|
147
|
-
Packwerk ships with dependency boundary checking only. See [`packwerk-extensions`](https://github.com/rubyatscale/packwerk-extensions) to incorporate privacy checks into your use of `packwerk`.
|
148
|
-
|
149
|
-
#### Enforcing dependency boundary
|
150
|
-
|
151
|
-
A package's dependency boundary is violated whenever it references a constant in some package that has not been declared as a dependency.
|
152
|
-
|
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.
|
154
|
-
|
155
|
-
Example:
|
156
|
-
|
157
|
-
```yaml
|
158
|
-
# components/shop_identity/package.yml
|
159
|
-
enforce_dependencies: true
|
160
|
-
dependencies:
|
161
|
-
- components/platform
|
162
|
-
```
|
163
|
-
|
164
|
-
It will be a dependency violation when `components/shop_identity` tries to reference a constant that is not within `components/platform` or itself.
|
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
|
-
|
175
|
-
## Checking for violations
|
176
|
-
|
177
|
-
After enforcing the boundary checks for a package, you may execute:
|
178
|
-
|
179
|
-
bin/packwerk check
|
180
|
-
|
181
|
-
Packwerk will check the entire codebase for any new or stale violations.
|
182
|
-
|
183
|
-
You can also specify folders for a shorter run time. When checking against folders all subfolders will be analyzed, irrespective of nested package boundaries.
|
184
|
-
|
185
|
-
bin/packwerk check components/your_package
|
186
|
-
|
187
|
-
You can also specify packages for a shorter run time. When checking against packages any packages nested underneath the specified packages will not be checked. This can be helpful to test packages like the root package, which can have many nested packages.
|
188
|
-
|
189
|
-
bin/packwerk check --packages=components/your_package,components/your_other_package
|
190
|
-
|
191
|
-

|
192
|
-
|
193
|
-
In order to keep the package system valid at each version of the application, we recommend running `bin/packwerk check` in your CI pipeline.
|
194
|
-
|
195
|
-
See: [TROUBLESHOOT.md - Sample violations](TROUBLESHOOT.md#Sample-violations)
|
196
|
-
|
197
|
-
## Resolving new violations
|
198
|
-
|
199
|
-
### Understanding how to respond to new violations
|
200
|
-
|
201
|
-
When you have a new dependency or privacy violation, what do you do?
|
202
|
-
|
203
|
-
See: [RESOLVING_VIOLATIONS.md](RESOLVING_VIOLATIONS.md)
|
204
|
-
|
205
|
-
## Recording existing violations
|
206
|
-
|
207
|
-
For existing codebases, packages are likely to have existing boundary violations.
|
208
|
-
|
209
|
-
If so, you will want to stop the bleeding and prevent more violations from occuring. The existing violations in the codebase can be recorded in a [todo list](#understanding-the-package-todo-file) by executing:
|
210
|
-
|
211
|
-
bin/packwerk update-todo
|
212
|
-
|
213
|
-

|
214
|
-
|
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.
|
216
|
-
|
217
|
-
See: [TROUBLESHOOT.md - Troubleshooting violations](TROUBLESHOOT.md#Troubleshooting_violations)
|
218
|
-
|
219
|
-
### Understanding the package todo file
|
220
|
-
|
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.
|
222
|
-
|
223
|
-
The package TODO list should not be added to, but worked off over time.
|
224
|
-
|
225
|
-
```yaml
|
226
|
-
components/merchant:
|
227
|
-
"::Checkouts::Core::CheckoutId":
|
228
|
-
violations:
|
229
|
-
- dependency
|
230
|
-
files:
|
231
|
-
- components/merchant/app/public/merchant/generate_order.rb
|
232
|
-
```
|
233
|
-
|
234
|
-
Above is an example of a constant violation entry in `package_todo.yml`.
|
235
|
-
|
236
|
-
* `components/merchant` - package where the constant violation is found
|
237
|
-
* `::Checkouts::Core::CheckoutId` - violated constant in question
|
238
|
-
* `dependency` - type of violation, either dependency or privacy
|
239
|
-
* `components/merchant/app/public/merchant/generate_order.rb` - path to the file containing the violated constant
|
240
|
-
|
241
|
-
Violations exist within the package that makes a violating reference. This means privacy violations of your package can be found listed in `package_todo.yml` files in the packages with the reference to a private constant.
|
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, such as the privacy checker listed below.
|
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
|
-
#### Privacy Checker
|
331
|
-
|
332
|
-
[`packwerk-extensions`](https://github.com/rubyatscale/packwerk-extensions) (originally extracted from `packwerk`) can be used to help define and enforce public API boundaries of a package. See the README.md for more details. To use this, add it to your `Gemfile` and then require it via `packwerk.yml`:
|
333
|
-
```yml
|
334
|
-
require:
|
335
|
-
- packwerk-extensions
|
336
|
-
```
|
337
|
-
|
338
|
-
### Custom Validators
|
339
|
-
|
340
|
-
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.
|
341
|
-
|
342
|
-
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:
|
343
|
-
|
344
|
-
```ruby
|
345
|
-
# ./path/to/file.rb
|
346
|
-
class MyValidator
|
347
|
-
include Packwerk::Validator
|
348
|
-
# implement the `Validator` interface
|
349
|
-
|
350
|
-
sig { override.returns(T::Array[String]) }
|
351
|
-
def permitted_keys
|
352
|
-
['enforce_my_custom_checker']
|
353
|
-
end
|
354
|
-
|
355
|
-
sig { override.params(package_set: PackageSet, configuration: Configuration).returns(ApplicationValidator::Result) }
|
356
|
-
def call(package_set, configuration)
|
357
|
-
# your logic here
|
358
|
-
end
|
359
|
-
end
|
360
|
-
```
|
361
|
-
|
362
|
-
Then, in the `require` directive described above, you'll want to tell `packwerk` about it:
|
363
|
-
|
364
|
-
```yml
|
365
|
-
require:
|
366
|
-
- ./path/to/file.rb
|
367
|
-
```
|
data/bin/console
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "bundler/setup"
|
5
|
-
require "packwerk"
|
6
|
-
|
7
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
-
# with your gem easier. You can also use a different console, if you like.
|
9
|
-
|
10
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
-
# require "pry"
|
12
|
-
# Pry.start
|
13
|
-
|
14
|
-
require "irb"
|
15
|
-
IRB.start(__FILE__)
|
data/bin/m
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
#
|
5
|
-
# This file was generated by Bundler.
|
6
|
-
#
|
7
|
-
# The application 'm' is installed as part of a gem, and
|
8
|
-
# this file is here to facilitate running it.
|
9
|
-
#
|
10
|
-
|
11
|
-
require "pathname"
|
12
|
-
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
-
Pathname.new(__FILE__).realpath)
|
14
|
-
|
15
|
-
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
-
|
17
|
-
if File.file?(bundle_binstub)
|
18
|
-
if /This file was generated by Bundler/.match?(File.read(bundle_binstub, 300))
|
19
|
-
load(bundle_binstub)
|
20
|
-
else
|
21
|
-
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
-
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
require "rubygems"
|
27
|
-
require "bundler/setup"
|
28
|
-
|
29
|
-
load Gem.bin_path("m", "m")
|
data/bin/rake
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
#
|
5
|
-
# This file was generated by Bundler.
|
6
|
-
#
|
7
|
-
# The application 'rake' is installed as part of a gem, and
|
8
|
-
# this file is here to facilitate running it.
|
9
|
-
#
|
10
|
-
|
11
|
-
require "pathname"
|
12
|
-
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
-
Pathname.new(__FILE__).realpath)
|
14
|
-
|
15
|
-
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
-
|
17
|
-
if File.file?(bundle_binstub)
|
18
|
-
if /This file was generated by Bundler/.match?(File.read(bundle_binstub, 300))
|
19
|
-
load(bundle_binstub)
|
20
|
-
else
|
21
|
-
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
-
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
require "rubygems"
|
27
|
-
require "bundler/setup"
|
28
|
-
|
29
|
-
load Gem.bin_path("rake", "rake")
|
data/bin/rubocop
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
#
|
5
|
-
# This file was generated by Bundler.
|
6
|
-
#
|
7
|
-
# The application 'rubocop' is installed as part of a gem, and
|
8
|
-
# this file is here to facilitate running it.
|
9
|
-
#
|
10
|
-
|
11
|
-
require "pathname"
|
12
|
-
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
-
Pathname.new(__FILE__).realpath)
|
14
|
-
|
15
|
-
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
-
|
17
|
-
if File.file?(bundle_binstub)
|
18
|
-
if /This file was generated by Bundler/.match?(File.read(bundle_binstub, 300))
|
19
|
-
load(bundle_binstub)
|
20
|
-
else
|
21
|
-
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
-
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
require "rubygems"
|
27
|
-
require "bundler/setup"
|
28
|
-
|
29
|
-
load Gem.bin_path("rubocop", "rubocop")
|
data/bin/setup
DELETED
data/bin/srb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
#
|
5
|
-
# This file was generated by Bundler.
|
6
|
-
#
|
7
|
-
# The application 'srb' is installed as part of a gem, and
|
8
|
-
# this file is here to facilitate running it.
|
9
|
-
#
|
10
|
-
|
11
|
-
require "pathname"
|
12
|
-
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
-
Pathname.new(__FILE__).realpath)
|
14
|
-
|
15
|
-
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
-
|
17
|
-
if File.file?(bundle_binstub)
|
18
|
-
if /This file was generated by Bundler/.match?(File.read(bundle_binstub, 300))
|
19
|
-
load(bundle_binstub)
|
20
|
-
else
|
21
|
-
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
-
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
require "rubygems"
|
27
|
-
require "bundler/setup"
|
28
|
-
|
29
|
-
load Gem.bin_path("sorbet", "srb")
|
data/bin/tapioca
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
#
|
5
|
-
# This file was generated by Bundler.
|
6
|
-
#
|
7
|
-
# The application 'tapioca' is installed as part of a gem, and
|
8
|
-
# this file is here to facilitate running it.
|
9
|
-
#
|
10
|
-
|
11
|
-
require "pathname"
|
12
|
-
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
-
Pathname.new(__FILE__).realpath)
|
14
|
-
|
15
|
-
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
-
|
17
|
-
if File.file?(bundle_binstub)
|
18
|
-
if /This file was generated by Bundler/.match?(File.read(bundle_binstub, 300))
|
19
|
-
load(bundle_binstub)
|
20
|
-
else
|
21
|
-
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
-
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
require "rubygems"
|
27
|
-
require "bundler/setup"
|
28
|
-
|
29
|
-
load Gem.bin_path("tapioca", "tapioca")
|
data/dev.yml
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
name: packwerk
|
2
|
-
|
3
|
-
type: ruby
|
4
|
-
|
5
|
-
up:
|
6
|
-
- ruby: 3.2.1
|
7
|
-
- bundler
|
8
|
-
|
9
|
-
commands:
|
10
|
-
test:
|
11
|
-
run: |
|
12
|
-
if [[ "$*" =~ ":"[0-9]+ ]];
|
13
|
-
then
|
14
|
-
# run test by its line number
|
15
|
-
bin/m "$@"
|
16
|
-
elif [[ "$#" -eq 1 && -f "$1" ]];
|
17
|
-
then
|
18
|
-
# run all tests in given file(s)
|
19
|
-
bin/rake test TEST="$@"
|
20
|
-
else
|
21
|
-
# run all tests
|
22
|
-
bin/rake test
|
23
|
-
fi
|
24
|
-
style: "bin/rubocop -D --auto-correct"
|
25
|
-
typecheck:
|
26
|
-
desc: "run Sorbet typechecking"
|
27
|
-
run: "bin/srb tc"
|
28
|
-
aliases: ['tc']
|
29
|
-
subcommands:
|
30
|
-
update:
|
31
|
-
desc: "update RBIs for gems"
|
32
|
-
run: "bin/tapioca sync -c 'dev typecheck update'"
|
data/docs/cohesion.png
DELETED
Binary file
|
data/gemfiles/Gemfile-rails-6-0
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
source("https://rubygems.org")
|
4
|
-
|
5
|
-
gemspec path: ".."
|
6
|
-
|
7
|
-
# Specify the same dependency sources as the application Gemfile
|
8
|
-
|
9
|
-
gem("spring")
|
10
|
-
gem("rails", "~> 6.0.0")
|
11
|
-
gem("constant_resolver", require: false)
|
12
|
-
gem("sorbet-runtime", require: false)
|
13
|
-
gem("rubocop-performance", require: false)
|
14
|
-
gem("rubocop-sorbet", require: false)
|
15
|
-
gem("mocha", require: false)
|
16
|
-
gem("rubocop-shopify", require: false)
|
17
|
-
gem("tapioca", require: false)
|
18
|
-
|
19
|
-
group :development do
|
20
|
-
gem("byebug", require: false)
|
21
|
-
gem("minitest-focus", require: false)
|
22
|
-
end
|
data/gemfiles/Gemfile-rails-6-1
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
source("https://rubygems.org")
|
4
|
-
|
5
|
-
gemspec path: ".."
|
6
|
-
|
7
|
-
# Specify the same dependency sources as the application Gemfile
|
8
|
-
|
9
|
-
gem("spring")
|
10
|
-
gem("rails", "~> 6.1")
|
11
|
-
gem("constant_resolver", require: false)
|
12
|
-
gem("sorbet-runtime", require: false)
|
13
|
-
gem("rubocop-performance", require: false)
|
14
|
-
gem("rubocop-sorbet", require: false)
|
15
|
-
gem("mocha", require: false)
|
16
|
-
gem("rubocop-shopify", require: false)
|
17
|
-
gem("tapioca", require: false)
|
18
|
-
|
19
|
-
group :development do
|
20
|
-
gem("byebug", require: false)
|
21
|
-
gem("minitest-focus", require: false)
|
22
|
-
end
|