packwerk 1.0.2 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b54c31e7c1902a03feb01304c85c9f2363b769fdb5f3c3cea7f38c67ff77af3
4
- data.tar.gz: efefa11bf554021227ce63a876c891d5466ee7bcd173e319deb086c8ec9732a5
3
+ metadata.gz: 91ec92f15d9d87fa8c54861afd424fe6149ef8f8e05f114b6ee04559fa6053fc
4
+ data.tar.gz: b83cc17ff9cd7d2d37db4456017d429b4a8f8e45766cdfa4551ace2e32c58183
5
5
  SHA512:
6
- metadata.gz: 5765e07827c5c5fc6d297f916e80c6cd2a6e09984ba91a1f2186ce1209d7965c7d906b6ff06a6b50fd37c299cdb9f82188d57273faf6e0fe7c654983ff8c0e8d
7
- data.tar.gz: b44c1ba5e1c5b61dd2d3e4755b5c215f245cb412a05f272513a6eba83e561097d6f6e624d65f5e8fa7021479616433f0e37ca9b818f00363efd11f24f8b6bca6
6
+ metadata.gz: 596f4f1a6b026e96ab6299e9ba7ceb63b7d709bd49c0629390f50e62e751174e4a92557243bef3a8f7f36aaf416e54fbf1fdb9f368fc2e478eb033e208358dc6
7
+ data.tar.gz: fdca4107315ea6140d7b92e5cd7b53216a73f65fac4c1c3147c798a1d407f75d3898b31517bec6bf2452e7ca0a3d1ad87599142250cf75684ccc9a48436bcd91
@@ -85,7 +85,7 @@ GIT
85
85
  PATH
86
86
  remote: .
87
87
  specs:
88
- packwerk (1.0.2)
88
+ packwerk (1.1.0)
89
89
  activesupport (>= 5.2)
90
90
  ast
91
91
  better_html
data/USAGE.md CHANGED
@@ -63,10 +63,31 @@ Packwerk reads from the `packwerk.yml` configuration file in the root directory.
63
63
  |----------------------|-------------------------------------------|--------------|
64
64
  | include | **/*.{rb,rake,erb} | list of patterns for folder paths to include |
65
65
  | exclude | {bin,node_modules,script,tmp,vendor}/**/* | list of patterns for folder paths to exclude |
66
- | package_paths | **/ | patterns to find package configuration files, see: Defining packages |
66
+ | package_paths | **/ | a single pattern or a list of patterns to find package configuration files, see: [Defining packages](#Defining-packages) |
67
67
  | load_paths | All application autoload paths | list of load paths |
68
68
  | custom_associations | N/A | list of custom associations, if any |
69
69
 
70
+ ### Using a custom ERB parser
71
+
72
+ You can specify a custom ERB parser if needed. For example, if you're using `<%graphql>` tags from https://github.com/github/graphql-client in your ERBs, you can use a custom parser subclass to comment them out so that Packwerk can parse the rest of the file:
73
+
74
+ ```ruby
75
+ class CustomParser < Packwerk::Parsers::Erb
76
+ def parse_buffer(buffer, file_path:)
77
+ preprocessed_source = buffer.source
78
+
79
+ # Comment out <%graphql ... %> tags. They won't contain any object
80
+ # references anyways.
81
+ preprocessed_source = preprocessed_source.gsub(/<%graphql/, "<%#")
82
+
83
+ preprocessed_buffer = Parser::Source::Buffer.new(file_path)
84
+ preprocessed_buffer.source = preprocessed_source
85
+ super(preprocessed_buffer, file_path: file_path)
86
+ end
87
+ end
88
+
89
+ Packwerk::Parsers::Factory.instance.erb_parser_class = CustomParser
90
+ ```
70
91
 
71
92
  ### Inflections
72
93
 
@@ -32,7 +32,7 @@ module Packwerk
32
32
  check_acyclic_graph,
33
33
  check_package_manifest_paths,
34
34
  check_valid_package_dependencies,
35
- check_root_package_exist,
35
+ check_root_package_exists,
36
36
  ]
