packwerk 3.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +36 -8
  3. data/.ruby-version +1 -1
  4. data/Gemfile.lock +11 -6
  5. data/README.md +4 -2
  6. data/Rakefile +10 -1
  7. data/USAGE.md +6 -0
  8. data/dev.yml +1 -1
  9. data/lib/packwerk/application_validator.rb +3 -0
  10. data/lib/packwerk/checker.rb +13 -4
  11. data/lib/packwerk/cli.rb +14 -177
  12. data/lib/packwerk/commands/base_command.rb +69 -0
  13. data/lib/packwerk/commands/check_command.rb +60 -0
  14. data/lib/packwerk/commands/help_command.rb +33 -0
  15. data/lib/packwerk/commands/init_command.rb +42 -0
  16. data/lib/packwerk/commands/lazy_loaded_entry.rb +37 -0
  17. data/lib/packwerk/commands/update_todo_command.rb +60 -0
  18. data/lib/packwerk/commands/uses_parse_run.rb +92 -0
  19. data/lib/packwerk/commands/validate_command.rb +46 -0
  20. data/lib/packwerk/commands/version_command.rb +18 -0
  21. data/lib/packwerk/commands.rb +54 -0
  22. data/lib/packwerk/configuration.rb +4 -1
  23. data/lib/packwerk/file_processor.rb +12 -1
  24. data/lib/packwerk/formatters/default_offenses_formatter.rb +3 -3
  25. data/lib/packwerk/formatters/progress_formatter.rb +11 -0
  26. data/lib/packwerk/generators/templates/packwerk.yml.erb +1 -1
  27. data/lib/packwerk/offense_collection.rb +32 -12
  28. data/lib/packwerk/offenses_formatter.rb +13 -4
  29. data/lib/packwerk/package_todo.rb +87 -60
  30. data/lib/packwerk/parse_run.rb +42 -82
  31. data/lib/packwerk/validator.rb +18 -4
  32. data/lib/packwerk/version.rb +1 -1
  33. data/lib/packwerk.rb +4 -28
  34. data/sorbet/rbi/gems/parser@3.2.2.0.rbi +7250 -0
  35. metadata +14 -5
  36. data/lib/packwerk/cli/result.rb +0 -11
  37. 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: 9af43a5e2d624ca5d6b38bfa3c9e65d1d12b1b9cc148a153563ebd55d3b6c82a
4
- data.tar.gz: 6dee0dac16640632fb272c72ebe024a36c6a85ba23748b2ab5886279cccd863c
3
+ metadata.gz: d72fbde4259baa5a5e7f8bc2ca4406d9bc6a835051efafefc23ef6ef3ae13f32
4
+ data.tar.gz: 65bff304e2be348e16800b0d4f65ab33a1b5fb84ab15ba495e03904d099be4a0
5
5
  SHA512:
6
- metadata.gz: 197f933a60a46aba470ee66a2c04bbabcf1ff3c3386665551dbb7b8241b9357d73e7576de7edd7c322249b8d45cfc0b282616e0f9a5a665d4560964632a66806
7
- data.tar.gz: ec903a206f47df277d6187afc5c17a9cfa1591446afb249f2ba62171f7c56ba8687ce403041db451eb8ea0dba842feb26cc7c32e9ff600b93e60e49eafb4584f
6
+ metadata.gz: e25488e4af51932d503d4120e458fe92bc4ae2945ca8009219e01f4f2e7756e0430a0d9a6176f01d864d55644f262a06fb88494234b43aae43ba254437d7fb49
7
+ data.tar.gz: 2c821f3e11a16c52d84f7f4e68f01e788e95d5723fd77305f69e15aba07f5f1f81ea43e9c60b6c00ca4503df036eb2b3ea0e0ae1378f0c584d7252614064f9d9
@@ -1,6 +1,11 @@
1
1
  name: CI
2
2
 
3
- on: [push, pull_request]
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
1
+ 3.2.2
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- packwerk (3.0.1)
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.0)
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.13.8)
70
- mini_portile2 (~> 2.8.0)
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.1.2.1)
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.6.0)
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
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/Gusto/packwerk-vscode integrates packwerk into Visual Studio Code so you can see violations right in your editor
62
- - https://github.com/Gusto/stimpack sets up Rails autoloading, as well as `rspec` and `FactoryBot` integration, for packages arranged in a flat list. Stimpack is quite convenient, but for autoloading we recommend to use `Rails::Engine`s instead.
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/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/USAGE.md CHANGED
@@ -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.
data/dev.yml CHANGED
@@ -3,7 +3,7 @@ name: packwerk
3
3
  type: ruby
4
4
 
5
5
  up:
6
- - ruby: 3.2.1
6
+ - ruby: 3.2.2
7
7
  - bundler
8
8
 
9
9
  commands:
@@ -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)
@@ -13,14 +13,13 @@ module Packwerk
13
13
 
14
14
  sig { params(base: Class).void }
