packwerk 3.0.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
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