37
37
 
38
38
  results.reject!(&:ok?)
@@ -207,7 +207,7 @@ module Packwerk
207
207
  end
208
208
 
209
209
  def check_acyclic_graph
210
- packages = Packwerk::PackageSet.load_all_from(".")
210
+ packages = Packwerk::PackageSet.load_all_from(@configuration.root_path)
211
211
 
212
212
  edges = packages.flat_map do |package|
213
213
  package.dependencies.map { |dependency| [package, packages.fetch(dependency)] }
@@ -297,7 +297,7 @@ module Packwerk
297
297
  end
298
298
  end
299
299
 
300
- def check_root_package_exist
300
+ def check_root_package_exists
301
301
  root_package_path = File.join(@configuration.root_path, "package.yml")
302
302
  all_packages_manifests = package_manifests(package_glob)
303
303
 
@@ -0,0 +1,47 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ require "packwerk/deprecated_references"
7
+ require "packwerk/reference"
8
+ require "packwerk/reference_lister"
9
+ require "packwerk/violation_type"
10
+
11
+ module Packwerk
12
+ class CacheDeprecatedReferences
13
+ extend T::Sig
14
+ extend T::Helpers
15
+ include ReferenceLister
16
+ abstract!
17
+
18
+ def initialize(root_path, deprecated_references = {})
19
+ @root_path = root_path
20
+ @deprecated_references = T.let(deprecated_references, T::Hash[String, Packwerk::DeprecatedReferences])
21
+ end
22
+
23
+ sig do
24
+ params(reference: Packwerk::Reference, violation_type: ViolationType)
25
+ .returns(T::Boolean)
26
+ .override
27
+ end
28
+ def listed?(reference, violation_type:)
29
+ deprecated_references = deprecated_references_for(reference.source_package)
30
+ deprecated_references.add_entries(reference, violation_type.serialize)
31
+ true
32
+ end
33
+
34
+ private
35
+
36
+ def deprecated_references_for(package)
37
+ @deprecated_references[package] ||= Packwerk::DeprecatedReferences.new(
38
+ package,
39
+ deprecated_references_file_for(package),
40
+ )
41
+ end
42
+
43
+ def deprecated_references_file_for(package)
44
+ File.join(@root_path, package.name, "deprecated_references.yml")
45
+ end
46
+ end
47
+ end
@@ -12,10 +12,13 @@ require "packwerk/output_styles"
12
12
  require "packwerk/run_context"
13
13
  require "packwerk/updating_deprecated_references"
14
14
  require "packwerk/checking_deprecated_references"
15
+ require "packwerk/commands/detect_stale_violations_command"
16
+ require "packwerk/commands/offense_progress_marker"
15
17
 
16
18
  module Packwerk
17
19
  class Cli
18
20
  extend T::Sig
21
+ include OffenseProgressMarker
19
22
 
20
23
  def initialize(run_context: nil, configuration: nil, out: $stdout, err_out: $stderr, style: OutputStyles::Plain)
21
24
  @out = out
@@ -45,6 +48,8 @@ module Packwerk
45
48
  generate_configs
46
49
  when "check"
47
50
  check(args)
51
+ when "detect-stale-violations"
52
+ detect_stale_violations(args)
48
53
  when "update"
49
54
  update(args)
50
55
  when "update-deprecations"
@@ -142,7 +147,9 @@ module Packwerk
142
147
  all_offenses = T.let([], T.untyped)
143
148
  execution_time = Benchmark.realtime do
144
149
  all_offenses = files.flat_map do |path|
145
- @run_context.process_file(file: path).tap { |offenses| mark_progress(offenses) }
150
+ @run_context.process_file(file: path).tap do |offenses|
151
+ mark_progress(offenses: offenses, progress_formatter: @progress_formatter)
152
+ end
146
153
  end
147
154
 
