visualize_packs 0.5.10 → 0.5.11

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93422667173f599ef3700a5a98451ea6fc00ae8269544e6b66138034fc743f22
4
- data.tar.gz: 9c238ea399cfcd9426f6de268a9b25796ac04995c39bd7ace459961a7e21d502
3
+ metadata.gz: 1c97e3f2997e104fa0359775892adfb5e8cc40d22a85d886bf0f8a1d608398a8
4
+ data.tar.gz: 7b933103509243b81126bc29cd193daf9536a46860d36ad40f1d988df29d77b3
5
5
  SHA512:
6
- metadata.gz: 556726fd4072b0426afa6c7e65271dc06d25f159d70c44245b5058bac0543fbfde80e0cfa205046b02428b856c0ecc2926828c85eb4988f9378b2e8dce5c609b
7
- data.tar.gz: c4d7c4fc2a64f278e9b5e6fae2afce66c1729eb47617807de29f7b6b7518ec091cb4da8a83ac6b945ecbb45b5bf2b2910e839c39c10d8dee3ab69dc817724605
6
+ metadata.gz: 0e0c1ca8ea26ee9cbd57b3e4066960868ea9362de4bae5fad1517481376ba8f3dd476ff8ad1f43c186d9b9c9d291a7332c203b877e37d5e9d6e07f4277d70899
7
+ data.tar.gz: 632e641dc9e1c45dd744bf9d09bff3cc9723138bc8eb27c1d6771afdc51ad3aef2de988801420b64a1c7f27155fb530fcbfe66cd3c4c65e7eb3b4c6de5e220e6
data/bin/visualize_packs CHANGED
@@ -32,12 +32,12 @@ OptionParser.new do |opt|
32
32
  opt.on('--no-privacy', "Don't show privacy enforcement") { |o| options.show_privacy = false }
33
33
  opt.on('--no-teams', "Don't show team colors") { |o| options.show_teams = false }
34
34
 
35
- opt.on('--focus-on=PACKAGE', "Focus on a specific package") { |o| options.focus_package = o }
35
+ opt.on('--focus-folder=FOLDER', "Draw package diagram only for packages in FOLDER. Matches with 'include' for partial matches") { |o| options.focus_folder = o.empty? ? nil : o }
36
+ opt.on('--focus-pack=pack1,pack2', "Focus on a specific package(s). Wildcards support: 'packs/*'") { |o| options.focus_package = o.to_s.split(",") }
36
37
  opt.on('--only-edges-to-focus', "If focus is set, this shows only the edges to/from the focus node instead of all edges in the focussed graph. This only has effect when --focus-on is set.") { |o| options.show_only_edges_to_focus_package = true }
37
38
 
38
39
  opt.on('--roll-nested-into-parent-packs', "Don't show nested packages (not counting root). Connect edges to top-level package instead") { |o| options.roll_nested_into_parent_packs = true }
39
- opt.on('--focus_folder=FOLDER', "Draw package diagram only for packages in FOLDER") { |o| options.focus_folder = o }
40
- opt.on('--no_nested_relationships', "Don't draw relationships between parents and nested packs") { |o| options.show_nested_relationships = false }
40
+ opt.on('--no-nested-relationships', "Don't draw relationships between parents and nested packs") { |o| options.show_nested_relationships = false }
41
41
 
42
42
  opt.on('--exclude-packs=pack1,pack2,etc', "Exclude listed packs from diagram. If used with include you will get all included that are not excluded. Wildcards support: 'packs/ignores/*'") { |o| options.exclude_packs = o.to_s.split(",") }
43
43
  opt.on('--include-packs=pack1,pack2,etc', "Include only listed packs in diagram. If used with exclude you will get all included that are not excluded. Wildcards support: 'packs/ignores/*'") { |o| options.include_packs = o.to_s.split(",") }
