packwerk 3.0.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +36 -8
- data/.ruby-version +1 -1
- data/Gemfile.lock +12 -7
- data/README.md +4 -2
- data/RESOLVING_VIOLATIONS.md +3 -8
- data/Rakefile +10 -1
- data/TROUBLESHOOT.md +2 -3
- data/UPGRADING.md +12 -0
- data/USAGE.md +11 -13
- data/dev.yml +1 -1
- data/exe/packwerk +4 -1
- data/lib/packwerk/application_validator.rb +3 -0
- data/lib/packwerk/checker.rb +13 -4
- data/lib/packwerk/cli.rb +14 -177
- data/lib/packwerk/commands/base_command.rb +69 -0
- data/lib/packwerk/commands/check_command.rb +60 -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 +4 -1
- 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/packwerk.yml.erb +1 -1
- data/lib/packwerk/offense_collection.rb +32 -12
- data/lib/packwerk/offenses_formatter.rb +13 -4
- data/lib/packwerk/package_todo.rb +89 -69
- data/lib/packwerk/parse_run.rb +42 -82
- data/lib/packwerk/spring_command.rb +0 -2
- data/lib/packwerk/validator.rb +18 -4
- data/lib/packwerk/version.rb +1 -1
- data/lib/packwerk.rb +4 -28
- data/sorbet/rbi/gems/parser@3.2.2.0.rbi +7250 -0
- metadata +14 -5
- data/lib/packwerk/cli/result.rb +0 -11
- data/sorbet/rbi/gems/parser@3.1.2.1.rbi +0 -9029
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d72fbde4259baa5a5e7f8bc2ca4406d9bc6a835051efafefc23ef6ef3ae13f32
|
4
|
+
data.tar.gz: 65bff304e2be348e16800b0d4f65ab33a1b5fb84ab15ba495e03904d099be4a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e25488e4af51932d503d4120e458fe92bc4ae2945ca8009219e01f4f2e7756e0430a0d9a6176f01d864d55644f262a06fb88494234b43aae43ba254437d7fb49
|
7
|
+
data.tar.gz: 2c821f3e11a16c52d84f7f4e68f01e788e95d5723fd77305f69e15aba07f5f1f81ea43e9c60b6c00ca4503df036eb2b3ea0e0ae1378f0c584d7252614064f9d9
|
data/.github/workflows/ci.yml
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
name: CI
|
2
2
|
|
3
|
-
on:
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- main
|
7
|
+
- '*-stable'
|
8
|
+
pull_request:
|
4
9
|
|
5
10
|
jobs:
|
6
11
|
tests:
|
@@ -12,10 +17,10 @@ jobs:
|
|
12
17
|
- gemfiles/Gemfile-rails-6-0
|
13
18
|
- gemfiles/Gemfile-rails-6-1
|
14
19
|
ruby:
|
15
|
-
- 2.7
|
16
|
-
- 3.0
|
17
|
-
- 3.1
|
18
|
-
- 3.2
|
20
|
+
- "2.7"
|
21
|
+
- "3.0"
|
22
|
+
- "3.1"
|
23
|
+
- "3.2"
|
19
24
|
env:
|
20
25
|
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
21
26
|
name: "Tests: Ruby ${{ matrix.ruby }} ${{ matrix.gemfile }}"
|
@@ -27,7 +32,30 @@ jobs:
|
|
27
32
|
ruby-version: ${{ matrix.ruby }}
|
28
33
|
bundler-cache: true
|
29
34
|
- name: Run tests
|
30
|
-
run: bin/rake
|
35
|
+
run: bin/rake test
|
36
|
+
loading-tests:
|
37
|
+
runs-on: ubuntu-latest
|
38
|
+
strategy:
|
39
|
+
matrix:
|
40
|
+
gemfile:
|
41
|
+
- Gemfile
|
42
|
+
ruby:
|
43
|
+
- "2.7"
|
44
|
+
- "3.0"
|
45
|
+
- "3.1"
|
46
|
+
- "3.2"
|
47
|
+
env:
|
48
|
+
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
49
|
+
name: "Loading Tests: Ruby ${{ matrix.ruby }} ${{ matrix.gemfile }}"
|
50
|
+
steps:
|
51
|
+
- uses: actions/checkout@v2
|
52
|
+
- name: Set up Ruby ${{ matrix.ruby }}
|
53
|
+
uses: ruby/setup-ruby@v1
|
54
|
+
with:
|
55
|
+
ruby-version: ${{ matrix.ruby }}
|
56
|
+
bundler-cache: true
|
57
|
+
- name: Run tests
|
58
|
+
run: bin/rake test:loading
|
31
59
|
lint:
|
32
60
|
runs-on: ubuntu-latest
|
33
61
|
name: Lint
|
@@ -36,7 +64,7 @@ jobs:
|
|
36
64
|
- name: Set up Ruby
|
37
65
|
uses: ruby/setup-ruby@v1
|
38
66
|
with:
|
39
|
-
ruby-version: 3.1
|
67
|
+
ruby-version: "3.1"
|
40
68
|
bundler-cache: true
|
41
69
|
- name: Run style checks
|
42
70
|
run: bin/rubocop
|
@@ -48,7 +76,7 @@ jobs:
|
|
48
76
|
- name: Set up Ruby
|
49
77
|
uses: ruby/setup-ruby@v1
|
50
78
|
with:
|
51
|
-
ruby-version: 3.1
|
79
|
+
ruby-version: "3.1"
|
52
80
|
# bundler-cache: true
|
53
81
|
- name: Run static type checks
|
54
82
|
run: |
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.2.
|
1
|
+
3.2.2
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
packwerk (3.
|
4
|
+
packwerk (3.1.0)
|
5
5
|
activesupport (>= 6.0)
|
6
6
|
ast
|
7
7
|
better_html
|
@@ -60,23 +60,27 @@ GEM
|
|
60
60
|
method_source (>= 0.6.7)
|
61
61
|
rake (>= 0.9.2.2)
|
62
62
|
method_source (1.0.0)
|
63
|
-
mini_portile2 (2.8.
|
63
|
+
mini_portile2 (2.8.4)
|
64
64
|
minitest (5.16.2)
|
65
65
|
minitest-focus (1.3.1)
|
66
66
|
minitest (>= 4, < 6)
|
67
67
|
mocha (1.14.0)
|
68
68
|
netrc (0.11.0)
|
69
|
-
nokogiri (1.
|
70
|
-
mini_portile2 (~> 2.8.
|
69
|
+
nokogiri (1.15.3)
|
70
|
+
mini_portile2 (~> 2.8.2)
|
71
|
+
racc (~> 1.4)
|
72
|
+
nokogiri (1.15.3-x86_64-darwin)
|
73
|
+
racc (~> 1.4)
|
74
|
+
nokogiri (1.15.3-x86_64-linux)
|
71
75
|
racc (~> 1.4)
|
72
76
|
parallel (1.22.1)
|
73
|
-
parser (3.
|
77
|
+
parser (3.2.2.0)
|
74
78
|
ast (~> 2.4.1)
|
75
79
|
prettier_print (0.1.0)
|
76
80
|
pry (0.14.1)
|
77
81
|
coderay (~> 1.1)
|
78
82
|
method_source (~> 1.0)
|
79
|
-
racc (1.
|
83
|
+
racc (1.7.1)
|
80
84
|
rack (2.2.4)
|
81
85
|
rack-test (2.0.2)
|
82
86
|
rack (>= 1.3)
|
@@ -178,6 +182,7 @@ PLATFORMS
|
|
178
182
|
ruby
|
179
183
|
x86_64-darwin
|
180
184
|
x86_64-darwin-20
|
185
|
+
x86_64-linux
|
181
186
|
|
182
187
|
DEPENDENCIES
|
183
188
|
byebug
|
@@ -198,4 +203,4 @@ DEPENDENCIES
|
|
198
203
|
zeitwerk
|
199
204
|
|
200
205
|
BUNDLED WITH
|
201
|
-
2.4.
|
206
|
+
2.4.8
|
data/README.md
CHANGED
@@ -58,10 +58,12 @@ Read [USAGE.md](USAGE.md) for usage once Packwerk is installed on your project.
|
|
58
58
|
Various third parties have built tooling on top of packwerk. Here's a selection of some that might prove useful:
|
59
59
|
|
60
60
|
- https://github.com/bellroy/graphwerk draws a graph of your package dependencies
|
61
|
-
- https://github.com/
|
62
|
-
- https://github.com/
|
61
|
+
- https://github.com/rubyatscale/packwerk-vscode integrates packwerk into Visual Studio Code so you can see violations right in your editor
|
62
|
+
- https://github.com/vinted/packwerk-intellij integrates packwerk into RubyMine so you can see violations right in your editor
|
63
|
+
- https://github.com/rubyatscale/packs-rails sets up Rails autoloading, as well as `rspec` and `FactoryBot` integration, for packages arranged in a flat list. packs-rails is quite convenient, but for autoloading we recommend to use `Rails::Engine`s instead.
|
63
64
|
- https://github.com/rubyatscale/danger-packwerk integrates packwerk with [danger.systems](https://danger.systems) to provide packwerk feedback as Github inline PR comments
|
64
65
|
- 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.
|
66
|
+
- https://github.com/alexevanczuk/packs is a Rust implementation of packwerk that has experimental support for non-Rails, non-Zeitwerk applications.
|
65
67
|
|
66
68
|
## Development
|
67
69
|
|
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/Rakefile
CHANGED
@@ -6,7 +6,16 @@ require "rake/testtask"
|
|
6
6
|
Rake::TestTask.new(:test) do |t|
|
7
7
|
t.libs << "test"
|
8
8
|
t.libs << "lib"
|
9
|
-
t.test_files = FileList["test/**/*_test.rb"]
|
9
|
+
t.test_files = FileList["test/**/*_test.rb"].reject do |file|
|
10
|
+
file.include?("test/loading/")
|
11
|
+
end
|
12
|
+
t.warning = false
|
13
|
+
end
|
14
|
+
|
15
|
+
Rake::TestTask.new("test:loading") do |t|
|
16
|
+
t.libs << "test"
|
17
|
+
t.libs << "lib"
|
18
|
+
t.test_files = FileList["test/loading/**/*_test.rb"]
|
10
19
|
t.warning = false
|
11
20
|
end
|
12
21
|
|
data/TROUBLESHOOT.md
CHANGED
@@ -14,9 +14,8 @@ You can specify folders or packages in Packwerk commands for a shorter run time:
|
|
14
14
|
|
15
15
|
bin/packwerk check components/your_package
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
_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._
|
20
19
|
|
21
20
|
![](static/packwerk_check_violation.gif)
|
22
21
|
|
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
@@ -144,7 +144,7 @@ Example:
|
|
144
144
|
|
145
145
|
## Types of boundary checks
|
146
146
|
|
147
|
-
Packwerk ships with dependency boundary checking only.
|
147
|
+
Packwerk ships with dependency boundary checking only. Other checking support may be added by extension gems.
|
148
148
|
|
149
149
|
#### Enforcing dependency boundary
|
150
150
|
|
@@ -172,6 +172,8 @@ Then, when you run `bin/packwerk check`, new violations will cause the following
|
|
172
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
173
|
```
|
174
174
|
|
175
|
+
Once the `strict` mode is enabled on a package, running `bin/packwerk update-todo` will not add new violations in the package_todo.yml file and the command will return an error.
|
176
|
+
|
175
177
|
## Checking for violations
|
176
178
|
|
177
179
|
After enforcing the boundary checks for a package, you may execute:
|
@@ -188,6 +190,10 @@ You can also specify packages for a shorter run time. When checking against pack
|
|
188
190
|
|
189
191
|
bin/packwerk check --packages=components/your_package,components/your_other_package
|
190
192
|
|
193
|
+
Using the following command line option you can also enable or disable parallel processing. It is enabled by default.
|
194
|
+
|
195
|
+
bin/packwerk check --[no-]parallel
|
196
|
+
|
191
197
|
![](static/packwerk_check.gif)
|
192
198
|
|
193
199
|
In order to keep the package system valid at each version of the application, we recommend running `bin/packwerk check` in your CI pipeline.
|
@@ -198,7 +204,7 @@ See: [TROUBLESHOOT.md - Sample violations](TROUBLESHOOT.md#Sample-violations)
|
|
198
204
|
|
199
205
|
### Understanding how to respond to new violations
|
200
206
|
|
201
|
-
When you have a new dependency
|
207
|
+
When you have a new dependency violation, what do you do?
|
202
208
|
|
203
209
|
See: [RESOLVING_VIOLATIONS.md](RESOLVING_VIOLATIONS.md)
|
204
210
|
|
@@ -235,10 +241,10 @@ Above is an example of a constant violation entry in `package_todo.yml`.
|
|
235
241
|
|
236
242
|
* `components/merchant` - package where the constant violation is found
|
237
243
|
* `::Checkouts::Core::CheckoutId` - violated constant in question
|
238
|
-
* `dependency` - type of violation,
|
244
|
+
* `dependency` - type of violation, typically dependency
|
239
245
|
* `components/merchant/app/public/merchant/generate_order.rb` - path to the file containing the violated constant
|
240
246
|
|
241
|
-
Violations exist within the package that makes a violating reference.
|
247
|
+
Violations exist within the package that makes a violating reference.
|
242
248
|
|
243
249
|
# Loading Extensions
|
244
250
|
|
@@ -284,7 +290,7 @@ bin/packwerk check --offenses-formatter=my_offenses_formatter
|
|
284
290
|
|
285
291
|
### Custom Checkers
|
286
292
|
|
287
|
-
Packwerk ships with a way to analyze dependencies and also supports custom checkers
|
293
|
+
Packwerk ships with a way to analyze dependencies and also supports custom checkers from extension gems.
|
288
294
|
|
289
295
|
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
296
|
|
@@ -327,14 +333,6 @@ require:
|
|
327
333
|
- ./path/to/file.rb
|
328
334
|
```
|
329
335
|
|
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
336
|
### Custom Validators
|
339
337
|
|
340
338
|
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.
|
data/dev.yml
CHANGED
data/exe/packwerk
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
4
|
+
unless defined?(Spring)
|
5
|
+
require "packwerk/disable_sorbet"
|
6
|
+
end
|
7
|
+
|
5
8
|
require "packwerk"
|
6
9
|
|
7
10
|
# Needs to be run in test environment in order to have test helper paths available in the autoload paths
|
@@ -11,6 +11,9 @@ module Packwerk
|
|
11
11
|
class ApplicationValidator
|
12
12
|
include Validator
|
13
13
|
extend T::Sig
|
14
|
+
extend ActiveSupport::Autoload
|
15
|
+
|
16
|
+
autoload :Helpers
|
14
17
|
|
15
18
|
sig { params(package_set: PackageSet, configuration: Configuration).returns(Validator::Result) }
|
16
19
|
def check_all(package_set, configuration)
|
data/lib/packwerk/checker.rb
CHANGED
@@ -13,14 +13,13 @@ module Packwerk
|
|
13
13
|
|
14
14
|
sig { params(base: Class).void }
|
15
15
|
def included(base)
|
16
|
-
|
17
|
-
@checkers ||= []
|
18
|
-
@checkers << base
|
16
|
+
checkers << base
|
19
17
|
end
|
20
18
|
|
21
19
|
sig { returns(T::Array[Checker]) }
|
22
20
|
def all
|
23
|
-
|
21
|
+
load_defaults
|
22
|
+
T.cast(checkers.map(&:new), T::Array[Checker])
|
24
23
|
end
|
25
24
|
|
26
25
|
sig { params(violation_type: String).returns(Checker) }
|
@@ -30,6 +29,16 @@ module Packwerk
|
|
30
29
|
|
31
30
|
private
|
32
31
|
|
32
|
+
sig { void }
|
33
|
+
def load_defaults
|
34
|
+
require("packwerk/reference_checking/checkers/dependency_checker")
|
35
|
+
end
|
36
|
+
|
37
|
+
sig { returns(T::Array[Class]) }
|
38
|
+
def checkers
|
39
|
+
@checkers ||= T.let([], T.nilable(T::Array[Class]))
|
40
|
+
end
|
41
|
+
|
33
42
|
sig { params(name: String).returns(Checker) }
|
34
43
|
def checker_by_violation_type(name)
|
35
44
|
@checker_by_violation_type ||= T.let(Checker.all.to_h do |checker|
|
data/lib/packwerk/cli.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "optparse"
|
5
|
-
|
6
4
|
module Packwerk
|
7
5
|
# A command-line interface to Packwerk.
|
8
6
|
class Cli
|
@@ -46,184 +44,23 @@ module Packwerk
|
|
46
44
|
|
47
45
|
sig { params(args: T::Array[String]).returns(T::Boolean) }
|
48
46
|
def execute_command(args)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
true
|
62
|
-
when nil, "help"
|
63
|
-
usage
|
64
|
-
else
|
65
|
-
@err_out.puts(
|
66
|
-
"'#{subcommand}' is not a packwerk command. See `packwerk help`."
|
67
|
-
)
|
68
|
-
false
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
sig { returns(T::Boolean) }
|
75
|
-
def init
|
76
|
-
@out.puts("📦 Initializing Packwerk...")
|
77
|
-
|
78
|
-
generate_configs
|
79
|
-
end
|
80
|
-
|
81
|
-
sig { returns(T::Boolean) }
|
82
|
-
def generate_configs
|
83
|
-
configuration_file = Generators::ConfigurationFile.generate(
|
84
|
-
root: @configuration.root_path,
|
85
|
-
out: @out
|
86
|
-
)
|
87
|
-
|
88
|
-
root_package = Generators::RootPackage.generate(root: @configuration.root_path, out: @out)
|
89
|
-
|
90
|
-
success = configuration_file && root_package
|
91
|
-
|
92
|
-
result = if success
|
93
|
-
<<~EOS
|
94
|
-
|
95
|
-
🎉 Packwerk is ready to be used. You can start defining packages and run `bin/packwerk check`.
|
96
|
-
For more information on how to use Packwerk, see: https://github.com/Shopify/packwerk/blob/main/USAGE.md
|
97
|
-
EOS
|
98
|
-
else
|
99
|
-
<<~EOS
|
100
|
-
|
101
|
-
⚠️ Packwerk is not ready to be used.
|
102
|
-
Please check output and refer to https://github.com/Shopify/packwerk/blob/main/USAGE.md for more information.
|
103
|
-
EOS
|
104
|
-
end
|
105
|
-
|
106
|
-
@out.puts(result)
|
107
|
-
success
|
108
|
-
end
|
109
|
-
|
110
|
-
sig { returns(T::Boolean) }
|
111
|
-
def usage
|
112
|
-
@err_out.puts(<<~USAGE)
|
113
|
-
Usage: #{$PROGRAM_NAME} <subcommand>
|
114
|
-
|
115
|
-
Subcommands:
|
116
|
-
init - set up packwerk
|
117
|
-
check - run all checks
|
118
|
-
update-todo - update package_todo.yml files
|
119
|
-
validate - verify integrity of packwerk and package configuration
|
120
|
-
version - output packwerk version
|
121
|
-
help - display help information about packwerk
|
122
|
-
USAGE
|
123
|
-
true
|
124
|
-
end
|
125
|
-
|
126
|
-
sig { params(result: Result).returns(T::Boolean) }
|
127
|
-
def output_result(result)
|
128
|
-
@out.puts
|
129
|
-
@out.puts(result.message)
|
130
|
-
result.status
|
131
|
-
end
|
132
|
-
|
133
|
-
sig do
|
134
|
-
params(
|
135
|
-
relative_file_paths: T::Array[String],
|
136
|
-
ignore_nested_packages: T::Boolean
|
137
|
-
).returns(FilesForProcessing)
|
138
|
-
end
|
139
|
-
def fetch_files_to_process(relative_file_paths, ignore_nested_packages)
|
140
|
-
files_for_processing = FilesForProcessing.fetch(
|
141
|
-
relative_file_paths: relative_file_paths,
|
142
|
-
ignore_nested_packages: ignore_nested_packages,
|
143
|
-
configuration: @configuration
|
144
|
-
)
|
145
|
-
@out.puts(<<~MSG.squish) if files_for_processing.files.empty?
|
146
|
-
No files found or given.
|
147
|
-
Specify files or check the include and exclude glob in the config file.
|
148
|
-
MSG
|
149
|
-
|
150
|
-
files_for_processing
|
151
|
-
end
|
152
|
-
|
153
|
-
sig { params(_paths: T::Array[String]).returns(T::Boolean) }
|
154
|
-
def validate(_paths)
|
155
|
-
result = T.let(nil, T.nilable(Validator::Result))
|
156
|
-
|
157
|
-
@progress_formatter.started_validation do
|
158
|
-
result = validator.check_all(package_set, @configuration)
|
159
|
-
|
160
|
-
list_validation_errors(result)
|
161
|
-
end
|
162
|
-
|
163
|
-
T.must(result).ok?
|
164
|
-
end
|
165
|
-
|
166
|
-
sig { returns(ApplicationValidator) }
|
167
|
-
def validator
|
168
|
-
ApplicationValidator.new
|
169
|
-
end
|
170
|
-
|
171
|
-
sig { returns(PackageSet) }
|
172
|
-
def package_set
|
173
|
-
PackageSet.load_all_from(
|
174
|
-
@configuration.root_path,
|
175
|
-
package_pathspec: @configuration.package_paths
|
176
|
-
)
|
177
|
-
end
|
178
|
-
|
179
|
-
sig { params(result: Validator::Result).void }
|
180
|
-
def list_validation_errors(result)
|
181
|
-
@out.puts
|
182
|
-
if result.ok?
|
183
|
-
@out.puts("Validation successful 🎉")
|
47
|
+
command = args.shift || "help"
|
48
|
+
command_class = Commands.for(command)
|
49
|
+
|
50
|
+
if command_class
|
51
|
+
command_class.new(
|
52
|
+
args,
|
53
|
+
configuration: @configuration,
|
54
|
+
out: @out,
|
55
|
+
err_out: @err_out,
|
56
|
+
progress_formatter: @progress_formatter,
|
57
|
+
offenses_formatter: @offenses_formatter,
|
58
|
+
).run
|
184
59
|
else
|
185
|
-
@
|
186
|
-
@out.puts
|
187
|
-
@out.puts(result.error_value)
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
sig { params(args: T::Array[String]).returns(ParseRun) }
|
192
|
-
def parse_run(args)
|
193
|
-
relative_file_paths = T.let([], T::Array[String])
|
194
|
-
ignore_nested_packages = nil
|
195
|
-
formatter = @offenses_formatter
|
60
|
+
@err_out.puts("'#{command}' is not a packwerk command. See `packwerk help`.",)
|
196
61
|
|
197
|
-
|
198
|
-
OptionParser.new do |parser|
|
199
|
-
parser.on("--packages=PACKAGESLIST", Array, "package names, comma separated") do |p|
|
200
|
-
relative_file_paths = p
|
201
|
-
end
|
202
|
-
end.parse!(args)
|
203
|
-
ignore_nested_packages = true
|
204
|
-
else
|
205
|
-
relative_file_paths = args
|
206
|
-
ignore_nested_packages = false
|
207
|
-
end
|
208
|
-
|
209
|
-
if args.any? { |arg| arg.include?("--offenses-formatter") }
|
210
|
-
OptionParser.new do |parser|
|
211
|
-
parser.on("--offenses-formatter=FORMATTER", String,
|
212
|
-
"identifier of offenses formatter to use") do |formatter_identifier|
|
213
|
-
formatter = OffensesFormatter.find(formatter_identifier)
|
214
|
-
end
|
215
|
-
end.parse!(args)
|
62
|
+
false
|
216
63
|
end
|
217
|
-
|
218
|
-
files_for_processing = fetch_files_to_process(relative_file_paths, ignore_nested_packages)
|
219
|
-
|
220
|
-
ParseRun.new(
|
221
|
-
relative_file_set: files_for_processing.files,
|
222
|
-
file_set_specified: files_for_processing.files_specified?,
|
223
|
-
configuration: @configuration,
|
224
|
-
progress_formatter: @progress_formatter,
|
225
|
-
offenses_formatter: formatter
|
226
|
-
)
|
227
64
|
end
|
228
65
|
end
|
229
66
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Commands
|
6
|
+
class BaseCommand
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Helpers
|
9
|
+
abstract!
|
10
|
+
|
11
|
+
@description = T.let("", String)
|
12
|
+
|
13
|
+
class << self
|
14
|
+
extend T::Sig
|
15
|
+
|
16
|
+
sig { params(description: T.nilable(String)).returns(String) }
|
17
|
+
def description(description = nil)
|
18
|
+
if description
|
19
|
+
@description = description
|
20
|
+
else
|
21
|
+
@description
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
sig do
|
27
|
+
params(
|
28
|
+
args: T::Array[String],
|
29
|
+
configuration: Configuration,
|
30
|
+
out: T.any(StringIO, IO),
|
31
|
+
err_out: T.any(StringIO, IO),
|
32
|
+
progress_formatter: Formatters::ProgressFormatter,
|
33
|
+
offenses_formatter: OffensesFormatter,
|
34
|
+
).void
|
35
|
+
end
|
36
|
+
def initialize(args, configuration:, out:, err_out:, progress_formatter:, offenses_formatter:)
|
37
|
+
@args = args
|
38
|
+
@configuration = configuration
|
39
|
+
@out = out
|
40
|
+
@err_out = err_out
|
41
|
+
@progress_formatter = progress_formatter
|
42
|
+
@offenses_formatter = offenses_formatter
|
43
|
+
end
|
44
|
+
|
45
|
+
sig { abstract.returns(T::Boolean) }
|
46
|
+
def run; end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
sig { returns(T::Array[String]) }
|
51
|
+
attr_reader :args
|
52
|
+
|
53
|
+
sig { returns(Configuration) }
|
54
|
+
attr_reader :configuration
|
55
|
+
|
56
|
+
sig { returns(T.any(StringIO, IO)) }
|
57
|
+
attr_reader :out
|
58
|
+
|
59
|
+
sig { returns(T.any(StringIO, IO)) }
|
60
|
+
attr_reader :err_out
|
61
|
+
|
62
|
+
sig { returns(Formatters::ProgressFormatter) }
|
63
|
+
attr_reader :progress_formatter
|
64
|
+
|
65
|
+
sig { returns(OffensesFormatter) }
|
66
|
+
attr_reader :offenses_formatter
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|