packwerk 1.2.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +3 -3
  3. data/Gemfile.lock +11 -8
  4. data/README.md +5 -4
  5. data/TROUBLESHOOT.md +3 -3
  6. data/USAGE.md +37 -22
  7. data/bin/m +29 -0
  8. data/bin/rake +29 -0
  9. data/bin/rubocop +29 -0
  10. data/bin/srb +29 -0
  11. data/bin/tapioca +29 -0
  12. data/dev.yml +6 -6
  13. data/exe/packwerk +7 -1
  14. data/lib/packwerk/application_load_paths.rb +19 -8
  15. data/lib/packwerk/application_validator.rb +28 -22
  16. data/lib/packwerk/cli.rb +31 -34
  17. data/lib/packwerk/configuration.rb +1 -1
  18. data/lib/packwerk/const_node_inspector.rb +3 -2
  19. data/lib/packwerk/constant_name_inspector.rb +1 -1
  20. data/lib/packwerk/deprecated_references.rb +19 -7
  21. data/lib/packwerk/file_processor.rb +39 -14
  22. data/lib/packwerk/files_for_processing.rb +15 -4
  23. data/lib/packwerk/formatters/offenses_formatter.rb +10 -1
  24. data/lib/packwerk/generators/templates/package.yml +1 -1
  25. data/lib/packwerk/graph.rb +2 -0
  26. data/lib/packwerk/inflector.rb +1 -0
  27. data/lib/packwerk/node.rb +1 -0
  28. data/lib/packwerk/node_processor.rb +10 -22
  29. data/lib/packwerk/node_processor_factory.rb +0 -2
  30. data/lib/packwerk/node_visitor.rb +4 -2
  31. data/lib/packwerk/offenses_formatter.rb +4 -0
  32. data/lib/packwerk/package.rb +34 -5
  33. data/lib/packwerk/package_set.rb +43 -8
  34. data/lib/packwerk/parse_run.rb +9 -7
  35. data/lib/packwerk/parsed_constant_definitions.rb +1 -0
  36. data/lib/packwerk/reference.rb +2 -1
  37. data/lib/packwerk/reference_checking/checkers/checker.rb +21 -0
  38. data/lib/packwerk/reference_checking/checkers/dependency_checker.rb +31 -0
  39. data/lib/packwerk/reference_checking/checkers/privacy_checker.rb +58 -0
  40. data/lib/packwerk/reference_checking/reference_checker.rb +33 -0
  41. data/lib/packwerk/reference_extractor.rb +2 -2
  42. data/lib/packwerk/reference_offense.rb +1 -0
  43. data/lib/packwerk/run_context.rb +9 -6
  44. data/lib/packwerk/sanity_checker.rb +1 -1
  45. data/lib/packwerk/version.rb +1 -1
  46. data/lib/packwerk/violation_type.rb +1 -1
  47. data/lib/packwerk.rb +14 -4
  48. data/packwerk.gemspec +3 -1
  49. data/service.yml +0 -2
  50. data/sorbet/rbi/gems/psych@3.3.2.rbi +24 -0
  51. metadata +28 -10
  52. data/lib/packwerk/checker.rb +0 -17
  53. data/lib/packwerk/dependency_checker.rb +0 -26
  54. data/lib/packwerk/generators/application_validation.rb +0 -62
  55. data/lib/packwerk/generators/templates/packwerk +0 -23
  56. data/lib/packwerk/generators/templates/packwerk_validator_test.rb +0 -11
  57. data/lib/packwerk/privacy_checker.rb +0 -53
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ced63ca0995c039913777f3e8ba1aac428970d8fc515b1629c9212f71baab34b
4
- data.tar.gz: 331b0a5f30e7104b4eb3e44920739ea592e7e3dc7df3dca339a74260fa13d62b
3
+ metadata.gz: 5d150d89ede62e601246e2ff0290ecda4aab883a7c8b8be02feb86760ad26b72
4
+ data.tar.gz: 33b088b3387b8fdfe694967f20529fb8795dec8572dc9e036cb4979402f19c5b
5
5
  SHA512:
