packwerk 1.1.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +6 -5
  3. data/USAGE.md +17 -16
  4. data/lib/packwerk.rb +72 -36
  5. data/lib/packwerk/application_validator.rb +0 -5
  6. data/lib/packwerk/association_inspector.rb +0 -3
  7. data/lib/packwerk/checker.rb +2 -8
  8. data/lib/packwerk/cli.rb +22 -77
  9. data/lib/packwerk/configuration.rb +5 -0
  10. data/lib/packwerk/const_node_inspector.rb +0 -2
  11. data/lib/packwerk/constant_discovery.rb +2 -0
  12. data/lib/packwerk/constant_name_inspector.rb +0 -1
  13. data/lib/packwerk/dependency_checker.rb +2 -15
  14. data/lib/packwerk/deprecated_references.rb +3 -9
  15. data/lib/packwerk/file_processor.rb +0 -4
  16. data/lib/packwerk/formatters/offenses_formatter.rb +3 -8
  17. data/lib/packwerk/formatters/progress_formatter.rb +5 -4
  18. data/lib/packwerk/generators/configuration_file.rb +0 -1
  19. data/lib/packwerk/inflector.rb +0 -2
  20. data/lib/packwerk/node.rb +1 -0
  21. data/lib/packwerk/node_processor.rb +14 -32
  22. data/lib/packwerk/node_processor_factory.rb +0 -5
  23. data/lib/packwerk/node_visitor.rb +1 -4
  24. data/lib/packwerk/offense.rb +0 -4
  25. data/lib/packwerk/offense_collection.rb +84 -0
  26. data/lib/packwerk/offenses_formatter.rb +15 -0
  27. data/lib/packwerk/package.rb +8 -0
  28. data/lib/packwerk/package_set.rb +0 -2
  29. data/lib/packwerk/parse_run.rb +104 -0
  30. data/lib/packwerk/parsed_constant_definitions.rb +0 -2
  31. data/lib/packwerk/parsers.rb +0 -2
  32. data/lib/packwerk/parsers/erb.rb +0 -2
  33. data/lib/packwerk/parsers/factory.rb +0 -2
  34. data/lib/packwerk/privacy_checker.rb +2 -15
  35. data/lib/packwerk/reference_extractor.rb +0 -8
  36. data/lib/packwerk/reference_offense.rb +49 -0
  37. data/lib/packwerk/result.rb +9 -0
  38. data/lib/packwerk/run_context.rb +4 -21
  39. data/lib/packwerk/sanity_checker.rb +0 -2
  40. data/lib/packwerk/version.rb +1 -1
  41. data/lib/packwerk/violation_type.rb +0 -2
  42. data/packwerk.gemspec +1 -0
  43. data/service.yml +1 -4
  44. data/sorbet/rbi/gems/parallel@1.20.1.rbi +111 -2
  45. data/sorbet/tapioca/require.rb +1 -0
  46. metadata +22 -12
  47. data/lib/packwerk/cache_deprecated_references.rb +0 -55
  48. data/lib/packwerk/checking_deprecated_references.rb +0 -43
  49. data/lib/packwerk/commands/detect_stale_violations_command.rb +0 -60
  50. data/lib/packwerk/commands/offense_progress_marker.rb +0 -24
  51. data/lib/packwerk/commands/result.rb +0 -13
  52. data/lib/packwerk/commands/update_deprecations_command.rb +0 -81
  53. data/lib/packwerk/detect_stale_deprecated_references.rb +0 -14
  54. data/lib/packwerk/reference_lister.rb +0 -23
  55. data/lib/packwerk/updating_deprecated_references.rb +0 -14
@@ -2,7 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "ast"
5
- require "sorbet-runtime"
6
5
 
7
6
  module Packwerk
8
7
  # An interface describing some object that can extract a constant name from an AST node
@@ -1,9 +1,6 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "packwerk/violation_type"
5
- require "packwerk/checker"
6
-
7
4
  module Packwerk
8
5
  class DependencyChecker
9
6
  extend T::Sig
@@ -16,24 +13,14 @@ module Packwerk
16
13
 
17
14
  sig do
18
15
  override
19
- .params(reference: Packwerk::Reference, reference_lister: Packwerk::ReferenceLister)
16
+ .params(reference: Packwerk::Reference)
20
17
  .returns(T::Boolean)
21
18
  end
22
- def invalid_reference?(reference, reference_lister)
19
+ def invalid_reference?(reference)
23
20
  return false unless reference.source_package
24
21
  return false unless reference.source_package.enforce_dependencies?
25
22
  return false if reference.source_package.dependency?(reference.constant.package)