148
155
  updating_deprecated_references.dump_deprecated_references_files
@@ -165,7 +172,7 @@ module Packwerk
165
172
  execution_time = Benchmark.realtime do
166
173
  files.each do |path|
167
174
  @run_context.process_file(file: path).tap do |offenses|
168
- mark_progress(offenses)
175
+ mark_progress(offenses: offenses, progress_formatter: @progress_formatter)
169
176
  all_offenses.concat(offenses)
170
177
  end
171
178
  end
@@ -181,6 +188,18 @@ module Packwerk
181
188
  all_offenses.empty?
182
189
  end
183
190
 
191
+ def detect_stale_violations(paths)
192
+ detect_stale_violations = DetectStaleViolationsCommand.new(
193
+ files: fetch_files_to_process(paths),
194
+ configuration: @configuration,
195
+ progress_formatter: @progress_formatter
196
+ )
197
+ result = detect_stale_violations.run
198
+ @out.puts
199
+ @out.puts(result.message)
200
+ result.status
201
+ end
202
+
184
203
  def fetch_files_to_process(paths)
185
204
  files = FilesForProcessing.fetch(paths: paths, configuration: @configuration)
186
205
  abort("No files found or given. "\
@@ -188,14 +207,6 @@ module Packwerk
188
207
  files
189
208
  end
190
209
 
191
- def mark_progress(offenses)
192
- if offenses.empty?
193
- @progress_formatter.mark_as_inspected
194
- else
195
- @progress_formatter.mark_as_failed
196
- end
197
- end
198
-
199
210
  def validate(_paths)
200
211
  warn("`packwerk validate` should be run within the application. "\
201
212
  "Generate the bin script using `packwerk init` and"\
@@ -0,0 +1,63 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+ require "packwerk/cli"
4
+ require "sorbet-runtime"
5
+ require "benchmark"
6
+ require "packwerk/configuration"
7
+ require "packwerk/formatters/progress_formatter"
8
+ require "packwerk/run_context"
9
+ require "packwerk/detect_stale_deprecated_references"
10
+ require "packwerk/commands/offense_progress_marker"
11
+
12
+ module Packwerk
13
+ class DetectStaleViolationsCommand
14
+ extend T::Sig
15
+ include OffenseProgressMarker
16
+ Result = Struct.new(:message, :status)
17
+
18
+ def initialize(files:, configuration:, run_context: nil, progress_formatter: nil, reference_lister: nil)
19
+ @configuration = configuration
20
+ @run_context = run_context
21
+ @reference_lister = reference_lister
22
+ @progress_formatter = progress_formatter
23
+ @files = files
24
+ end
25
+
26
+ sig { returns(Result) }
27
+ def run
28
+ @progress_formatter.started(@files)
29
+
30
+ all_offenses = T.let([], T.untyped)
31
+ execution_time = Benchmark.realtime do
32
+ all_offenses = @files.flat_map do |path|
33
+ run_context.process_file(file: path).tap do |offenses|
34
+ mark_progress(offenses: offenses, progress_formatter: @progress_formatter)
35
+ end
36
+ end
37
+ end
38
+
39
+ @progress_formatter.finished(execution_time)
40
+ calculate_result
41
+ end
42
+
43
+ private
44
+
45
+ def run_context
46
+ @run_context ||= Packwerk::RunContext.from_configuration(@configuration, reference_lister: reference_lister)
47
+ end
48
+
49
+ def reference_lister
50
+ @reference_lister ||= ::Packwerk::DetectStaleDeprecatedReferences.new(@configuration.root_path)
51
+ end
52
+
53
+ sig { returns Result }
54
+ def calculate_result
55
+ result_status = !reference_lister.stale_violations?
56
+ message = "There were stale violations found, please run `packwerk update`"
57
+ if result_status
58
+ message = "No stale violations detected"
59
+ end
60
+ Result.new(message, result_status)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+ require "sorbet-runtime"
4
+ require "packwerk/formatters/progress_formatter"
5
+
6
+ module Packwerk
7
+ module OffenseProgressMarker
8
+ extend T::Sig
9
+
10
+ sig do
11
+ params(
12
+ offenses: T::Array[T.nilable(::Packwerk::Offense)],
13
+ progress_formatter: ::Packwerk::Formatters::ProgressFormatter
14
+ ).void
15
+ end
16
+ def mark_progress(offenses:, progress_formatter:)
17
+ if offenses.empty?
18
+ progress_formatter.mark_as_inspected
19
+ else
20
+ progress_formatter.mark_as_failed
21
+ end
22
+ end
23
+ end
24
+ end
@@ -3,6 +3,7 @@
3
3
 
4
4
  require "sorbet-runtime"
5
5
  require "yaml"
6
+ require "sorbet-runtime"
6
7
 
7
8
  require "packwerk/reference"
8
9
  require "packwerk/reference_lister"
@@ -47,6 +48,24 @@ module Packwerk
47
48
  @new_entries[reference.constant.package.name] = package_violations
48
49
  end
49
50
 
51
+ sig { returns(T::Boolean) }
52
+ def stale_violations?
53
+ prepare_entries_for_dump
54
+ deprecated_references.any? do |package, package_violations|
55
+ package_violations.any? do |constant_name, entries_for_file|
56
+ new_entries_violation_types = @new_entries.dig(package, constant_name, "violations")
57
+ return true if new_entries_violation_types.nil?
58
+ if entries_for_file["violations"].all? { |type| new_entries_violation_types.include?(type) }
59
+ stale_violations =
60
+ entries_for_file["files"] - Array(@new_entries.dig(package, constant_name, "files"))
61
+ stale_violations.present?
62
+ else
63
+ return true
64
+ end
65
+ end
66
+ end
67
+ end
68
+
50
69
  def dump
51
70
  if @new_entries.empty?
52
71
  File.delete(@filepath) if File.exist?(@filepath)
@@ -0,0 +1,14 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "packwerk/cache_deprecated_references"
5
+
6
+ module Packwerk
7
+ class DetectStaleDeprecatedReferences < CacheDeprecatedReferences
8
+ extend T::Sig
9
+ sig { returns(T::Boolean) }
10
+ def stale_violations?
11
+ @deprecated_references.values.any?(&:stale_violations?)
12
+ end
13
+ end
14
+ end
@@ -28,8 +28,12 @@ module Packwerk
28
28
  def package_paths(root_path, package_pathspec)
29
29
  bundle_path_match = Bundler.bundle_path.join("**").to_s
30
30
 
31
- Dir.glob(File.join(root_path, package_pathspec, PACKAGE_CONFIG_FILENAME))
32
- .map { |path| Pathname.new(path) }
31
+ glob_patterns = Array(package_pathspec).map do |pathspec|
32
+ File.join(root_path, pathspec, PACKAGE_CONFIG_FILENAME)
33
+ end
34
+
35
+ Dir.glob(glob_patterns)
36
+ .map { |path| Pathname.new(path).cleanpath }
33
37
  .reject { |path| path.realpath.fnmatch(bundle_path_match) }
34
38
  end
35
39
 
@@ -19,6 +19,10 @@ module Packwerk
19
19
  def call(io:, file_path: "<unknown>")
20
20
  buffer = Parser::Source::Buffer.new(file_path)
21
21
  buffer.source = io.read
22
+ parse_buffer(buffer, file_path: file_path)
23
+ end
24
+
25
+ def parse_buffer(buffer, file_path:)
22
26
  parser = @parser_class.new(buffer, template_language: :html)
23
27
  to_ruby_ast(parser.ast, file_path)
24
28
  rescue EncodingError => e
@@ -26,9 +26,18 @@ module Packwerk
26
26
  when RUBY_REGEX
27
27
  @ruby_parser ||= Ruby.new
28
28
  when ERB_REGEX
29
- @erb_parser ||= Erb.new
29
+ @erb_parser ||= erb_parser_class.new
30
30
  end
31
31
  end
32
+
33
+ def erb_parser_class
34
+ @erb_parser_class || Erb
35
+ end
36
+
37
+ def erb_parser_class=(klass)
38
+ @erb_parser_class = klass
39
+ @erb_parser = nil
40
+ end
32
41
  end
33
42
  end
34
43
  end
@@ -25,9 +25,10 @@ module Packwerk
25
25
  :inflector,
26
26
  :custom_associations,
27
27
  :checker_classes,
28
- :reference_lister,
29
28
  )
30
29
 
30
+ attr_accessor :reference_lister
31
+
31
32
  DEFAULT_CHECKERS = [
32
33
  ::Packwerk::DependencyChecker,
33
34
  ::Packwerk::PrivacyChecker,
@@ -1,51 +1,14 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
- require "sorbet-runtime"
5
-
6
- require "packwerk/deprecated_references"
7
- require "packwerk/reference"
8
- require "packwerk/reference_lister"
9
- require "packwerk/violation_type"
4
+ require "packwerk/cache_deprecated_references"
10
5
 
11
6
  module Packwerk
12
- class UpdatingDeprecatedReferences
13
- extend T::Sig
14
- include ReferenceLister
15
-
16
- def initialize(root_path, deprecated_references = {})
17
- @root_path = root_path
18
- @deprecated_references = deprecated_references
19
- end
20
-
21
- sig do
22
- params(reference: Packwerk::Reference, violation_type: ViolationType)
23
- .returns(T::Boolean)
24
- .override
25
- end
26
- def listed?(reference, violation_type:)
27
- deprecated_references = deprecated_references_for(reference.source_package)
28
- deprecated_references.add_entries(reference, violation_type.serialize)
29
- true
30
- end
31
-
7
+ class UpdatingDeprecatedReferences < CacheDeprecatedReferences
32
8
  def dump_deprecated_references_files
33
9
  @deprecated_references.each do |_, deprecated_references_file|
34
10
  deprecated_references_file.dump
35
11
  end
36
12
  end
37
-
38
- private
39
-
40
- def deprecated_references_for(package)
41
- @deprecated_references[package] ||= Packwerk::DeprecatedReferences.new(
42
- package,
43
- deprecated_references_file_for(package),
44
- )
45
- end
46
-
47
- def deprecated_references_file_for(package)
48
- File.join(@root_path, package.name, "deprecated_references.yml")
49
- end
50
13
  end
51
14
  end
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Packwerk
5
- VERSION = "1.0.2"
5
+ VERSION = "1.1.0"
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: packwerk
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-16 00:00:00.000000000 Z
11
+ date: 2020-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -186,15 +186,19 @@ files:
186
186
  - lib/packwerk/application_load_paths.rb
187
187
  - lib/packwerk/application_validator.rb
188
188
  - lib/packwerk/association_inspector.rb
189
+ - lib/packwerk/cache_deprecated_references.rb
189
190
  - lib/packwerk/checker.rb
190
191
  - lib/packwerk/checking_deprecated_references.rb
191
192
  - lib/packwerk/cli.rb
193
+ - lib/packwerk/commands/detect_stale_violations_command.rb
194
+ - lib/packwerk/commands/offense_progress_marker.rb
192
195
  - lib/packwerk/configuration.rb
193
196
  - lib/packwerk/const_node_inspector.rb
194
197
  - lib/packwerk/constant_discovery.rb
195
198
  - lib/packwerk/constant_name_inspector.rb
196
199
  - lib/packwerk/dependency_checker.rb
197
200
  - lib/packwerk/deprecated_references.rb
201
+ - lib/packwerk/detect_stale_deprecated_references.rb
198
202
  - lib/packwerk/file_processor.rb
199
203
  - lib/packwerk/files_for_processing.rb
200
204
  - lib/packwerk/formatters/progress_formatter.rb