packs 0.0.5 → 0.0.22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +101 -12
  3. data/bin/packs +10 -0
  4. data/bin/rubocop +29 -0
  5. data/bin/tapioca +29 -0
  6. data/lib/packs/cli.rb +164 -0
  7. data/lib/packs/code_ownership_post_processor.rb +58 -0
  8. data/lib/packs/configuration.rb +61 -0
  9. data/lib/packs/default_user_event_logger.rb +7 -0
  10. data/lib/packs/logging.rb +37 -0
  11. data/lib/packs/per_file_processor_interface.rb +18 -0
  12. data/lib/packs/private/file_move_operation.rb +80 -0
  13. data/lib/packs/private/interactive_cli/file_selector.rb +26 -0
  14. data/lib/packs/private/interactive_cli/pack_selector.rb +55 -0
  15. data/lib/packs/private/interactive_cli/team_selector.rb +58 -0
  16. data/lib/packs/private/interactive_cli/use_cases/add_dependency.rb +30 -0
  17. data/lib/packs/private/interactive_cli/use_cases/check.rb +25 -0
  18. data/lib/packs/private/interactive_cli/use_cases/create.rb +27 -0
  19. data/lib/packs/private/interactive_cli/use_cases/get_info.rb +37 -0
  20. data/lib/packs/private/interactive_cli/use_cases/interface.rb +34 -0
  21. data/lib/packs/private/interactive_cli/use_cases/lint_package_todo_yml_files.rb +25 -0
  22. data/lib/packs/private/interactive_cli/use_cases/lint_package_yml_files.rb +26 -0
  23. data/lib/packs/private/interactive_cli/use_cases/make_public.rb +30 -0
  24. data/lib/packs/private/interactive_cli/use_cases/move.rb +32 -0
  25. data/lib/packs/private/interactive_cli/use_cases/move_to_parent.rb +31 -0
  26. data/lib/packs/private/interactive_cli/use_cases/query.rb +51 -0
  27. data/lib/packs/private/interactive_cli/use_cases/rename.rb +25 -0
  28. data/lib/packs/private/interactive_cli/use_cases/update.rb +25 -0
  29. data/lib/packs/private/interactive_cli/use_cases/validate.rb +25 -0
  30. data/lib/packs/private/interactive_cli/use_cases/visualize.rb +44 -0
  31. data/lib/packs/private/interactive_cli.rb +52 -0
  32. data/lib/packs/private/pack_relationship_analyzer.rb +135 -0
  33. data/lib/packs/private/packwerk_wrapper/offenses_aggregator_formatter.rb +44 -0
  34. data/lib/packs/private/packwerk_wrapper.rb +70 -0
  35. data/lib/packs/private.rb +606 -4
  36. data/lib/packs/rubocop_post_processor.rb +30 -0
  37. data/lib/packs/user_event_logger.rb +199 -0
  38. data/lib/packs.rb +233 -53
  39. metadata +225 -14
  40. data/lib/packs/pack.rb +0 -43
  41. data/lib/packs/private/configuration.rb +0 -36
  42. data/lib/packs/rspec/fixture_helper.rb +0 -33
  43. data/lib/packs/rspec/support.rb +0 -21