15
15
  def included(base)
16
- @checkers ||= T.let(@checkers, T.nilable(T::Array[Class]))
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
- T.unsafe(@checkers).map(&:new)
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
- subcommand = args.shift
50
- case subcommand
51
- when "init"
52
- init
53
- when "check"
54
- output_result(parse_run(args).check)
55
- when "update-todo", "update"
56
- output_result(parse_run(args).update_todo)
57
- when "validate"
58
- validate(args)
59
- when "version"
60
- @out.puts(Packwerk::VERSION)
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
- @out.puts("Validation failed ")
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
- if args.any? { |arg| arg.include?("--packages") }
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
@@ -0,0 +1,60 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Packwerk
5
+ module Commands
6
+ class CheckCommand < BaseCommand
7
+ extend T::Sig
8
+ include UsesParseRun
9
+
10
+ description "run all checks"
11
+
12
+ sig { override.returns(T::Boolean) }
13
+ def run
14
+ if @files_for_processing.files.empty?
15
+ out.puts(<<~MSG.squish)
16
+ No files found or given.
17
+ Specify files or check the include and exclude glob in the config file.
18
+ MSG
19
+
20
+ true
21
+ end
22
+
23
+ all_offenses = T.let([], T::Array[Offense])
24
+ on_interrupt = T.let(-> { progress_formatter.interrupted }, T.proc.void)
25
+
26
+ progress_formatter.started_inspection(@files_for_processing.files) do
27
+ all_offenses = parse_run.find_offenses(run_context, on_interrupt: on_interrupt) do |offenses|
28
+ failed = offenses.any? { |offense| !offense_collection.listed?(offense) }
29
+ progress_formatter.increment_progress(failed)
30
+ end
31
+ end
32
+ offense_collection.add_offenses(all_offenses)
33
+
34
+ messages = [
35
+ offenses_formatter.show_offenses(offense_collection.outstanding_offenses),
36
+ offenses_formatter.show_stale_violations(offense_collection, @files_for_processing.files),
37
+ offenses_formatter.show_strict_mode_violations(offense_collection.strict_mode_violations),
38
+ ]
39
+
40
+ out.puts(messages.select(&:present?).join("\n") + "\n")
41
+
42
+ offense_collection.outstanding_offenses.empty? &&
43
+ !offense_collection.stale_violations?(@files_for_processing.files) &&
44
+ offense_collection.strict_mode_violations.empty?
45
+ end
46
+
47
+ private
48
+
49
+ sig { returns(RunContext) }
50
+ def run_context
51
+ @run_context ||= T.let(RunContext.from_configuration(configuration), T.nilable(RunContext))
52
+ end
53
+
54
+ sig { returns(OffenseCollection) }
55
+ def offense_collection
56
+ @offense_collection ||= T.let(OffenseCollection.new(configuration.root_path), T.nilable(OffenseCollection))
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,33 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Packwerk
5
+ module Commands
6
+ class HelpCommand < BaseCommand
7
+ extend T::Sig
8
+
9
+ description "display help information about packwerk"
10
+
11
+ sig { override.returns(T::Boolean) }
12
+ def run
13
+ err_out.puts(<<~USAGE)
14
+ Usage: #{$PROGRAM_NAME} <subcommand>
15
+
16
+ Subcommands:
17
+ #{command_help_lines}
18
+ USAGE
19
+
20
+ true
21
+ end
22
+
23
+ private
24
+
25
+ sig { returns(String) }
26
+ def command_help_lines
27
+ Commands.all.map do |command|
28
+ " #{command.name} - #{command.description}"
29
+ end.join("\n")
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,42 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Packwerk
5
+ module Commands
6
+ class InitCommand < BaseCommand
7
+ extend T::Sig
8
+
9
+ description "set up packwerk"
10
+
11
+ sig { override.returns(T::Boolean) }
12
+ def run
13
+ out.puts("📦 Initializing Packwerk...")
14
+
15
+ configuration_file = Generators::ConfigurationFile.generate(
16
+ root: configuration.root_path,
17
+ out: out
18
+ )
19
+
20
+ root_package = Generators::RootPackage.generate(root: configuration.root_path, out: out)
21
+
22
+ success = configuration_file && root_package
23
+
24
+ if success
25
+ out.puts(<<~EOS)
26
+
27
+ 🎉 Packwerk is ready to be used. You can start defining packages and run `bin/packwerk check`.
28
+ For more information on how to use Packwerk, see: https://github.com/Shopify/packwerk/blob/main/USAGE.md
29
+ EOS
30
+ else
31
+ out.puts(<<~EOS)
32
+
33
+ ⚠️ Packwerk is not ready to be used.
34
+ Please check output and refer to https://github.com/Shopify/packwerk/blob/main/USAGE.md for more information.
35
+ EOS
36
+ end
37
+
38
+ success
39
+ end
40
+ end
41
+ end
42
+ end