packwerk 1.2.0 → 1.4.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 +3 -3
- data/Gemfile.lock +11 -8
- data/README.md +5 -4
- data/TROUBLESHOOT.md +3 -3
- data/USAGE.md +37 -22
- data/bin/m +29 -0
- data/bin/rake +29 -0
- data/bin/rubocop +29 -0
- data/bin/srb +29 -0
- data/bin/tapioca +29 -0
- data/dev.yml +6 -6
- data/exe/packwerk +7 -1
- data/lib/packwerk/application_load_paths.rb +19 -8
- data/lib/packwerk/application_validator.rb +28 -22
- data/lib/packwerk/cli.rb +31 -34
- data/lib/packwerk/configuration.rb +1 -1
- data/lib/packwerk/const_node_inspector.rb +3 -2
- data/lib/packwerk/constant_name_inspector.rb +1 -1
- data/lib/packwerk/deprecated_references.rb +19 -7
- data/lib/packwerk/file_processor.rb +39 -14
- data/lib/packwerk/files_for_processing.rb +15 -4
- data/lib/packwerk/formatters/offenses_formatter.rb +10 -1
- data/lib/packwerk/generators/templates/package.yml +1 -1
- data/lib/packwerk/graph.rb +2 -0
- data/lib/packwerk/inflector.rb +1 -0
- data/lib/packwerk/node.rb +1 -0
- data/lib/packwerk/node_processor.rb +10 -22
- data/lib/packwerk/node_processor_factory.rb +0 -2
- data/lib/packwerk/node_visitor.rb +4 -2
- data/lib/packwerk/offenses_formatter.rb +4 -0
- data/lib/packwerk/package.rb +34 -5
- data/lib/packwerk/package_set.rb +43 -8
- data/lib/packwerk/parse_run.rb +9 -7
- data/lib/packwerk/parsed_constant_definitions.rb +1 -0
- data/lib/packwerk/reference.rb +2 -1
- data/lib/packwerk/reference_checking/checkers/checker.rb +21 -0
- data/lib/packwerk/reference_checking/checkers/dependency_checker.rb +31 -0
- data/lib/packwerk/reference_checking/checkers/privacy_checker.rb +58 -0
- data/lib/packwerk/reference_checking/reference_checker.rb +33 -0
- data/lib/packwerk/reference_extractor.rb +2 -2
- data/lib/packwerk/reference_offense.rb +1 -0
- data/lib/packwerk/run_context.rb +9 -6
- data/lib/packwerk/sanity_checker.rb +1 -1
- data/lib/packwerk/version.rb +1 -1
- data/lib/packwerk/violation_type.rb +1 -1
- data/lib/packwerk.rb +14 -4
- data/packwerk.gemspec +3 -1
- data/service.yml +0 -2
- data/sorbet/rbi/gems/psych@3.3.2.rbi +24 -0
- metadata +28 -10
- data/lib/packwerk/checker.rb +0 -17
- data/lib/packwerk/dependency_checker.rb +0 -26
- data/lib/packwerk/generators/application_validation.rb +0 -62
- data/lib/packwerk/generators/templates/packwerk +0 -23
- data/lib/packwerk/generators/templates/packwerk_validator_test.rb +0 -11
- data/lib/packwerk/privacy_checker.rb +0 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d150d89ede62e601246e2ff0290ecda4aab883a7c8b8be02feb86760ad26b72
|
4
|
+
data.tar.gz: 33b088b3387b8fdfe694967f20529fb8795dec8572dc9e036cb4979402f19c5b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 102154447670b85dc4e0825dcdf1ec11cd0fd291ffc1c7230b98da2208b0a39c38d2c8726a33226aeea68082e16a24917db7ad3380c871b9b02b0d76c8ac69fc
|
7
|
+
data.tar.gz: 666e3745e9167028e5d3901d74400848c87c1b8a64a7487d3e4bfe13916e2902990633db7c448057b8106fd4b6b5c5de08c697c88151a305c92d34259266448f
|
data/.github/workflows/ci.yml
CHANGED
@@ -30,7 +30,7 @@ jobs:
|
|
30
30
|
run: |
|
31
31
|
gem install bundler
|
32
32
|
bundle install --jobs 4 --retry 3
|
33
|
-
|
33
|
+
bin/rake
|
34
34
|
lint:
|
35
35
|
runs-on: ubuntu-latest
|
36
36
|
steps:
|
@@ -43,7 +43,7 @@ jobs:
|
|
43
43
|
run: |
|
44
44
|
gem install bundler
|
45
45
|
bundle install --jobs 4 --retry 3
|
46
|
-
|
46
|
+
bin/rubocop
|
47
47
|
static-type-checking:
|
48
48
|
runs-on: ubuntu-latest
|
49
49
|
steps:
|
@@ -56,4 +56,4 @@ jobs:
|
|
56
56
|
run: |
|
57
57
|
gem install bundler
|
58
58
|
bundle install --jobs 4 --retry 3
|
59
|
-
|
59
|
+
bin/srb tc
|
data/Gemfile.lock
CHANGED
@@ -87,10 +87,11 @@ GIT
|
|
87
87
|
PATH
|
88
88
|
remote: .
|
89
89
|
specs:
|
90
|
-
packwerk (1.
|
90
|
+
packwerk (1.4.0)
|
91
91
|
activesupport (>= 5.2)
|
92
92
|
ast
|
93
93
|
better_html
|
94
|
+
bundler
|
94
95
|
constant_resolver
|
95
96
|
parallel
|
96
97
|
parser
|
@@ -135,16 +136,16 @@ GEM
|
|
135
136
|
marcel (1.0.0)
|
136
137
|
method_source (1.0.0)
|
137
138
|
mini_mime (1.0.3)
|
138
|
-
mini_portile2 (2.
|
139
|
+
mini_portile2 (2.6.1)
|
139
140
|
minitest (5.14.4)
|
140
141
|
minitest-focus (1.2.1)
|
141
142
|
minitest (>= 4, < 6)
|
142
143
|
mocha (1.12.0)
|
143
144
|
nio4r (2.5.7)
|
144
|
-
nokogiri (1.
|
145
|
-
mini_portile2 (~> 2.
|
145
|
+
nokogiri (1.12.5)
|
146
|
+
mini_portile2 (~> 2.6.1)
|
146
147
|
racc (~> 1.4)
|
147
|
-
nokogiri (1.
|
148
|
+
nokogiri (1.12.5-x86_64-darwin)
|
148
149
|
racc (~> 1.4)
|
149
150
|
parallel (1.20.1)
|
150
151
|
parlour (6.0.0)
|
@@ -157,6 +158,7 @@ GEM
|
|
157
158
|
pry (0.14.0)
|
158
159
|
coderay (~> 1.1)
|
159
160
|
method_source (~> 1.0)
|
161
|
+
psych (3.3.2)
|
160
162
|
racc (1.5.2)
|
161
163
|
rack (2.2.3)
|
162
164
|
rack-test (1.1.0)
|
@@ -189,7 +191,7 @@ GEM
|
|
189
191
|
rubocop-sorbet (0.6.1)
|
190
192
|
rubocop
|
191
193
|
ruby-progressbar (1.11.0)
|
192
|
-
smart_properties (1.
|
194
|
+
smart_properties (1.16.3)
|
193
195
|
sorbet (0.5.6360)
|
194
196
|
sorbet-static (= 0.5.6360)
|
195
197
|
sorbet-runtime (0.5.6360)
|
@@ -233,16 +235,17 @@ GEM
|
|
233
235
|
|
234
236
|
PLATFORMS
|
235
237
|
ruby
|
238
|
+
x86_64-darwin
|
236
239
|
x86_64-darwin-20
|
237
240
|
|
238
241
|
DEPENDENCIES
|
239
|
-
bundler
|
240
242
|
byebug
|
241
243
|
constant_resolver
|
242
244
|
m
|
243
245
|
minitest-focus
|
244
246
|
mocha
|
245
247
|
packwerk!
|
248
|
+
psych (~> 3)
|
246
249
|
rails!
|
247
250
|
rake
|
248
251
|
rubocop-performance
|
@@ -254,4 +257,4 @@ DEPENDENCIES
|
|
254
257
|
tapioca
|
255
258
|
|
256
259
|
BUNDLED WITH
|
257
|
-
2.2.
|
260
|
+
2.2.22
|
data/README.md
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
## NOTE: Packwerk is considered to be feature-complete for Shopify's uses. We are currently accepting bug fixes only, and it is not being actively developed. Please fork this project if you are interested in adding new features.
|
4
4
|
|
5
|
-
> "I know who you are and because of that I know what you do."
|
6
|
-
> This knowledge is a dependency that raises the cost of change.
|
5
|
+
> "I know who you are and because of that I know what you do."
|
6
|
+
> This knowledge is a dependency that raises the cost of change.
|
7
7
|
|
8
8
|
-- _Sandi Metz, Practical Object-Oriented Design in Ruby_
|
9
9
|
|
@@ -47,7 +47,8 @@ Or install it yourself as:
|
|
47
47
|
|
48
48
|
$ gem install packwerk
|
49
49
|
|
50
|
-
|
50
|
+
2. Run `bundle binstub packwerk` to generate the binstub
|
51
|
+
3. Run `bin/packwerk init` to generate the configuration files
|
51
52
|
|
52
53
|
## Usage
|
53
54
|
|
@@ -59,7 +60,7 @@ Read [USAGE.md](USAGE.md) for usage once Packwerk is installed on your project.
|
|
59
60
|
|
60
61
|
## Development
|
61
62
|
|
62
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `
|
63
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
63
64
|
|
64
65
|
## Limitations
|
65
66
|
|
data/TROUBLESHOOT.md
CHANGED
@@ -14,11 +14,11 @@ Packwerk can give feedback via continuous integration (CI) if you have it set up
|
|
14
14
|
|
15
15
|
You can specify folders or packages in Packwerk commands for a shorter run time:
|
16
16
|
|
17
|
-
|
17
|
+
bin/packwerk check components/your_package
|
18
18
|
|
19
|
-
|
19
|
+
bin/packwerk update-deprecations components/your_package
|
20
20
|
|
21
|
-
_Note: You cannot specify folders or packages for `packwerk validate` because the command runs for the entire application._
|
21
|
+
_Note: You cannot specify folders or packages for `bin/packwerk validate` because the command runs for the entire application._
|
22
22
|
|
23
23
|
![](static/packwerk_check_violation.gif)
|
24
24
|
|
data/USAGE.md
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
# Packwerk usage
|
2
2
|
|
3
3
|
## Table of Contents
|
4
|
+
|
4
5
|
* [What problem does Packwerk solve?](#what-problem-does-packwerk-solve)
|
5
6
|
* [What is a package?](#what-is-a-package)
|
6
7
|
* [Package principles](#package-principles)
|
7
8
|
* [Getting started](#getting-started)
|
8
|
-
* [Setting up
|
9
|
+
* [Setting up Spring](#setting-up-spring)
|
10
|
+
* [Configuring Packwerk](#configuring-packwerk)
|
11
|
+
* [Using a custom ERB parser](#using-a-custom-erb-parser)
|
9
12
|
* [Inflections](#inflections)
|
10
13
|
* [Validating the package system](#validating-the-package-system)
|
11
14
|
* [Defining packages](#defining-packages)
|
@@ -19,11 +22,13 @@
|
|
19
22
|
* [Understanding the list of deprecated references](#understanding-the-list-of-deprecated-references)
|
20
23
|
|
21
24
|
## What problem does Packwerk solve?
|
25
|
+
|
22
26
|
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.
|
23
27
|
|
24
28
|
Packwerk is a gem that can be used to enforce boundaries between groups of code we call packages.
|
25
29
|
|
26
30
|
## What is a package?
|
31
|
+
|
27
32
|
A package is a folder containing autoloaded code. To decide whether code belongs together in a package, these are some design best practices:
|
28
33
|
|
29
34
|
- We should package things together that have high functional [cohesion](https://en.wikipedia.org/wiki/Cohesion_(computer_science)).
|
@@ -39,9 +44,12 @@ The [package principles](https://en.wikipedia.org/wiki/Package_principles) page
|
|
39
44
|
|
40
45
|
## Getting started
|
41
46
|
|
42
|
-
After including Packwerk in the Gemfile, you
|
47
|
+
After including Packwerk in the Gemfile, you will first want to generate a binstub:
|
48
|
+
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`.
|
49
|
+
|
50
|
+
Then, you can generate the necessary files to get Packwerk running by executing:
|
43
51
|
|
44
|
-
|
52
|
+
bin/packwerk init
|
45
53
|
|
46
54
|
Here is a list of files generated:
|
47
55
|
|
@@ -49,13 +57,18 @@ Here is a list of files generated:
|
|
49
57
|
|-----------------------------|--------------|------------|
|
50
58
|
| Packwerk configuration | packwerk.yml | See [Setting up the configuration file](#Setting-up-the-configuration-file) |
|
51
59
|
| Root package | package.yml | A package for the root folder |
|
52
|
-
| Bin script | bin/packwerk | For Rails applications to run Packwerk validation on CI, see [Validating the package system](#Validating-the-package-system) |
|
53
|
-
| Validation test | test/packwerk_validator_test.rb | For Ruby projects to run Packwerk validation using tests, see [Validating the package system](#Validating-the-package-system) |
|
54
60
|
| Custom inflections | config/inflections.yml | A custom inflections file is only required if you have custom inflections in `inflections.rb`, see [Inflections](#Inflections) |
|
55
61
|
|
56
62
|
After that, you may begin creating packages for your application. See [Defining packages](#Defining-packages)
|
57
63
|
|
58
|
-
|
64
|
+
### Setting up Spring
|
65
|
+
|
66
|
+
[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.
|
67
|
+
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.
|
68
|
+
Secondly, to enable Spring, first run `bin/spring binstub packwerk` which will "springify" the generated binstub.
|
69
|
+
|
70
|
+
|
71
|
+
## Configuring Packwerk
|
59
72
|
|
60
73
|
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.
|
61
74
|
|
@@ -133,18 +146,14 @@ Any new inflectors should be added to `config/inflections.yml`.
|
|
133
146
|
|
134
147
|
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.
|
135
148
|
|
136
|
-
|
149
|
+
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`.
|
137
150
|
|
138
|
-
|
151
|
+
Use the following command to validate the application:
|
139
152
|
|
140
|
-
|
141
|
-
|
142
|
-
bin/packwerk validate
|
153
|
+
packwerk validate
|
143
154
|
|
144
155
|
![](static/packwerk_validate.gif)
|
145
156
|
|
146
|
-
If running `packwerk init` on your Ruby project generates `test/packwerk_validator_test.rb`, you can use this test as validation.
|
147
|
-
|
148
157
|
## Defining packages
|
149
158
|
|
150
159
|
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.
|
@@ -152,6 +161,7 @@ You can create a `package.yml` in any folder to make it a package. The package n
|
|
152
161
|
_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._
|
153
162
|
|
154
163
|
### Package metadata
|
164
|
+
|
155
165
|
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.
|
156
166
|
|
157
167
|
Example:
|
@@ -169,6 +179,7 @@ Example:
|
|
169
179
|
Packwerk can perform two types of boundary checks: privacy and dependency.
|
170
180
|
|
171
181
|
#### Enforcing privacy boundary
|
182
|
+
|
172
183
|
A package's privacy boundary is violated when there is a reference to the package's private constants from a source outside the package.
|
173
184
|
|
174
185
|
There are two ways you can enforce privacy for your package:
|
@@ -228,17 +239,21 @@ It will be a dependency violation when `components/shop_identity` tries to refer
|
|
228
239
|
|
229
240
|
After enforcing the boundary checks for a package, you may execute:
|
230
241
|
|
231
|
-
|
242
|
+
packwerk check
|
243
|
+
|
244
|
+
Packwerk will check the entire codebase for any new or stale violations.
|
245
|
+
|
246
|
+
You can also specify folders for a shorter run time. When checking against folders all subfolders will be analyzed, irrespective of nested package boundaries.
|
232
247
|
|
233
|
-
|
248
|
+
packwerk check components/your_package
|
234
249
|
|
235
|
-
You can also specify
|
250
|
+
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.
|
236
251
|
|
237
|
-
|
252
|
+
packwerk check --packages=components/your_package,components/your_other_package
|
238
253
|
|
239
254
|
![](static/packwerk_check.gif)
|
240
255
|
|
241
|
-
In order to keep the package system valid at each version of the application, we recommend running `packwerk check` in your CI pipeline.
|
256
|
+
In order to keep the package system valid at each version of the application, we recommend running `bin/packwerk check` in your CI pipeline.
|
242
257
|
|
243
258
|
See: [TROUBLESHOOT.md - Sample violations](TROUBLESHOOT.md#Sample-violations)
|
244
259
|
|
@@ -248,17 +263,17 @@ For existing codebases, packages are likely to have existing boundary violations
|
|
248
263
|
|
249
264
|
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 [deprecated references list](#Understanding_the_list_of_deprecated_references) by executing:
|
250
265
|
|
251
|
-
|
266
|
+
bin/packwerk update-deprecations
|
252
267
|
|
253
|
-
Similar to `packwerk check`, you may also run `packwerk update-deprecations` on folders or packages:
|
268
|
+
Similar to `bin/packwerk check`, you may also run `bin/packwerk update-deprecations` on folders or packages:
|
254
269
|
|
255
|
-
|
270
|
+
bin/packwerk update-deprecations components/your_package
|
256
271
|
|
257
272
|
![](static/packwerk_update.gif)
|
258
273
|
|
259
274
|
_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._
|
260
275
|
|
261
|
-
`packwerk update-deprecations` should only be run to record existing violations and to remove deprecated references that have been worked off. Running `packwerk update-deprecations` to resolve a violation should be the very last resort.
|
276
|
+
`bin/packwerk update-deprecations` should only be run to record existing violations and to remove deprecated references that have been worked off. Running `bin/packwerk update-deprecations` to resolve a violation should be the very last resort.
|
262
277
|
|
263
278
|
See: [TROUBLESHOOT.md - Troubleshooting violations](TROUBLESHOOT.md#Troubleshooting_violations)
|
264
279
|
|
data/bin/m
ADDED
@@ -0,0 +1,29 @@
|
|
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 File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
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
ADDED
@@ -0,0 +1,29 @@
|
|
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 File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
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
ADDED
@@ -0,0 +1,29 @@
|
|
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 File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
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/srb
ADDED
@@ -0,0 +1,29 @@
|
|
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 File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
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
ADDED
@@ -0,0 +1,29 @@
|
|
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 File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
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
CHANGED
@@ -12,21 +12,21 @@ commands:
|
|
12
12
|
if [[ "$*" =~ ":"[0-9]+ ]];
|
13
13
|
then
|
14
14
|
# run test by its line number
|
15
|
-
|
15
|
+
bin/m "$@"
|
16
16
|
elif [[ "$#" -eq 1 && -f "$1" ]];
|
17
17
|
then
|
18
18
|
# run all tests in given file(s)
|
19
|
-
|
19
|
+
bin/rake test TEST="$@"
|
20
20
|
else
|
21
21
|
# run all tests
|
22
|
-
|
22
|
+
bin/rake test
|
23
23
|
fi
|
24
|
-
style: "
|
24
|
+
style: "bin/rubocop -D --auto-correct"
|
25
25
|
typecheck:
|
26
26
|
desc: "run Sorbet typechecking"
|
27
|
-
run: "
|
27
|
+
run: "bin/srb tc"
|
28
28
|
aliases: ['tc']
|
29
29
|
subcommands:
|
30
30
|
update:
|
31
31
|
desc: "update RBIs for gems"
|
32
|
-
run: "
|
32
|
+
run: "bin/tapioca sync -c 'dev typecheck update'"
|
data/exe/packwerk
CHANGED
@@ -3,4 +3,10 @@
|
|
3
3
|
|
4
4
|
require "packwerk"
|
5
5
|
|
6
|
-
|
6
|
+
# Needs to be run in test environment in order to have test helper paths available in the autoload paths
|
7
|
+
ENV["RAILS_ENV"] = "test"
|
8
|
+
|
9
|
+
# Command line arguments needs to be duplicated because spring modifies it
|
10
|
+
packwerk_argv = ARGV.dup
|
11
|
+
cli = Packwerk::Cli.new(style: Packwerk::OutputStyles::Coloured.new)
|
12
|
+
cli.run(packwerk_argv)
|
@@ -4,24 +4,20 @@
|
|
4
4
|
require "bundler"
|
5
5
|
|
6
6
|
module Packwerk
|
7
|
+
# Extracts the load paths from the analyzed application so that we can map constant names to paths.
|
7
8
|
module ApplicationLoadPaths
|
8
9
|
class << self
|
9
10
|
extend T::Sig
|
10
11
|
|
11
|
-
sig { returns(T::Array[String]) }
|
12
|
-
def extract_relevant_paths
|
13
|
-
|
12
|
+
sig { params(root: String, environment: String).returns(T::Array[String]) }
|
13
|
+
def extract_relevant_paths(root, environment)
|
14
|
+
require_application(root, environment)
|
14
15
|
all_paths = extract_application_autoload_paths
|
15
16
|
relevant_paths = filter_relevant_paths(all_paths)
|
16
17
|
assert_load_paths_present(relevant_paths)
|
17
18
|
relative_path_strings(relevant_paths)
|
18
19
|
end
|
19
20
|
|
20
|
-
sig { void }
|
21
|
-
def assert_application_booted
|
22
|
-
raise "The application needs to be booted to extract load paths" unless defined?(::Rails)
|
23
|
-
end
|
24
|
-
|
25
21
|
sig { returns(T::Array[String]) }
|
26
22
|
def extract_application_autoload_paths
|
27
23
|
Rails.application.railties
|
@@ -54,6 +50,21 @@ module Packwerk
|
|
54
50
|
.uniq
|
55
51
|
end
|
56
52
|
|
53
|
+
private
|
54
|
+
|
55
|
+
sig { params(root: String, environment: String).void }
|
56
|
+
def require_application(root, environment)
|
57
|
+
environment_file = "#{root}/config/environment"
|
58
|
+
|
59
|
+
if File.file?("#{environment_file}.rb")
|
60
|
+
ENV["RAILS_ENV"] ||= environment
|
61
|
+
|
62
|
+
require environment_file
|
63
|
+
else
|
64
|
+
raise "A Rails application could not be found in #{root}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
57
68
|
sig { params(paths: T::Array[T.untyped]).void }
|
58
69
|
def assert_load_paths_present(paths)
|
59
70
|
if paths.empty?
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: true
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "active_support/inflector/inflections"
|
@@ -7,12 +7,15 @@ require "pathname"
|
|
7
7
|
require "yaml"
|
8
8
|
|
9
9
|
module Packwerk
|
10
|
+
# Checks the structure of the application and its packwerk configuration to make sure we can run a check and deliver
|
11
|
+
# correct results.
|
10
12
|
class ApplicationValidator
|
11
|
-
def initialize(config_file_path:, configuration:)
|
13
|
+
def initialize(config_file_path:, configuration:, environment:)
|
12
14
|
@config_file_path = config_file_path
|
13
15
|
@configuration = configuration
|
16
|
+
@environment = environment
|
14
17
|
|
15
|
-
@application_load_paths = ApplicationLoadPaths.extract_relevant_paths
|
18
|
+
@application_load_paths = ApplicationLoadPaths.extract_relevant_paths(configuration.root_path, environment)
|
16
19
|
end
|
17
20
|
|
18
21
|
Result = Struct.new(:ok?, :error_value)
|
@@ -34,9 +37,9 @@ module Packwerk
|
|
34
37
|
end
|
35
38
|
|
36
39
|
def check_autoload_path_cache
|
37
|
-
expected = @application_load_paths
|
38
|
-
actual = @configuration.load_paths
|
39
|
-
if expected
|
40
|
+
expected = Set.new(@application_load_paths)
|
41
|
+
actual = Set.new(@configuration.load_paths)
|
42
|
+
if expected == actual
|
40
43
|
Result.new(true)
|
41
44
|
else
|
42
45
|
Result.new(
|
@@ -176,21 +179,9 @@ module Packwerk
|
|
176
179
|
edges = package_set.flat_map do |package|
|
177
180
|
package.dependencies.map { |dependency| [package, package_set.fetch(dependency)] }
|
178
181
|
end
|
179
|
-
dependency_graph = Packwerk::Graph.new(*edges)
|
180
|
-
|
181
|
-
|
182
|
-
#
|
183
|
-
# [a, b, c]
|
184
|
-
#
|
185
|
-
# to the string
|
186
|
-
#
|
187
|
-
# a -> b -> c -> a
|
188
|
-
#
|
189
|
-
cycle_strings = dependency_graph.cycles.map do |cycle|
|
190
|
-
cycle_strings = cycle.map(&:to_s)
|
191
|
-
cycle_strings << cycle.first.to_s
|
192
|
-
"\t- #{cycle_strings.join(" → ")}"
|
193
|
-
end
|
182
|
+
dependency_graph = Packwerk::Graph.new(*T.unsafe(edges))
|
183
|
+
|
184
|
+
cycle_strings = build_cycle_strings(dependency_graph.cycles)
|
194
185
|
|
195
186
|
if dependency_graph.acyclic?
|
196
187
|
Result.new(true)
|
@@ -279,6 +270,21 @@ module Packwerk
|
|
279
270
|
|
280
271
|
private
|
281
272
|
|
273
|
+
# Convert the cycles:
|
274
|
+
#
|
275
|
+
# [[a, b, c], [b, c]]
|
276
|
+
#
|
277
|
+
# to the string:
|
278
|
+
#
|
279
|
+
# ["a -> b -> c -> a", "b -> c -> b"]
|
280
|
+
def build_cycle_strings(cycles)
|
281
|
+
cycles.map do |cycle|
|
282
|
+
cycle_strings = cycle.map(&:to_s)
|
283
|
+
cycle_strings << cycle.first.to_s
|
284
|
+
"\t- #{cycle_strings.join(" → ")}"
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
282
288
|
def package_manifests_settings_for(setting)
|
283
289
|
package_manifests.map { |f| [f, (YAML.load_file(File.join(f)) || {})[setting]] }
|
284
290
|
end
|
@@ -292,7 +298,7 @@ module Packwerk
|
|
292
298
|
end
|
293
299
|
|
294
300
|
def package_manifests(glob_pattern = package_glob)
|
295
|
-
PackageSet.package_paths(@configuration.root_path, glob_pattern)
|
301
|
+
PackageSet.package_paths(@configuration.root_path, glob_pattern, @configuration.exclude)
|
296
302
|
.map { |f| File.realpath(f) }
|
297
303
|
end
|
298
304
|
|