@@ -0,0 +1,135 @@
1
+ # typed: strict
2
+
3
+ module Packs
4
+ module Private
5
+ module PackRelationshipAnalyzer
6
+ extend T::Sig
7
+
8
+ sig do
9
+ params(
10
+ pack_name: T.nilable(String),
11
+ limit: Integer
12
+ ).void
13
+ end
14
+ def self.list_top_privacy_violations(pack_name, limit)
15
+ all_packages = ParsePackwerk.all
16
+ if pack_name.nil?
17
+ to_package_names = all_packages.map(&:name)
18
+ else
19
+ pack_name = Private.clean_pack_name(pack_name)
20
+ package = all_packages.find { |p| p.name == pack_name }
21
+ if package.nil?
22
+ raise StandardError, "Can not find package with name #{pack_name}. Make sure the argument is of the form `packs/my_pack/`"
23
+ end
24
+
25
+ to_package_names = [pack_name]
26
+ end
27
+
28
+ violations_by_count = {}
29
+ total_pack_violation_count = 0
30
+
31
+ Logging.section('👋 Hi there') do
32
+ intro = Packs.config.user_event_logger.before_list_top_privacy_violations(pack_name, limit)
33
+ Logging.print_bold_green(intro)
34
+ end
35
+
36
+ # TODO: This is a copy of the implementation below. We may want to refactor out this implementation detail before making changes that apply to both.
37
+ all_packages.each do |client_package|
38
+ client_package.violations.select(&:privacy?).each do |violation|
39
+ next unless to_package_names.include?(violation.to_package_name)
40
+
41
+ if pack_name.nil?
42
+ violated_symbol = "#{violation.class_name} (#{violation.to_package_name})"
43
+ else
44
+ violated_symbol = violation.class_name
45
+ end
46
+ violations_by_count[violated_symbol] ||= {}
47
+ violations_by_count[violated_symbol][:total_count] ||= 0
48
+ violations_by_count[violated_symbol][:by_package] ||= {}
49
+ violations_by_count[violated_symbol][:by_package][client_package.name] ||= 0
50
+ violations_by_count[violated_symbol][:total_count] += violation.files.count
51
+ violations_by_count[violated_symbol][:by_package][client_package.name] += violation.files.count
52
+ total_pack_violation_count += violation.files.count
53
+ end
54
+ end
55
+
56
+ Logging.print("Total Count: #{total_pack_violation_count}")
57
+
58
+ sorted_violations = violations_by_count.sort_by { |violated_symbol, count_info| [-count_info[:total_count], violated_symbol] }
59
+ sorted_violations.first(limit).each do |violated_symbol, count_info|
60
+ percentage_of_total = (count_info[:total_count] * 100.0 / total_pack_violation_count).round(2)
61
+ Logging.print(violated_symbol)
62
+ Logging.print(" - Total Count: #{count_info[:total_count]} (#{percentage_of_total}% of total)")
63
+
64
+ Logging.print(' - By package:')
65
+ count_info[:by_package].sort_by { |client_package_name, count| [-count, client_package_name] }.each do |client_package_name, count|
66
+ Logging.print(" - #{client_package_name}: #{count}")
67
+ end
68
+ end
69
+ end
70
+
71
+ sig do
72
+ params(
73
+ pack_name: T.nilable(String),
74
+ limit: Integer
75
+ ).void
76
+ end
77
+ def self.list_top_dependency_violations(pack_name, limit)
78
+ all_packages = ParsePackwerk.all
79
+
80
+ if pack_name.nil?
81
+ to_package_names = all_packages.map(&:name)
82
+ else
83
+ pack_name = Private.clean_pack_name(pack_name)
84
+ package = all_packages.find { |p| p.name == pack_name }
85
+ if package.nil?
86
+ raise StandardError, "Can not find package with name #{pack_name}. Make sure the argument is of the form `packs/my_pack/`"
87
+ end
88
+
89
+ to_package_names = [pack_name]
90
+ end
91
+
92
+ Logging.section('👋 Hi there') do
93
+ intro = Packs.config.user_event_logger.before_list_top_dependency_violations(pack_name, limit)
94
+ Logging.print_bold_green(intro)
95
+ end
96
+
97
+ violations_by_count = {}
98
+ total_pack_violation_count = 0
99
+
100
+ # TODO: This is a copy of the implementation above. We may want to refactor out this implementation detail before making changes that apply to both.
101
+ all_packages.each do |client_package|
102
+ client_package.violations.select(&:dependency?).each do |violation|
103
+ next unless to_package_names.include?(violation.to_package_name)
104
+
105
+ if pack_name.nil?
106
+ violated_symbol = "#{violation.class_name} (#{violation.to_package_name})"
107
+ else
108
+ violated_symbol = violation.class_name
109
+ end
110
+ violations_by_count[violated_symbol] ||= {}
111
+ violations_by_count[violated_symbol][:total_count] ||= 0
112
+ violations_by_count[violated_symbol][:by_package] ||= {}
113
+ violations_by_count[violated_symbol][:by_package][client_package.name] ||= 0
114
+ violations_by_count[violated_symbol][:total_count] += violation.files.count
115
+ violations_by_count[violated_symbol][:by_package][client_package.name] += violation.files.count
116
+ total_pack_violation_count += violation.files.count
117
+ end
118
+ end
119
+
120
+ Logging.print("Total Count: #{total_pack_violation_count}")
121
+
122
+ sorted_violations = violations_by_count.sort_by { |violated_symbol, count_info| [-count_info[:total_count], violated_symbol] }
123
+ sorted_violations.first(limit).each do |violated_symbol, count_info|
124
+ percentage_of_total = (count_info[:total_count] * 100.0 / total_pack_violation_count).round(2)
125
+ Logging.print(violated_symbol)
126
+ Logging.print(" - Total Count: #{count_info[:total_count]} (#{percentage_of_total}% of total)")
127
+ Logging.print(' - By package:')
128
+ count_info[:by_package].sort_by { |client_package_name, count| [-count, client_package_name] }.each do |client_package_name, count|
129
+ Logging.print(" - #{client_package_name}: #{count}")
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,44 @@
1
+ # typed: strict
2
+
3
+ module Packs
4
+ module Private
5
+ module PackwerkWrapper
6
+ #
7
+ # This formatter simply collects offenses so we can feed them into other systems
8
+ #
9
+ class OffensesAggregatorFormatter
10
+ extend T::Sig
11
+ include Packwerk::OffensesFormatter
12
+
13
+ sig { returns(T::Array[Packwerk::ReferenceOffense]) }
14
+ attr_reader :aggregated_offenses
15
+
16
+ sig { void }
17
+ def initialize
18
+ @aggregated_offenses = T.let([], T::Array[Packwerk::ReferenceOffense])
19
+ end
20
+
21
+ sig { override.params(offenses: T::Array[T.nilable(Packwerk::Offense)]).returns(String) }
22
+ def show_offenses(offenses)
23
+ @aggregated_offenses = T.unsafe(offenses)
24
+ ''
25
+ end
26
+
27
+ sig { override.params(offense_collection: Packwerk::OffenseCollection, for_files: T::Set[String]).returns(String) }
28
+ def show_stale_violations(offense_collection, for_files)
29
+ ''
30
+ end
31
+
32
+ sig { override.params(strict_mode_violations: T::Array[::Packwerk::ReferenceOffense]).returns(::String) }
33
+ def show_strict_mode_violations(strict_mode_violations)
34
+ ''
35
+ end
36
+
37
+ sig { override.returns(::String) }
38
+ def identifier
39
+ 'offenses_aggregator'
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,70 @@
1
+ # typed: strict
2
+
3
+ require 'packwerk'
4
+ require 'packs/private/packwerk_wrapper/offenses_aggregator_formatter'
5
+
6
+ module Packs
7
+ module Private
8
+ module PackwerkWrapper
9
+ extend T::Sig
10
+
11
+ #
12
+ # execute_command is like `run` except it does not `exit`
13
+ #
14
+ sig { params(argv: T.untyped, formatter: T.nilable(Packwerk::OffensesFormatter)).void }
15
+ def self.packwerk_cli_execute_safely(argv, formatter = nil)
16
+ with_safe_exit_if_no_files_found do
17
+ packwerk_cli(formatter).execute_command(argv)
18
+ end
19
+ end
20
+
21
+ sig { params(block: T.proc.returns(T.untyped)).void }
22
+ def self.with_safe_exit_if_no_files_found(&block)
23
+ block.call
24
+ rescue SystemExit => e
25
+ # Packwerk should probably exit positively here rather than raising an error -- there should be no
26
+ # errors if the user has excluded all files being checked.
27
+ unless e.message == 'No files found or given. Specify files or check the include and exclude glob in the config file.'
28
+ raise
29
+ end
30
+ end
31
+
32
+ sig { params(formatter: T.nilable(Packwerk::OffensesFormatter)).returns(Packwerk::Cli) }
33
+ def self.packwerk_cli(formatter)
34
+ # This is mostly copied from exe/packwerk within the packwerk gem, but we use our own formatters
35
+ # Note that packwerk does not allow you to pass in your own progress formatter currently
36
+ ENV['RAILS_ENV'] = 'test'
37
+
38
+ style = Packwerk::OutputStyles::Coloured.new
39
+ Packwerk::Cli.new(style: style, offenses_formatter: formatter)
40
+ end
41
+
42
+ sig { params(files: T::Array[String]).returns(T::Array[Packwerk::ReferenceOffense]) }
43
+ def self.get_offenses_for_files(files)
44
+ formatter = OffensesAggregatorFormatter.new
45
+ packwerk_cli_execute_safely(['check', *files], formatter)
46
+ formatter.aggregated_offenses.compact
47
+ end
48
+
49
+ sig { void }
50
+ def self.validate!
51
+ formatter = OffensesAggregatorFormatter.new
52
+ packwerk_cli_execute_safely(['validate'], formatter)
53
+ end
54
+
55
+ sig { params(files: T::Array[String]).returns(T::Array[Packwerk::ReferenceOffense]) }
56
+ def self.get_offenses_for_files_by_package(files)
57
+ packages = package_names_for_files(files)
58
+ argv = ['check', '--packages', packages.join(',')]
59
+ formatter = OffensesAggregatorFormatter.new
60
+ packwerk_cli_execute_safely(argv, formatter)
61
+ formatter.aggregated_offenses.compact
62
+ end
63
+
64
+ sig { params(files: T::Array[String]).returns(T::Array[String]) }
65
+ def self.package_names_for_files(files)
66
+ files.map { |f| ParsePackwerk.package_from_path(f).name }.compact.uniq
67
+ end
68
+ end
69
+ end
70
+ end