data/lib/graph.dot.erb CHANGED
@@ -33,7 +33,7 @@ digraph package_diagram {
33
33
  <%- end -%>
34
34
  <%- grouped_packages[layer_name].each do |package| -%>
35
35
  "<%= package.name -%>" [
36
- fontsize=<%= options.focus_package && package.name == options.focus_package ? 18.0 : 12.0 -%>
36
+ fontsize=<%= options.focus_package.any? && options.focus_package.any? {|p| File.fnmatch(p, package.name)} ? 18.0 : 12.0 -%>
37
37
  <%- if options.remote_base_url %>
38
38
  URL="<%= options.remote_base_url %>/<%= package.name == '.' ? '' : package.name -%>"
39
39
  <%- end %>
data/lib/options.rb CHANGED
@@ -12,7 +12,7 @@ class Options < T::Struct
12
12
  prop :show_privacy, T::Boolean, default: true
13
13
  prop :show_teams, T::Boolean, default: true
14
14
 
15
- prop :focus_package, T.nilable(String)
15
+ prop :focus_package, T::Array[String], default: []
16
16
  prop :show_only_edges_to_focus_package, T::Boolean, default: false
17
17
 
18
18
  prop :roll_nested_into_parent_packs, T::Boolean, default: false
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- # typed: false
2
+ # typed: strict
3
3
 
4
4
  require 'erb'
5
5
  require 'packs-specification'
@@ -7,14 +7,14 @@ require 'parse_packwerk'
7
7
  require 'digest/md5'
8
8
 
9
9
  module VisualizePacks
10
+ extend T::Sig
10
11
 
12
+ sig { params(options: Options, raw_config: T::Hash[String, T.untyped], packages: T::Array[ParsePackwerk::Package]).returns(String) }
11
13
  def self.package_graph!(options, raw_config, packages)
12
- raise ArgumentError, "Package #{options.focus_package} does not exist. Found packages #{packages.map(&:name).join(", ")}" if options.focus_package && !packages.map(&:name).include?(options.focus_package)
13
-
14
- all_packages = filtered(packages, options.focus_package, options.focus_folder, options.include_packs, options.exclude_packs).sort_by {|x| x.name }
14
+ all_packages = filtered(packages, options).compact.sort_by {|x| x.name }
15
15
  all_package_names = all_packages.map &:name
16
16
 
17
- all_packages = remove_nested_packs(all_packages) if options.roll_nested_into_parent_packs
17
+ all_packages = remove_nested_packs(all_packages, options)
18
18
 
19
19
  show_edge = show_edge_builder(options, all_package_names)
20
20
  node_color = node_color_builder()
@@ -48,14 +48,16 @@ module VisualizePacks
48
48
 
49
49
  private
50
50
 
51
+ sig { params(package: ParsePackwerk::Package).returns(T.nilable(String)) }
51
52
  def self.code_owner(package)
52
53
  package.config.dig("metadata", "owner") || package.config["owner"]
53
54
  end
54
55
 
56
+ sig { params(options: Options, max_todo_count: T.nilable(Integer)).returns(String) }
55
57
  def self.diagram_title(options, max_todo_count)
56
58
  app_name = File.basename(Dir.pwd)
57
- focus_edge_info = options.focus_package && options.show_only_edges_to_focus_package ? "showing only edges to/from focus pack" : "showing all edges between visible packs"
58
- focus_info = options.focus_package || options.focus_folder ? "Focus on #{[options.focus_package, options.focus_folder].compact.join(' and ')} (#{focus_edge_info})" : "All packs"
59
+ focus_edge_info = options.focus_package.any? && options.show_only_edges_to_focus_package ? "showing only edges to/from focus pack" : "showing all edges between visible packs"
60
+ focus_info = options.focus_package.any? || options.focus_folder ? "Focus on #{[limited_sentence(options.focus_package), options.focus_folder].compact.join(' and ')} (#{focus_edge_info})" : "All packs"
59
61
  skipped_info =
60
62
  [
61
63
  options.show_legend ? nil : "hiding legend",
@@ -78,14 +80,18 @@ module VisualizePacks
78
80
  "<<b>#{main_title}</b>#{sub_title}>"
79
81
  end
80
82
 
83
+ sig { params(list: T.nilable(T::Array[String])).returns(T.nilable(String)) }
81
84
  def self.limited_sentence(list)
85
+ return nil if !list || list.empty?
86
+
82
87
  if list.size <= 2
83
88
  list.join(" and ")
84
89
  else
85
- "#{list[0, 2].join(", ")}, and #{list.size - 2} more"
90
+ "#{T.must(list[0, 2]).join(", ")}, and #{list.size - 2} more"
86
91
  end
87
92
  end
88
93
 
94
+ sig { params(options: Options, all_package_names: T::Array[String]).returns(T.proc.params(arg0: String, arg1: String).returns(T::Boolean)) }
89
95
  def self.show_edge_builder(options, all_package_names)
90
96
  return lambda do |start_node, end_node|
91
97
  (
@@ -97,11 +103,15 @@ module VisualizePacks
97
103
  options.show_only_edges_to_focus_package &&
98
104
  all_package_names.include?(start_node) &&
99
105
  all_package_names.include?(end_node) &&
100
- [start_node, end_node].include?(options.focus_package)
106
+ (
107
+ match_packs?(start_node, options.focus_package) ||
108
+ match_packs?(end_node, options.focus_package)
109
+ )
101
110
  )
102
111
  end
103
112
  end
104
113
 
114
+ sig { returns(T.nilable(T.proc.params(arg0: String).returns(String))) }
105
115
  def self.node_color_builder
106
116
  return lambda do |text|
107
117
  return unless text
@@ -114,17 +124,17 @@ module VisualizePacks
114
124
  end
115
125
  end
116
126
 
127
+ sig { params(all_packages: T::Array[ParsePackwerk::Package], show_edge: T.proc.params(arg0: String, arg1: String).returns(T::Boolean)).returns(T.nilable(Integer)) }
117
128
  def self.max_todo_count(all_packages, show_edge)
118
129
  todo_counts = {}
119
130
  all_packages.each do |package|
120
- todos_by_package = package.violations.group_by(&:to_package_name)
121
- todos_by_package.keys.each do |todos_to_package|
122
- todo_types = todos_by_package[todos_to_package].group_by(&:type)
123
- todo_types.keys.each do |todo_type|
131
+ todos_by_package = package.violations&.group_by(&:to_package_name)
132
+ todos_by_package&.keys&.each do |todos_to_package|
133
+ todo_types = todos_by_package&& todos_by_package[todos_to_package]&.group_by(&:type)
134
+ todo_types&.keys&.each do |todo_type|
124
135
  if show_edge.call(package.name, todos_to_package)
125
136
  key = "#{package.name}->#{todos_to_package}:#{todo_type}"
126
- todo_counts[key] = todo_types[todo_type].count
127
- # todo_counts[key] += 1
137
+ todo_counts[key] = todo_types && todo_types[todo_type]&.count
128
138
  end
129
139
  end
130
140
  end
@@ -132,6 +142,7 @@ module VisualizePacks
132
142
  todo_counts.values.max
133
143
  end
134
144
 
145
+ sig { params(todo_count: Integer, max_count: Integer).returns(T.any(Float, Integer)) }
135
146
  def self.todo_edge_width(todo_count, max_count)
136
147
  # Limits
137
148
  min_width = 1
@@ -151,22 +162,44 @@ module VisualizePacks
151
162
  edge_width.round(2)
152
163
  end
153
164
 
154
- def self.filtered(packages, filter_package, filter_folder, include_packs, exclude_packs)
155
- return packages unless filter_package || filter_folder || include_packs || exclude_packs.any?
165
+ sig { params(packages: T::Array[ParsePackwerk::Package], options: Options).returns(T::Array[ParsePackwerk::Package]) }
166
+ def self.filtered(packages, options)
167
+ focus_package = options.focus_package
168
+ focus_folder = options.focus_folder
169
+ include_packs = options.include_packs
170
+ exclude_packs = options.exclude_packs
171
+
172
+ return packages unless focus_package.any? || focus_folder || include_packs || exclude_packs.any?
173
+
174
+ nested_packages = all_nested_packages(packages.map { |p| p.name })
156
175
 
176
+ packages_by_name = packages.inject({}) do |res, p|
177
+ res[p.name] = p
178
+ res
179
+ end
180
+
181
+ result = T.let([], T::Array[T.nilable(String)])
157
182
  result = packages.map { |pack| pack.name }
158
183
 
159
- if filter_package
160
- result = [filter_package]
161
- result += packages.select{ |p| p.dependencies.include? filter_package }.map { |pack| pack.name }
162
- result += ParsePackwerk.find(filter_package).dependencies
163
- result += packages.select{ |p| p.violations.map(&:to_package_name).include? filter_package }.map { |pack| pack.name }
164
- result += ParsePackwerk.find(filter_package).violations.map(&:to_package_name)
184
+ if !focus_package.empty?
185
+ result = []
186
+ result += packages.map { |pack| pack.name }.select { |p| match_packs?(p, focus_package) }
187
+ result += packages.select{ |p| p.dependencies.any? { |d| match_packs?(d, focus_package) }}.map { |pack| pack.name }
188
+ result += packages.select{ |p| p.violations&.map(&:to_package_name)&.any? { |v| match_packs?(v, focus_package) }}.map { |pack| pack.name }
189
+ packages.map { |pack| pack.name }.select { |p| match_packs?(p, focus_package) }.each do |p|
190
+ result += packages_by_name[p].dependencies
191
+ result += packages_by_name[p].violations.map(&:to_package_name)
192
+ end
165
193
  result = result.uniq
194
+ parent_packs = result.inject([]) do |res, package_name|
195
+ res << nested_packages[package_name]
196
+ res
197
+ end
198
+ result = (result + parent_packs).uniq.compact
166
199
  end
167
200
 
168
- if filter_folder
169
- result = result.select { |p| p.include? filter_folder }
201
+ if focus_folder
202
+ result = result.select { |p| p.include? focus_folder }
170
203
  end
171
204
 
172
205
  if include_packs
@@ -177,9 +210,10 @@ module VisualizePacks
177
210
  result = result.reject { |p| match_packs?(p, exclude_packs) }
178
211
  end
179
212
 
180
- result.map { |pack_name| ParsePackwerk.find(pack_name) }
213
+ result.map { |pack_name| packages_by_name[pack_name] }
181
214
  end
182
215
 
216
+ sig { params(all_package_names: T::Array[String]).returns(T::Hash[String, String]) }
183
217
  def self.all_nested_packages(all_package_names)
184
218
  all_package_names.reject { |p| p == '.' }.inject({}) do |result, package|
185
219
  package_map_tally = all_package_names.map { |other_package| Pathname.new(package).parent.to_s.include?(other_package) }
@@ -193,7 +227,10 @@ module VisualizePacks
193
227
  end
194
228
  end
195
229
 
196
- def self.remove_nested_packs(packages)
230
+ sig { params(packages: T::Array[ParsePackwerk::Package], options: Options).returns(T::Array[ParsePackwerk::Package]) }
231
+ def self.remove_nested_packs(packages, options)
232
+ return packages unless options.roll_nested_into_parent_packs
233
+
197
234
  nested_packages = all_nested_packages(packages.map { |p| p.name })
198
235
 
199
236
  # top-level packages
@@ -212,9 +249,9 @@ module VisualizePacks
212
249
  enforce_privacy: package.enforce_privacy,
213
250
  public_path: package.public_path,
214
251
  metadata: package.metadata,
215
- dependencies: package.dependencies + nested_package.dependencies,
252
+ dependencies: package.dependencies + T.must(nested_package).dependencies,
216
253
  config: package.config,
217
- violations: package.violations + nested_package.violations
254
+ violations: T.must(package.violations) + T.must(T.must(nested_package).violations)
218
255
  )
219
256
  end
220
257
  end
@@ -224,7 +261,7 @@ module VisualizePacks
224
261
  nested_packages[d] || d
225
262
  end.uniq.reject { |p| p == package.name }
226
263
 
227
- morphed_todos = package.violations.map do |v|
264
+ morphed_todos = T.must(package.violations).map do |v|
228
265
  ParsePackwerk::Violation.new(
229
266
  type: v.type,
230
267
  to_package_name: nested_packages[v.to_package_name] || v.to_package_name,
@@ -244,15 +281,14 @@ module VisualizePacks
244
281
  config: package.config,
245
282
  violations: morphed_todos
246
283
  )
247
- # add dependencies TO nested packages to top-level package
248
- # add todos TO nested packages to top-level package
249
284
  end
250
285
  end
251
286
 
252
287
  morphed_packages.reject { |p| nested_packages.keys.include?(p.name) }
253
288
  end
254
289
 
255
- def self.match_packs?(pack, packs)
256
- packs.any? {|p| File.fnmatch(p, pack)}
290
+ sig { params(pack: String, packs_name_with_wildcards: T::Array[String]).returns(T::Boolean) }
291
+ def self.match_packs?(pack, packs_name_with_wildcards)
292
+ packs_name_with_wildcards.any? {|p| File.fnmatch(p, pack)}
257
293
  end
258
294
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: visualize_packs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.10
4
+ version: 0.5.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gusto Engineers