6
- metadata.gz: a487a501ca28d341a888eea2e0ec13c4831897680ce1d17bef2a4943408ff718d75cffe9a50030fd93d7a2388d161c7a3cd946a0764930f89016c2f2c81e5306
7
- data.tar.gz: d4685f869617c8b1141125fcaacbde96839be3d239aa2422db6f76dbcb485f42fcde97e0c6aaa580ef4abcd847ebbfd9979fe4896f0bc5c7421d104673346981
6
+ metadata.gz: 102154447670b85dc4e0825dcdf1ec11cd0fd291ffc1c7230b98da2208b0a39c38d2c8726a33226aeea68082e16a24917db7ad3380c871b9b02b0d76c8ac69fc
7
+ data.tar.gz: 666e3745e9167028e5d3901d74400848c87c1b8a64a7487d3e4bfe13916e2902990633db7c448057b8106fd4b6b5c5de08c697c88151a305c92d34259266448f
@@ -30,7 +30,7 @@ jobs:
30
30
  run: |
31
31
  gem install bundler
32
32
  bundle install --jobs 4 --retry 3
33
- bundle exec rake
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
- bundle exec rubocop
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
- bundle exec srb tc
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.2.0)
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.5.1)
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.11.5)
145
- mini_portile2 (~> 2.5.0)
145
+ nokogiri (1.12.5)
146
+ mini_portile2 (~> 2.6.1)
146
147
  racc (~> 1.4)
147
- nokogiri (1.11.5-x86_64-darwin)
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.15.0)
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.14
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
- 3. Run `bundle exec packwerk init` to generate the configuration files
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 `bundle exec rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
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
- bundle exec packwerk check components/your_package
17
+ bin/packwerk check components/your_package
18
18
 
19
- bundle exec packwerk update-deprecations components/your_package
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 the configuration file](#setting-up-the-configuration-file)
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 can generate the necessary files to get Packwerk running by executing:
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
- bundle exec packwerk init
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
- ## Setting up the configuration file
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
- The package system can be validated through a series of built in validation checks. Currently, the validation checks require the application to be booted either through `spring` or as part of its test suite.
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
- 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 `packwerk check`.
151
+ Use the following command to validate the application:
139
152
 
140
- If running `packwerk init` generates a `bin/packwerk` script, proceed to run:
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
- bundle exec packwerk check
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
- Packwerk will check the entire codebase for any violations.
248
+ packwerk check components/your_package
234
249
 
235
- You can also specify folders or packages for a shorter run time:
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
- bundle exec packwerk check components/your_package
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
- bundle exec packwerk update-deprecations
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
- bundle exec packwerk update-deprecations components/your_package
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
- bundle exec m "$@"
15
+ bin/m "$@"
16
16
  elif [[ "$#" -eq 1 && -f "$1" ]];
17
17
  then
18
18
  # run all tests in given file(s)
19
- bundle exec rake test TEST="$@"
19
+ bin/rake test TEST="$@"
20
20
  else
21
21
  # run all tests
22
- bundle exec rake test
22
+ bin/rake test
23
23
  fi
24
- style: "bundle exec rubocop -D --auto-correct"
24
+ style: "bin/rubocop -D --auto-correct"
25
25
  typecheck:
26
26
  desc: "run Sorbet typechecking"
27
- run: "bundle exec srb tc"
27
+ run: "bin/srb tc"
28
28
  aliases: ['tc']
29
29
  subcommands:
30
30
  update:
31
31
  desc: "update RBIs for gems"
32
- run: "bundle exec tapioca sync -c 'dev typecheck update'"
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
- Packwerk::Cli.new(style: Packwerk::OutputStyles::Coloured.new).run(ARGV.dup)
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
- assert_application_booted
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: false
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.sort == actual.sort
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
- # Convert the cycle
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