26
- return false if reference_lister.listed?(reference, violation_type: violation_type)
27
23
  true
28
24
  end
29
-
30
- sig { override.params(reference: Packwerk::Reference).returns(String) }
31
- def message_for(reference)
32
- "Dependency violation: #{reference.constant.name} belongs to '#{reference.constant.package}', but " \
33
- "'#{reference.source_package}' does not specify a dependency on " \
34
- "'#{reference.constant.package}'.\n" \
35
- "Are we missing an abstraction?\n" \
36
- "Is the code making the reference, and the referenced constant, in the right packages?\n"
37
- end
38
25
  end
39
26
  end
@@ -1,17 +1,11 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
- require "sorbet-runtime"
5
4
  require "yaml"
6
5
 
7
- require "packwerk/reference"
8
- require "packwerk/reference_lister"
9
- require "packwerk/violation_type"
10
-
11
6
  module Packwerk
12
7
  class DeprecatedReferences
13
8
  extend T::Sig
14
- include ReferenceLister
15
9
 
16
10
  sig { params(package: Packwerk::Package, filepath: String).void }
17
11
  def initialize(package, filepath)
@@ -23,7 +17,6 @@ module Packwerk
23
17
  sig do
24
18
  params(reference: Packwerk::Reference, violation_type: ViolationType)
25
19
  .returns(T::Boolean)
26
- .override
27
20
  end
28
21
  def listed?(reference, violation_type:)
29
22
  violated_constants_found = deprecated_references.dig(reference.constant.package.name, reference.constant.name)
@@ -35,18 +28,19 @@ module Packwerk
35
28
  violated_constants_found.fetch("violations", []).include?(violation_type.serialize)
36
29
  end
37
30
 
38
- sig { params(reference: Packwerk::Reference, violation_type: String).void }
31
+ sig { params(reference: Packwerk::Reference, violation_type: Packwerk::ViolationType).returns(T::Boolean) }
39
32
  def add_entries(reference, violation_type)
40
33
  package_violations = @new_entries.fetch(reference.constant.package.name, {})
41
34
  entries_for_file = package_violations[reference.constant.name] ||= {}
42
35
 
43
36
  entries_for_file["violations"] ||= []
44
- entries_for_file["violations"] << violation_type
37
+ entries_for_file["violations"] << violation_type.serialize
45
38
 
46
39
  entries_for_file["files"] ||= []
47
40
  entries_for_file["files"] << reference.relative_path.to_s
48
41
 
49
42
  @new_entries[reference.constant.package.name] = package_violations
43
+ listed?(reference, violation_type: violation_type)
50
44
  end
51
45
 
52
46
  sig { returns(T::Boolean) }
@@ -3,10 +3,6 @@
3
3
 
4
4
  require "ast/node"
5
5
 
6
- require "packwerk/node"
7
- require "packwerk/offense"
8
- require "packwerk/parsers"
9
-
10
6
  module Packwerk
11
7
  class FileProcessor
12
8
  class UnknownFileTypeResult < Offense
@@ -1,16 +1,11 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "benchmark"
5
- require "sorbet-runtime"
6
-
7
- require "packwerk/inflector"
8
- require "packwerk/output_style"
9
- require "packwerk/output_styles/plain"
10
-
11
4
  module Packwerk
12
5
  module Formatters
13
6
  class OffensesFormatter
7
+ include Packwerk::OffensesFormatter
8
+
14
9
  extend T::Sig
15
10
 
16
11
  sig { params(style: OutputStyle).void }
@@ -18,7 +13,7 @@ module Packwerk
18
13
  @style = style
19
14
  end
20
15
 
21
- sig { params(offenses: T::Array[T.nilable(Offense)]).returns(String) }
16
+ sig { override.params(offenses: T::Array[T.nilable(Offense)]).returns(String) }
22
17
  def show_offenses(offenses)
23
18
  return "No offenses detected 🎉" if offenses.empty?
24
19
 
@@ -3,10 +3,6 @@
3
3
 
4
4
  require "benchmark"
5
5
 
6
- require "packwerk/inflector"
7
- require "packwerk/output_style"
8
- require "packwerk/output_styles/plain"
9
-
10
6
  module Packwerk
11
7
  module Formatters
12
8
  class ProgressFormatter
@@ -45,6 +41,11 @@ module Packwerk
45
41
  @out.puts
46
42
  @out.puts("📦 Finished in #{execution_time.round(2)} seconds")
47
43
  end
44
+
45
+ def interrupted
46
+ @out.puts
47
+ @out.puts("Manually interrupted. Violations caught so far are listed below:")
48
+ end
48
49
  end
