packwerk 1.3.0 → 2.0.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/CHANGELOG.md +1 -0
  3. data/Gemfile.lock +11 -8
  4. data/README.md +2 -3
  5. data/TROUBLESHOOT.md +3 -3
  6. data/UPGRADING.md +54 -0
  7. data/USAGE.md +27 -53
  8. data/exe/packwerk +7 -1
  9. data/lib/packwerk/application_load_paths.rb +1 -0
  10. data/lib/packwerk/application_validator.rb +3 -54
  11. data/lib/packwerk/association_inspector.rb +1 -1
  12. data/lib/packwerk/cli.rb +37 -20
  13. data/lib/packwerk/configuration.rb +34 -4
  14. data/lib/packwerk/const_node_inspector.rb +3 -2
  15. data/lib/packwerk/constant_discovery.rb +1 -1
  16. data/lib/packwerk/constant_name_inspector.rb +1 -1
  17. data/lib/packwerk/deprecated_references.rb +19 -7
  18. data/lib/packwerk/file_processor.rb +39 -14
  19. data/lib/packwerk/files_for_processing.rb +15 -4
  20. data/lib/packwerk/formatters/offenses_formatter.rb +1 -1
  21. data/lib/packwerk/formatters/progress_formatter.rb +1 -1
  22. data/lib/packwerk/generators/configuration_file.rb +4 -19
  23. data/lib/packwerk/generators/templates/package.yml +1 -1
  24. data/lib/packwerk/generators/templates/packwerk.yml.erb +0 -6
  25. data/lib/packwerk/graph.rb +2 -0
  26. data/lib/packwerk/node.rb +1 -0
  27. data/lib/packwerk/node_processor.rb +10 -22
  28. data/lib/packwerk/node_processor_factory.rb +0 -2
  29. data/lib/packwerk/node_visitor.rb +4 -2
  30. data/lib/packwerk/package.rb +25 -4
  31. data/lib/packwerk/package_set.rb +43 -8
  32. data/lib/packwerk/parsed_constant_definitions.rb +1 -0
  33. data/lib/packwerk/reference.rb +2 -1
  34. data/lib/packwerk/reference_checking/checkers/checker.rb +21 -0
  35. data/lib/packwerk/reference_checking/checkers/dependency_checker.rb +31 -0
  36. data/lib/packwerk/reference_checking/checkers/privacy_checker.rb +58 -0
  37. data/lib/packwerk/reference_checking/reference_checker.rb +33 -0
  38. data/lib/packwerk/reference_extractor.rb +2 -2
  39. data/lib/packwerk/reference_offense.rb +1 -0
  40. data/lib/packwerk/run_context.rb +10 -7
  41. data/lib/packwerk/sanity_checker.rb +1 -1
  42. data/lib/packwerk/spring_command.rb +28 -0
  43. data/lib/packwerk/version.rb +1 -1
  44. data/lib/packwerk/violation_type.rb +1 -1
  45. data/lib/packwerk.rb +17 -12
  46. data/packwerk.gemspec +3 -1
  47. data/service.yml +0 -2
  48. data/sorbet/rbi/gems/psych@3.3.2.rbi +24 -0
  49. metadata +25 -11
  50. data/lib/packwerk/checker.rb +0 -17
  51. data/lib/packwerk/dependency_checker.rb +0 -26
  52. data/lib/packwerk/generators/inflections_file.rb +0 -43
  53. data/lib/packwerk/generators/templates/inflections.yml +0 -6
  54. data/lib/packwerk/inflections/custom.rb +0 -33
  55. data/lib/packwerk/inflections/default.rb +0 -73
  56. data/lib/packwerk/inflector.rb +0 -48
  57. data/lib/packwerk/privacy_checker.rb +0 -53
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c73a1063d42615133bdd6a3cc797838f0d4a0b71c716a5a6dd2730c125723ca8
4
- data.tar.gz: ede1cf336fb3c64f0093c9b853bc4db9e06004d8bfd297fd9ff60a6253c16e1c
3
+ metadata.gz: a473db2c54adea132ca7e61a51072b570d7e37b7d63c021259eef3cc7625177b
4
+ data.tar.gz: 1b8486e61969621fac562d2fb6ebe47dcf6f71fcd13f6d18524337a3ed831ac2
5
5
  SHA512:
