packs 0.0.5 → 0.0.22
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +101 -12
- data/bin/packs +10 -0
- data/bin/rubocop +29 -0
- data/bin/tapioca +29 -0
- data/lib/packs/cli.rb +164 -0
- data/lib/packs/code_ownership_post_processor.rb +58 -0
- data/lib/packs/configuration.rb +61 -0
- data/lib/packs/default_user_event_logger.rb +7 -0
- data/lib/packs/logging.rb +37 -0
- data/lib/packs/per_file_processor_interface.rb +18 -0
- data/lib/packs/private/file_move_operation.rb +80 -0
- data/lib/packs/private/interactive_cli/file_selector.rb +26 -0
- data/lib/packs/private/interactive_cli/pack_selector.rb +55 -0
- data/lib/packs/private/interactive_cli/team_selector.rb +58 -0
- data/lib/packs/private/interactive_cli/use_cases/add_dependency.rb +30 -0
- data/lib/packs/private/interactive_cli/use_cases/check.rb +25 -0
- data/lib/packs/private/interactive_cli/use_cases/create.rb +27 -0
- data/lib/packs/private/interactive_cli/use_cases/get_info.rb +37 -0
- data/lib/packs/private/interactive_cli/use_cases/interface.rb +34 -0
- data/lib/packs/private/interactive_cli/use_cases/lint_package_todo_yml_files.rb +25 -0
- data/lib/packs/private/interactive_cli/use_cases/lint_package_yml_files.rb +26 -0
- data/lib/packs/private/interactive_cli/use_cases/make_public.rb +30 -0
- data/lib/packs/private/interactive_cli/use_cases/move.rb +32 -0
- data/lib/packs/private/interactive_cli/use_cases/move_to_parent.rb +31 -0
- data/lib/packs/private/interactive_cli/use_cases/query.rb +51 -0
- data/lib/packs/private/interactive_cli/use_cases/rename.rb +25 -0
- data/lib/packs/private/interactive_cli/use_cases/update.rb +25 -0
- data/lib/packs/private/interactive_cli/use_cases/validate.rb +25 -0
- data/lib/packs/private/interactive_cli/use_cases/visualize.rb +44 -0
- data/lib/packs/private/interactive_cli.rb +52 -0
- data/lib/packs/private/pack_relationship_analyzer.rb +135 -0
- data/lib/packs/private/packwerk_wrapper/offenses_aggregator_formatter.rb +44 -0
- data/lib/packs/private/packwerk_wrapper.rb +70 -0
- data/lib/packs/private.rb +606 -4
- data/lib/packs/rubocop_post_processor.rb +30 -0
- data/lib/packs/user_event_logger.rb +199 -0
- data/lib/packs.rb +233 -53
- metadata +225 -14
- data/lib/packs/pack.rb +0 -43
- data/lib/packs/private/configuration.rb +0 -36
- data/lib/packs/rspec/fixture_helper.rb +0 -33
- 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
|