49
50
  end
50
51
  end
@@ -1,7 +1,6 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
- require "packwerk/configuration"
5
4
  require "erb"
6
5
 
7
6
  module Packwerk
@@ -2,8 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "active_support/inflector"
5
- require "packwerk/inflections/default"
6
- require "packwerk/inflections/custom"
7
5
 
8
6
  module Packwerk
9
7
  class Inflector
data/lib/packwerk/node.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "parser"
4
5
  require "parser/ast/node"
5
6
 
6
7
  module Packwerk
@@ -1,11 +1,6 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
- require "packwerk/node"
5
- require "packwerk/offense"
6
- require "packwerk/checker"
7
- require "packwerk/reference_lister"
8
-
9
4
  module Packwerk
10
5
  class NodeProcessor
11
6
  extend T::Sig
@@ -13,49 +8,36 @@ module Packwerk
13
8
  sig do
14
9
  params(
15
10
  reference_extractor: ReferenceExtractor,
16
- reference_lister: ReferenceLister,
17
11
  filename: String,
18
12
  checkers: T::Array[Checker]
19
13
  ).void
20
14
  end
21
- def initialize(reference_extractor:, reference_lister:, filename:, checkers:)
15
+ def initialize(reference_extractor:, filename:, checkers:)
22
16
  @reference_extractor = reference_extractor
23
- @reference_lister = reference_lister
24
17
  @filename = filename
25
18
  @checkers = checkers
26
19
  end
27
20
 
28
- sig { params(node: Parser::AST::Node, ancestors: T::Array[Parser::AST::Node]).returns(T.nilable(Offense)) }
21
+ sig { params(node: Parser::AST::Node, ancestors: T::Array[Parser::AST::Node]).returns(T::Array[Offense]) }
29
22
  def call(node, ancestors)
30
- if Node.method_call?(node) || Node.constant?(node)
31
- reference = @reference_extractor.reference_from_node(node, ancestors: ancestors, file_path: @filename)
32
- check_reference(reference, node) if reference
33
- end
23
+ return [] unless Node.method_call?(node) || Node.constant?(node)
24
+ reference = @reference_extractor.reference_from_node(node, ancestors: ancestors, file_path: @filename)
25
+ check_reference(reference, node)
34
26
  end
35
27
 
36
28
  private
37
29
 
38
30
  def check_reference(reference, node)
39
- return nil unless (message = failed_check(reference))
40
-
41
- constant = reference.constant
42
-
43
- Packwerk::Offense.new(
44
- location: Node.location(node),
45
- file: reference.relative_path,
46
- message: <<~EOS
47
- #{message}
48
- Inference details: this is a reference to #{constant.name} which seems to be defined in #{constant.location}.
49
- To receive help interpreting or resolving this error message, see: https://github.com/Shopify/packwerk/blob/main/TROUBLESHOOT.md#Troubleshooting-violations
50
- EOS
51
- )
52
- end
53
-
54
- def failed_check(reference)
55
- failing_checker = @checkers.find do |checker|
56
- checker.invalid_reference?(reference, @reference_lister)
31
+ return [] unless reference
32
+ @checkers.each_with_object([]) do |checker, violations|
33
+ next unless checker.invalid_reference?(reference)
34
+ offense = Packwerk::ReferenceOffense.new(
35
+ location: Node.location(node),
36
+ reference: reference,
37
+ violation_type: checker.violation_type
38
+ )
39
+ violations << offense
57
40
  end
58
- failing_checker&.message_for(reference)
59
41
  end
60
42
  end
61
43
  end
@@ -1,15 +1,11 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "packwerk/constant_name_inspector"
5
- require "packwerk/checker"
6
-
7
4
  module Packwerk
8
5
  class NodeProcessorFactory < T::Struct
9
6
  extend T::Sig
10
7
 
11
8
  const :root_path, String
12
- const :reference_lister, ReferenceLister
13
9
  const :context_provider, Packwerk::ConstantDiscovery
14
10
  const :constant_name_inspectors, T::Array[ConstantNameInspector]
15
11
  const :checkers, T::Array[Checker]
@@ -18,7 +14,6 @@ module Packwerk
18
14
  def for(filename:, node:)
19
15
  ::Packwerk::NodeProcessor.new(
20
16
  reference_extractor: reference_extractor(node: node),
21
- reference_lister: reference_lister,
22
17
  filename: filename,
23
18
  checkers: checkers,
24
19
  )
@@ -1,8 +1,6 @@
1
1
  # typed: false
2
2
  # frozen_string_literal: true
3
3
 
4
- require "packwerk/node"
5
-
6
4
  module Packwerk