6
- metadata.gz: 3a2dd60787d4b8bb40e40491fb364321c0e2d086df7315b64ccb7a27b21f28d4e5e1855d40b52c1aadcbaf61d02ececa43d186fc10abdbe1de2c9609469586ef
7
- data.tar.gz: 327267b82f798960ccf5a295125a980077bcfb9dfc00587583dcefbdcd1bfcea935f79f0f4c2efebad8d90d08807d6f08d800240bc4315ded513abdca9efcce0
6
+ metadata.gz: 8c05f0ce6e8b398a87e98453c721d00707628918ce8455600c1cb00fb2cb9c68efc6bea373323e889d36fc3658ff45db3508b0e6386b876da58ca50cdb53ab9f
7
+ data.tar.gz: 51586b68f489ff1f32df06338d05a64cab753da56c11575b3c5c8fa55019e9a8527e0a20c54c8695e1432307227d26c10f01b0d6c610e41c55d84296be000bd8
data/CHANGELOG.md ADDED
@@ -0,0 +1 @@
1
+ See https://github.com/Shopify/packwerk/releases.
data/Gemfile.lock CHANGED
@@ -87,10 +87,11 @@ GIT
87
87
  PATH
88
88
  remote: .
89
89
  specs:
90
- packwerk (1.3.0)
90
+ packwerk (2.0.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
@@ -35,8 +35,6 @@ Watch a [1-minute video demo](https://www.youtube.com/watch?v=NwqlyBAxVpQ&featur
35
35
  gem 'packwerk'
36
36
  ```
37
37
 
38
- _Note: Packwerk has to be grouped in production environment within the Gemfile if your Rails app has custom inflections._
39
-
40
38
  2. Install the gem
41
39
 
42
40
  Execute:
@@ -47,7 +45,8 @@ Or install it yourself as:
47
45
 
48
46
  $ gem install packwerk
49
47
 
50
- 3. Run `packwerk init` to generate the configuration files
48
+ 2. Run `bundle binstub packwerk` to generate the binstub
49
+ 3. Run `bin/packwerk init` to generate the configuration files
51
50
 
52
51
  ## Usage
53
52
 
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
- packwerk check components/your_package
17
+ bin/packwerk check components/your_package
18
18
 
19
- 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/UPGRADING.md ADDED
@@ -0,0 +1,54 @@
1
+ # Upgrading from 1.x to 2.0
2
+
3
+ With Packwerk 2.0, we made a few changes to simplify the setup. Updating will require removing some previously necessary files and configuration.
4
+
5
+ ## Gem group
6
+
7
+ Because packwerk is no longer involved in specifying the application's inflections, it doesn't have to live in the `production` group in your `Gemfile` anymore. We recommend moving it to the `development` group.
8
+
9
+ ## Removing application config caches
10
+
11
+ ### Load paths
12
+ We no longer require the `load_paths` key in `packwerk.yml`. You can simply delete the load_paths key as it will not be read anymore. Instead, Packwerk will ask Rails for load paths. If you're using spring, make sure to properly set up spring (see [USAGE.md](USAGE.md#setting-up-spring)) to keep packwerk fast.
13
+
14
+ ### Inflections
15
+ We no longer require a custom `inflections.yml` file. Instead, you'll want to revert BACK to using the `inflections.rb` initializer as you would have done prior to adopting packwerk. To do this, you'll want to convert back to using the plain [ActiveSupport Inflections API](https://api.rubyonrails.org/classes/ActiveSupport/Inflector/Inflections.html).
16
+
17
+
18
+ Given the following example `inflections.yml`, here is an example `inflections.rb` that would follow. Tip: if you're using git, you can run `git log config/inflections.yml`, find the first commit that introduced `inflections.yml`, find the COMMIT_SHA, and then run `git show COMMIT_SHA` to see what your inflections file looked like before (note that you may have changed `inflections.yml` since then, though).
19
+
20
+ `config/inflections.yml`
21
+ ```yml
22
+ # List your inflections in this file instead of `inflections.rb`
23
+ # See steps to set up custom inflections:
24
+ # https://github.com/Shopify/packwerk/blob/main/USAGE.md#Inflections
25
+
26
+ acronym:
27
+ - 'HTML'
28
+ - 'API'
29
+
30
+ singular:
31
+ - ['oxen', 'oxen']
32
+
33
+ irregular:
34
+ - ['person', 'people']
35
+
36
+ uncountable:
37
+ - 'fish'
38
+ - 'sheep'
39
+ ```
40
+
41
+ `config/initializers/inflections.rb`
42
+ ```ruby
43
+ ActiveSupport::Inflector.inflections(:en) do |inflect|
44
+ inflect.acronym('HTML')
45
+ inflect.acronym('API')
46
+
47
+ inflect.singular('oxen', 'oxen')
48
+
49
+ inflect.irregular('person', 'people')
50
+
51
+ inflect.uncountable('fish')
52
+ inflect.uncountable('sheep')
53
+ end
54
+ ```
data/USAGE.md CHANGED
@@ -6,8 +6,9 @@
6
6
  * [What is a package?](#what-is-a-package)
7
7
  * [Package principles](#package-principles)
8
8
  * [Getting started](#getting-started)
9
- * [Setting up the configuration file](#setting-up-the-configuration-file)
10
- * [Inflections](#inflections)
9
+ * [Setting up Spring](#setting-up-spring)
10
+ * [Configuring Packwerk](#configuring-packwerk)
11
+ * [Using a custom ERB parser](#using-a-custom-erb-parser)
11
12
  * [Validating the package system](#validating-the-package-system)
12
13
  * [Defining packages](#defining-packages)
13
14
  * [Package metadata](#package-metadata)
@@ -42,9 +43,12 @@ The [package principles](https://en.wikipedia.org/wiki/Package_principles) page
42
43
 
43
44
  ## Getting started
44
45
 
45
- After including Packwerk in the Gemfile, you can generate the necessary files to get Packwerk running by executing:
46
+ After including Packwerk in the Gemfile, you will first want to generate a binstub:
47
+ 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`.
46
48
 
47
- packwerk init
49
+ Then, you can generate the necessary files to get Packwerk running by executing:
50
+
51
+ bin/packwerk init
48
52
 
49
53
  Here is a list of files generated:
50
54
 
@@ -52,11 +56,17 @@ Here is a list of files generated:
52
56
  |-----------------------------|--------------|------------|
53
57
  | Packwerk configuration | packwerk.yml | See [Setting up the configuration file](#Setting-up-the-configuration-file) |
54
58
  | Root package | package.yml | A package for the root folder |
55
- | Custom inflections | config/inflections.yml | A custom inflections file is only required if you have custom inflections in `inflections.rb`, see [Inflections](#Inflections) |
56
59
 
57
60
  After that, you may begin creating packages for your application. See [Defining packages](#Defining-packages)
58
61
 
59
- ## Setting up the configuration file
62
+ ### Setting up Spring
63
+
64
+ [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.
65
+ 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.
66
+ Secondly, to enable Spring, first run `bin/spring binstub packwerk` which will "springify" the generated binstub.
67
+
68
+
69
+ ## Configuring Packwerk
60
70
 
61
71
  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.
62
72
 
@@ -65,7 +75,6 @@ Packwerk reads from the `packwerk.yml` configuration file in the root directory.
65
75
  | include | **/*.{rb,rake,erb} | list of patterns for folder paths to include |
66
76
  | exclude | {bin,node_modules,script,tmp,vendor}/**/* | list of patterns for folder paths to exclude |
67
77
  | package_paths | **/ | a single pattern or a list of patterns to find package configuration files, see: [Defining packages](#Defining-packages) |
68
- | load_paths | All application autoload paths | list of load paths |
69
78
  | custom_associations | N/A | list of custom associations, if any |
70
79
  | parallel | true | when true, fork code parsing out to subprocesses |
71
80
 
@@ -91,50 +100,11 @@ end
91
100
  Packwerk::Parsers::Factory.instance.erb_parser_class = CustomParser
92
101
  ```
93
102
 
94
- ### Inflections
95
-
96
- Packwerk requires custom inflections to be defined in `inflections.yml` instead of the traditional `inflections.rb`. This is because Packwerk accounts for custom inflections, such as acronyms, when resolving constants. Additionally, Packwerk interprets Active Record associations as references to constants. For example, `has_many :birds` is a reference to the `Bird` constant.
97
-
98
- In order to make your custom inflections compatible with Active Support and Packwerk, you must create a `config/inflections.yml` file and point `ActiveSupport::Inflector` to that file.
99
-
100
- In `inflections.rb`, add:
101
-
102
- ```rb
103
- require "packwerk/inflections/custom"
104
-
105
- ActiveSupport::Inflector.inflections do |inflect|
106
- # please add all custom inflections in the file below.
107
- Packwerk::Inflections::Custom.new(
108
- Rails.root.join("config", "inflections.yml")
109
- ).apply_to(inflect)
110
- end
111
- ```
112
-
113
- _Note: Packwerk has to be grouped in production environment within the Gemfile if you have custom inflections._
114
-
115
- Next, move your existing custom inflections into `config/inflections.yml`:
116
-
117
- ```yaml
118
- acronym:
119
- - 'GraphQL'
120
- - 'MRuby'
121
- - 'TOS'
122
- irregular:
123
- - ['analysis', 'analyses']
124
- - ['reserve', 'reserves']
125
- uncountable:
126
- - 'payment_details'
127
- singular:
128
- - [!ruby/regexp /status$/, 'status']
129
- ```
130
-
131
- Any new inflectors should be added to `config/inflections.yml`.
132
-
133
103
  ## Validating the package system
134
104
 
135
105
  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.
136
106
 
137
- 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`.
107
+ 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`.
138
108
 
139
109
  Use the following command to validate the application:
140
110
 
@@ -231,13 +201,17 @@ After enforcing the boundary checks for a package, you may execute:
231
201
 
232
202
  Packwerk will check the entire codebase for any new or stale violations.
233
203
 
234
- You can also specify folders or packages for a shorter run time:
204
+ You can also specify folders for a shorter run time. When checking against folders all subfolders will be analyzed, irrespective of nested package boundaries.
235
205
 
236
206
  packwerk check components/your_package
237
207
 
208
+ 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.
209
+
210
+ packwerk check --packages=components/your_package,components/your_other_package
211
+
238
212
  ![](static/packwerk_check.gif)
239
213
 
240
- In order to keep the package system valid at each version of the application, we recommend running `packwerk check` in your CI pipeline.
214
+ In order to keep the package system valid at each version of the application, we recommend running `bin/packwerk check` in your CI pipeline.
241
215
 
242
216
  See: [TROUBLESHOOT.md - Sample violations](TROUBLESHOOT.md#Sample-violations)
243
217
 
@@ -247,17 +221,17 @@ For existing codebases, packages are likely to have existing boundary violations
247
221
 
248
222
  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:
249
223
 
250
- packwerk update-deprecations
224
+ bin/packwerk update-deprecations
251
225
 
252
- Similar to `packwerk check`, you may also run `packwerk update-deprecations` on folders or packages:
226
+ Similar to `bin/packwerk check`, you may also run `bin/packwerk update-deprecations` on folders or packages:
253
227
 
254
- packwerk update-deprecations components/your_package
228
+ bin/packwerk update-deprecations components/your_package
255
229
 
256
230
  ![](static/packwerk_update.gif)
257
231
 
258
232
  _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._
259
233
 
260
- `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.
234
+ `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.
261
235
 
262
236
  See: [TROUBLESHOOT.md - Troubleshooting violations](TROUBLESHOOT.md#Troubleshooting_violations)
263
237
 
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,6 +4,7 @@
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
@@ -1,30 +1,27 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
- require "active_support/inflector/inflections"
5
4
  require "constant_resolver"
6
5
  require "pathname"
7
6
  require "yaml"
8
7
 
9
8
  module Packwerk
9
+ # Checks the structure of the application and its packwerk configuration to make sure we can run a check and deliver
10
+ # correct results.
10
11
  class ApplicationValidator
11
12
  def initialize(config_file_path:, configuration:, environment:)
12
13
  @config_file_path = config_file_path
13
14
  @configuration = configuration
14
15
  @environment = environment
15
-
16
- @application_load_paths = ApplicationLoadPaths.extract_relevant_paths(configuration.root_path, environment)
17
16
  end
18
17
 
19
18
  Result = Struct.new(:ok?, :error_value)
20
19
 
21
20
  def check_all
22
21
  results = [
23
- check_autoload_path_cache,
24
22
  check_package_manifests_for_privacy,
25
23
  check_package_manifest_syntax,
26
24
  check_application_structure,
27
- check_inflection_file,
28
25
  check_acyclic_graph,
29
26
  check_package_manifest_paths,
30
27
  check_valid_package_dependencies,
@@ -34,21 +31,6 @@ module Packwerk
34
31
  merge_results(results)
35
32
  end
36
33
 
37
- def check_autoload_path_cache
38
- expected = @application_load_paths
39
- actual = @configuration.load_paths
40
- if expected.sort == actual.sort
41
- Result.new(true)
42
- else
43
- Result.new(
44
- false,
45
- "Load path cache in #{@config_file_path} incorrect!\n"\
46
- "Paths missing from file:\n#{format_yaml_strings(expected - actual)}\n"\
47
- "Extraneous load paths in file:\n#{format_yaml_strings(actual - expected)}"
48
- )
49
- end
50
- end
51
-
52
34
  def check_package_manifests_for_privacy
53
35
  privacy_settings = package_manifests_settings_for("enforce_privacy")
54
36
 
@@ -140,39 +122,6 @@ module Packwerk
140
122
  end
141
123
  end
142
124
 
143
- def check_inflection_file
144
- inflections_file = @configuration.inflections_file
145
-
146
- application_inflections = ActiveSupport::Inflector.inflections
147
- packwerk_inflections = Packwerk::Inflector.from_file(inflections_file).inflections
148
-
149
- results = %i(plurals singulars uncountables humans acronyms).map do |type|
150
- expected = application_inflections.public_send(type).to_set
151
- actual = packwerk_inflections.public_send(type).to_set
152
-
153
- if expected == actual
154
- Result.new(true)
155
- else
156
- missing_msg = unless (expected - actual).empty?
157
- "Expected #{type} to be specified in file: #{expected - actual}"
158
- end
159
- extraneous_msg = unless (actual - expected).empty?
160
- "Extraneous #{type} was specified in file: #{actual - expected}"
161
- end
162
- Result.new(
163
- false,
164
- [missing_msg, extraneous_msg].join("\n")
165
- )
166
- end
167
- end
168
-
169
- merge_results(
170
- results,
171
- separator: "\n",
172
- errors_headline: "Inflections specified in #{inflections_file} don't line up with application!\n"
173
- )
174
- end
175
-
176
125
  def check_acyclic_graph
177
126
  edges = package_set.flat_map do |package|
178
127
  package.dependencies.map { |dependency| [package, package_set.fetch(dependency)] }
@@ -296,7 +245,7 @@ module Packwerk
296
245
  end
297
246
 
298
247
  def package_manifests(glob_pattern = package_glob)
299
- PackageSet.package_paths(@configuration.root_path, glob_pattern)
248
+ PackageSet.package_paths(@configuration.root_path, glob_pattern, @configuration.exclude)
300
249
  .map { |f| File.realpath(f) }
301
250
  end
302
251
 
@@ -19,7 +19,7 @@ module Packwerk
19
19
  CustomAssociations
20
20
  )
21
21
 
22
- sig { params(inflector: Inflector, custom_associations: CustomAssociations).void }
22
+ sig { params(inflector: T.class_of(ActiveSupport::Inflector), custom_associations: CustomAssociations).void }
23
23
  def initialize(inflector:, custom_associations: Set.new)
24
24
  @inflector = inflector
25
25
  @associations = T.let(RAILS_ASSOCIATIONS + custom_associations, CustomAssociations)
data/lib/packwerk/cli.rb CHANGED
@@ -1,7 +1,10 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "optparse"
5
+
4
6
  module Packwerk
7
+ # A command-line interface to Packwerk.
5
8
  class Cli
6
9
  extend T::Sig
7
10
 
@@ -50,8 +53,6 @@ module Packwerk
50
53
  output_result(parse_run(args).check)
51
54
  when "detect-stale-violations"
52
55
  output_result(parse_run(args).detect_stale_violations)
53
- when "update"
54
- update(args)
55
56
  when "update-deprecations"
56
57
  output_result(parse_run(args).update_deprecations)
57
58
  when "validate"
@@ -85,19 +86,18 @@ module Packwerk
85
86
 
86
87
  def generate_configs
87
88
  configuration_file = Packwerk::Generators::ConfigurationFile.generate(
88
- load_paths: Packwerk::ApplicationLoadPaths.extract_relevant_paths(@configuration.root_path, @environment),
89
89
  root: @configuration.root_path,
90
90
  out: @out
91
91
  )
92
- inflections_file = Packwerk::Generators::InflectionsFile.generate(root: @configuration.root_path, out: @out)
92
+
93
93
  root_package = Packwerk::Generators::RootPackage.generate(root: @configuration.root_path, out: @out)
94
94
 
95
- success = configuration_file && inflections_file && root_package
95
+ success = configuration_file && root_package
96
96
 
97
97
  result = if success
98
98
  <<~EOS
99
99
 
100
- 🎉 Packwerk is ready to be used. You can start defining packages and run `packwerk check`.
100
+ 🎉 Packwerk is ready to be used. You can start defining packages and run `bin/packwerk check`.
101
101
  For more information on how to use Packwerk, see: https://github.com/Shopify/packwerk/blob/main/USAGE.md
102
102
  EOS
103
103
  else
@@ -112,19 +112,18 @@ module Packwerk
112
112
  success
113
113
  end
114
114
 
115
- def update(paths)
116
- warn("`packwerk update` is deprecated in favor of `packwerk update-deprecations`.")
117
- output_result(parse_run(paths).update_deprecations)
118
- end
119
-
120
115
  def output_result(result)
121
116
  @out.puts
122
117
  @out.puts(result.message)
123
118
  result.status
124
119
  end
125
120
 
126
- def fetch_files_to_process(paths)
127
- files = FilesForProcessing.fetch(paths: paths, configuration: @configuration)
121
+ def fetch_files_to_process(paths, ignore_nested_packages)
122
+ files = FilesForProcessing.fetch(
123
+ paths: paths,
124
+ ignore_nested_packages: ignore_nested_packages,
125
+ configuration: @configuration
126
+ )
128
127
  abort("No files found or given. "\
129
128
  "Specify files or check the include and exclude glob in the config file.") if files.empty?
130
129
  files
@@ -132,11 +131,6 @@ module Packwerk
132
131
 
133
132
  def validate(_paths)
134
133
  @progress_formatter.started_validation do
135
- checker = Packwerk::ApplicationValidator.new(
136
- config_file_path: @configuration.config_path,
137
- configuration: @configuration,
138
- environment: @environment,
139
- )
140
134
  result = checker.check_all
141
135
 
142
136
  list_validation_errors(result)
@@ -145,6 +139,14 @@ module Packwerk
145
139
  end
146
140
  end
147
141
 
142
+ def checker
143
+ Packwerk::ApplicationValidator.new(
144
+ config_file_path: @configuration.config_path,
145
+ configuration: @configuration,
146
+ environment: @environment,
147
+ )
148
+ end
149
+
148
150
  def list_validation_errors(result)
149
151
  @out.puts
150
152
  if result.ok?
@@ -155,9 +157,24 @@ module Packwerk
155
157
  end
156
158
  end
157
159
 
158
- def parse_run(paths)
160
+ def parse_run(params)
161
+ paths = T.let([], T::Array[String])
162
+ ignore_nested_packages = nil
163
+
164
+ if params.any? { |p| p.include?("--packages") }
165
+ OptionParser.new do |parser|
166
+ parser.on("--packages=PACKAGESLIST", Array, "package names, comma separated") do |p|
167
+ paths = p
168
+ end
169
+ end.parse!(params)
170
+ ignore_nested_packages = true
171
+ else
172
+ paths = params
173
+ ignore_nested_packages = false
174
+ end
175
+
159
176
  ParseRun.new(
160
- files: fetch_files_to_process(paths),
177
+ files: fetch_files_to_process(paths, ignore_nested_packages),
161
178
  configuration: @configuration,
162
179
  progress_formatter: @progress_formatter,
163
180
  offenses_formatter: @offenses_formatter
@@ -34,24 +34,54 @@ module Packwerk
34
34
  DEFAULT_EXCLUDE_GLOBS = ["{bin,node_modules,script,tmp,vendor}/**/*"]
35
35
 
36
36
  attr_reader(
37
- :include, :exclude, :root_path, :package_paths, :custom_associations, :load_paths, :inflections_file,
38
- :config_path,
37
+ :include, :exclude, :root_path, :package_paths, :custom_associations, :config_path
39
38
  )
40
39
 
41
40
  def initialize(configs = {}, config_path: nil)
41
+ if configs["load_paths"]
42
+ warning = <<~WARNING
43
+ DEPRECATION WARNING: The 'load_paths' key in `packwerk.yml` is deprecated.
44
+ This value is no longer cached, and you can remove the key from `packwerk.yml`.
45
+ WARNING
46
+
47
+ warn(warning)
48
+ end
49
+
50
+ inflection_file = File.expand_path(configs["inflections_file"] || "config/inflections.yml", @root_path)
51
+ if configs["inflections_file"]
52
+ warning = <<~WARNING
53
+ DEPRECATION WARNING: The 'inflections_file' key in `packwerk.yml` is deprecated.
54
+ This value is no longer cached, and you can remove the key from `packwerk.yml`.
55
+ You can also delete #{configs["inflections_file"]}.
56
+ WARNING
57
+
58
+ warn(warning)
59
+ end
60
+
61
+ if Pathname.new(inflection_file).exist?
62
+ warning = <<~WARNING
63
+ DEPRECATION WARNING: Inflections YMLs in packwerk are now deprecated.
64
+ This value is no longer cached, and you can now delete #{inflection_file}
65
+ WARNING
66
+
67
+ warn(warning)
68
+ end
69
+
42
70
  @include = configs["include"] || DEFAULT_INCLUDE_GLOBS
43
71
  @exclude = configs["exclude"] || DEFAULT_EXCLUDE_GLOBS
44
72
  root = config_path ? File.dirname(config_path) : "."
45
73
  @root_path = File.expand_path(root)
46
74
  @package_paths = configs["package_paths"] || "**/"
47
75
  @custom_associations = configs["custom_associations"] || []
48
- @load_paths = configs["load_paths"] || []
49
- @inflections_file = File.expand_path(configs["inflections_file"] || "config/inflections.yml", @root_path)
50
76
  @parallel = configs.key?("parallel") ? configs["parallel"] : true
51
77
 
52
78
  @config_path = config_path
53
79
  end
54
80
 
81
+ def load_paths
82
+ @load_paths ||= ApplicationLoadPaths.extract_relevant_paths(@root_path, "test")
83
+ end
84
+
55
85
  def parallel?
56
86
  @parallel
57
87
  end
@@ -15,6 +15,9 @@ module Packwerk
15
15
  def constant_name_from_node(node, ancestors:)
16
16
  return nil unless Node.constant?(node)
17
17
  parent = ancestors.first
18
+
19
+ # Only process the root `const` node for namespaced constant references. For example, in the
20
+ # reference `Spam::Eggs::Thing`, we only process the const node associated with `Spam`.
18
21
  return nil unless root_constant?(parent)
19
22
 
20
23
  if parent && constant_in_module_or_class_definition?(node, parent: parent)
@@ -30,8 +33,6 @@ module Packwerk
30
33
 
31
34
  private
32
35
 
33
- # Only process the root `const` node for namespaced constant references. For example, in the
34
- # reference `Spam::Eggs::Thing`, we only process the const node associated with `Spam`.
35
36
  sig { params(parent: T.nilable(AST::Node)).returns(T::Boolean) }
36
37
  def root_constant?(parent)
37
38
  !(parent && Node.constant?(parent))
@@ -45,7 +45,7 @@ module Packwerk
45
45
  begin
46
46
  constant = @resolver.resolve(const_name, current_namespace_path: current_namespace_path)
47
47
  rescue ConstantResolver::Error => e
48
- raise(ConstantResolver::Error, e.message + "\n Make sure autoload paths are added to the config file.")
48
+ raise(ConstantResolver::Error, e.message)
49
49
  end
50
50
 
51
51
  return unless constant