7
5
  class NodeVisitor
8
6
  def initialize(node_processor:)
@@ -10,8 +8,7 @@ module Packwerk
10
8
  end
11
9
 
12
10
  def visit(node, ancestors:, result:)
13
- offense = @node_processor.call(node, ancestors)
14
- result << offense if offense
11
+ result.concat(@node_processor.call(node, ancestors))
15
12
 
16
13
  child_ancestors = [node] + ancestors
17
14
  Node.each_child(node) do |child|
@@ -2,10 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "parser/source/map"
5
- require "sorbet-runtime"
6
-
7
- require "packwerk/output_style"
8
- require "packwerk/output_styles/plain"
9
5
 
10
6
  module Packwerk
11
7
  class Offense
@@ -0,0 +1,84 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Packwerk
5
+ class OffenseCollection
6
+ extend T::Sig
7
+ extend T::Helpers
8
+
9
+ sig do
10
+ params(
11
+ root_path: String,
12
+ deprecated_references: T::Hash[Packwerk::Package, Packwerk::DeprecatedReferences]
13
+ ).void
14
+ end
15
+ def initialize(root_path, deprecated_references = {})
16
+ @root_path = root_path
17
+ @deprecated_references = T.let(deprecated_references, T::Hash[Packwerk::Package, Packwerk::DeprecatedReferences])
18
+ @new_violations = T.let([], T::Array[Packwerk::ReferenceOffense])
19
+ @errors = T.let([], T::Array[Packwerk::Offense])
20
+ end
21
+
22
+ sig { returns(T::Array[Packwerk::ReferenceOffense]) }
23
+ attr_reader :new_violations
24
+
25
+ sig { returns(T::Array[Packwerk::Offense]) }
26
+ attr_reader :errors
27
+
28
+ sig do
29
+ params(offense: Packwerk::Offense)
30
+ .returns(T::Boolean)
31
+ end
32
+ def listed?(offense)
33
+ return false unless offense.is_a?(ReferenceOffense)
34
+ reference = offense.reference
35
+ deprecated_references_for(reference.source_package).listed?(reference, violation_type: offense.violation_type)
36
+ end
37
+
38
+ sig do
39
+ params(offense: Packwerk::Offense).void
40
+ end
41
+ def add_offense(offense)
42
+ unless offense.is_a?(ReferenceOffense)
43
+ @errors << offense
44
+ return
45
+ end
46
+ deprecated_references = deprecated_references_for(offense.reference.source_package)
47
+ unless deprecated_references.add_entries(offense.reference, offense.violation_type)
48
+ new_violations << offense
49
+ end
50
+ end
51
+
52
+ sig { returns(T::Boolean) }
53
+ def stale_violations?
54
+ @deprecated_references.values.any?(&:stale_violations?)
55
+ end
56
+
57
+ sig { void }
58
+ def dump_deprecated_references_files
59
+ @deprecated_references.each do |_, deprecated_references_file|
60
+ deprecated_references_file.dump
61
+ end
62
+ end
63
+
64
+ sig { returns(T::Array[Packwerk::Offense]) }
65
+ def outstanding_offenses
66
+ errors + new_violations
67
+ end
68
+
69
+ private
70
+
71
+ sig { params(package: Packwerk::Package).returns(Packwerk::DeprecatedReferences) }
72
+ def deprecated_references_for(package)
73
+ @deprecated_references[package] ||= Packwerk::DeprecatedReferences.new(
74
+ package,
75
+ deprecated_references_file_for(package),
76
+ )
77
+ end
78
+
79
+ sig { params(package: Packwerk::Package).returns(String) }
80
+ def deprecated_references_file_for(package)
81
+ File.join(@root_path, package.name, "deprecated_references.yml")
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,15 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Packwerk
5
+ module OffensesFormatter
6
+ extend T::Sig
7
+ extend T::Helpers
8
+
9
+ interface!
10
+
11
+ sig { abstract.params(offenses: T::Array[T.nilable(Offense)]).returns(String) }
12
+ def show_offenses(offenses)
13
+ end
14
+ end
15
+ end
@@ -52,6 +52,14 @@ module Packwerk
52
52
  name <=> other.name
53
53
  end
54
54
 
55
+ def eql?(other)
56
+ self == other
57
+ end
58
+
59
+ def hash
60
+ name.hash
61
+ end
62
+
55
63
  def to_s
56
64
  name
57
65
  end
@@ -3,8 +3,6 @@
3
3
 
4
4
  require "pathname"
5
5
 
6
- require "packwerk/package"
7
-
8
6
  module Packwerk
9
7
  class PackageSet
10
8
  include Enumerable