visualize_packs 0.5.3 → 0.5.5
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 +4 -4
- data/bin/visualize_packs +18 -1
- data/lib/graph.dot.erb +35 -27
- data/lib/options.rb +4 -0
- data/lib/visualize_packs.rb +21 -6
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b4ade528fc794c71afb2470d457f31d72b2456b85a484f2b328ca1c73e7fc2cf
|
4
|
+
data.tar.gz: 239feaba7ced4136ac77625e6ed1632a031492e86ca6d1c0d025976b23e31300
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18a67a3b983c99f4d273a8e32e390a63bb0fa0398c7da51d4552fee73eedda0e77debfe45a3e196cae5b8d0e370124f7eeb82d202f4daba7b68bb90fb5b15708
|
7
|
+
data.tar.gz: 60e06eaacba3c796ee9045223d47203f63b19e83664c58b9db69696c591f68169f217bc8172287fcb9cc61befaa2008c4a337e486febd0477b6a0b8d14100520
|
data/bin/visualize_packs
CHANGED
@@ -10,20 +10,37 @@ require_relative '../lib/options'
|
|
10
10
|
|
11
11
|
options = Options.new
|
12
12
|
|
13
|
+
supported_todo_types = %w[
|
14
|
+
privacy
|
15
|
+
architecture
|
16
|
+
visibility
|
17
|
+
dependency
|
18
|
+
].sort.freeze
|
19
|
+
|
20
|
+
def validated_list(o, valid_arguments)
|
21
|
+
list = o.to_s.split(",").uniq
|
22
|
+
raise OptionParser::InvalidArgument, o unless (list - valid_arguments).empty?
|
23
|
+
list
|
24
|
+
end
|
25
|
+
|
13
26
|
OptionParser.new do |opt|
|
27
|
+
opt.on('--no-legend', "Don't show legend") { |o| options.show_legend = false }
|
14
28
|
opt.on('--no-layers', "Don't show architectural layers") { |o| options.show_layers = false }
|
15
29
|
opt.on('--no-dependencies', "Don't show accepted dependencies") { |o| options.show_dependencies = false }
|
16
30
|
opt.on('--no-todos', "Don't show package todos") { |o| options.show_todos = false }
|
31
|
+
opt.on('--only-todo-types=privacy,architecture,etc', "Show only these types of todos (supported types: #{supported_todo_types.join(', ')})") { |o| options.only_todo_types = validated_list(o, supported_todo_types) }
|
17
32
|
opt.on('--no-privacy', "Don't show privacy enforcement") { |o| options.show_privacy = false }
|
18
33
|
opt.on('--no-teams', "Don't show team colors") { |o| options.show_teams = false }
|
19
34
|
|
20
|
-
opt.on('--focus-on=PACKAGE', "
|
35
|
+
opt.on('--focus-on=PACKAGE', "Focus on a specific package") { |o| options.focus_package = o }
|
21
36
|
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 }
|
22
37
|
|
23
38
|
opt.on('--roll_nested_todos_into_top_level', "Don't show nested packages (not counting root). Connect edges to top-level package instead") { |o| options.roll_nested_todos_into_top_level = true }
|
24
39
|
opt.on('--focus_folder=FOLDER', "Draw package diagram only for packages in FOLDER") { |o| options.focus_folder = o }
|
25
40
|
opt.on('--no_nested_relationships', "Don't draw nested package relationships") { |o| options.show_nested_relationships = false }
|
26
41
|
|
42
|
+
opt.on('--exclude-packs=pack1,pack2,etc', "Exclude these packs from diagram") { |o| options.exclude_packs = o.to_s.split(",") }
|
43
|
+
|
27
44
|
opt.on('--remote-base-url=PACKAGE', "Link package nodes to an URL (affects graphviz SVG generation)") { |o| options.remote_base_url = o }
|
28
45
|
|
29
46
|
opt.on_tail("-h", "--help", "Show this message") do
|
data/lib/graph.dot.erb
CHANGED
@@ -81,10 +81,14 @@ digraph package_diagram {
|
|
81
81
|
<%- end -%>
|
82
82
|
<%- if options.show_todos -%>
|
83
83
|
<%- all_packages.each do |package| -%>
|
84
|
-
<%-
|
84
|
+
<%- filtered_violations = package.violations -%>
|
85
|
+
<%- if options.only_todo_types.any? -%>
|
86
|
+
<%- filtered_violations = filtered_violations.select { options.only_todo_types.include?(_1.type) } -%>
|
87
|
+
<%- end -%>
|
88
|
+
<%- violations_by_package = filtered_violations.group_by(&:to_package_name) -%>
|
85
89
|
<%- violations_by_package.keys.each do |violations_to_package| -%>
|
86
|
-
<%-
|
87
|
-
<%-
|
90
|
+
<%- todo_types = violations_by_package[violations_to_package].group_by(&:type) -%>
|
91
|
+
<%- todo_types.keys.each do |violation_type| -%>
|
88
92
|
<%- if show_edge.call(package.name, violations_to_package) -%>
|
89
93
|
"<%= package.name -%>" -> "<%= violations_to_package -%>"<%= violation_type == 'privacy' ? ':private' : '' -%> [ color=darkred style=dashed
|
90
94
|
constraint=false
|
@@ -100,7 +104,7 @@ digraph package_diagram {
|
|
100
104
|
<%- end -%>
|
101
105
|
<%-
|
102
106
|
max_edge_width = 10
|
103
|
-
edge_width = (
|
107
|
+
edge_width = (todo_types[violation_type].count / max_violation_count.to_f * max_edge_width).to_i
|
104
108
|
-%>
|
105
109
|
penwidth=<%= edge_width -%>
|
106
110
|
]
|
@@ -118,28 +122,30 @@ digraph package_diagram {
|
|
118
122
|
<%- end -%>
|
119
123
|
<%- end -%>
|
120
124
|
<%- end -%>
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
125
|
+
<%- if options.show_legend -%>
|
126
|
+
subgraph cluster_legend {
|
127
|
+
fontsize=16
|
128
|
+
label="Edges Styles and Arrow Heads"
|
129
|
+
A [ fontsize=12 shape=box label="package"]
|
130
|
+
B [ fontsize=12 shape=box label="package"]
|
131
|
+
C [ fontsize=12 shape=box label="package"]
|
132
|
+
D [ fontsize=12 shape=box label="package"]
|
133
|
+
E [ fontsize=12 shape=box label="package"]
|
134
|
+
F [ fontsize=12 shape=box label="package"]
|
135
|
+
G [ fontsize=12 shape=box label="package"]
|
136
|
+
H [ fontsize=12 shape=box label="package"]
|
137
|
+
I [ fontsize=12 shape=box label="package"]
|
138
|
+
J [ fontsize=12 shape=box label="package"]
|
139
|
+
K [ fontsize=12 shape=box label="package"]
|
140
|
+
L [ fontsize=12 shape=box label="package"]
|
141
|
+
A -> B [label="accepted dependency" color=darkgreen]
|
142
|
+
C -> D [label="privac todo" color=darkred style=dashed arrowhead=crow]
|
143
|
+
E -> F [label="architecture todo" color=darkred style=dashed arrowhead=invodot]
|
144
|
+
G -> H [label="visibility todo" color=darkred style=dashed arrowhead=obox]
|
145
|
+
I -> J [label="dependency todo" color=darkred style=dashed arrowhead=odot]
|
146
|
+
K -> L [label="nested package" color=purple penwidth=3]
|
147
|
+
}
|
148
|
+
<%- end -%>
|
143
149
|
<%- if options.show_teams && all_team_names != [] -%>
|
144
150
|
subgraph cluster_teams_legend {
|
145
151
|
fontsize=16
|
@@ -157,6 +163,8 @@ digraph package_diagram {
|
|
157
163
|
<%- end %>
|
158
164
|
<%- end -%>
|
159
165
|
}
|
160
|
-
|
166
|
+
<%- if options.show_legend -%>
|
167
|
+
J -> "<%= all_team_names.last %><%= all_team_names.last %>" [style=invis]
|
168
|
+
<%- end -%>
|
161
169
|
<%- end -%>
|
162
170
|
}
|
data/lib/options.rb
CHANGED
@@ -4,9 +4,11 @@
|
|
4
4
|
class Options < T::Struct
|
5
5
|
extend T::Sig
|
6
6
|
|
7
|
+
prop :show_legend, T::Boolean, default: true
|
7
8
|
prop :show_layers, T::Boolean, default: true
|
8
9
|
prop :show_dependencies, T::Boolean, default: true
|
9
10
|
prop :show_todos, T::Boolean, default: true
|
11
|
+
prop :only_todo_types, T::Array[String], default: []
|
10
12
|
prop :show_privacy, T::Boolean, default: true
|
11
13
|
prop :show_teams, T::Boolean, default: true
|
12
14
|
|
@@ -17,5 +19,7 @@ class Options < T::Struct
|
|
17
19
|
prop :focus_folder, T.nilable(String)
|
18
20
|
prop :show_nested_relationships, T::Boolean, default: true
|
19
21
|
|
22
|
+
prop :exclude_packs, T::Array[String], default: []
|
23
|
+
|
20
24
|
prop :remote_base_url, T.nilable(String)
|
21
25
|
end
|
data/lib/visualize_packs.rb
CHANGED
@@ -11,7 +11,7 @@ module VisualizePacks
|
|
11
11
|
def self.package_graph!(options, raw_config, packages)
|
12
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
13
|
|
14
|
-
all_packages = filtered(packages, options.focus_package, options.focus_folder).sort_by {|x| x.name }
|
14
|
+
all_packages = filtered(packages, options.focus_package, options.focus_folder, options.exclude_packs).sort_by {|x| x.name }
|
15
15
|
all_package_names = all_packages.map &:name
|
16
16
|
|
17
17
|
all_packages = remove_nested_packs(all_packages) if options.roll_nested_todos_into_top_level
|
@@ -58,13 +58,16 @@ module VisualizePacks
|
|
58
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
59
|
skipped_info =
|
60
60
|
[
|
61
|
+
options.show_legend ? nil : "hiding legend",
|
61
62
|
options.show_layers ? nil : "hiding layers",
|
62
63
|
options.show_dependencies ? nil : "hiding dependencies",
|
63
64
|
options.show_todos ? nil : "hiding todos",
|
65
|
+
options.only_todo_types.empty? ? nil : "only #{limited_sentence(options.only_todo_types)} todos",
|
64
66
|
options.show_privacy ? nil : "hiding privacy",
|
65
67
|
options.show_teams ? nil : "hiding teams",
|
66
68
|
options.roll_nested_todos_into_top_level ? "hiding nested packs" : nil,
|
67
69
|
options.show_nested_relationships ? nil : "hiding nested relationships",
|
70
|
+
options.exclude_packs.empty? ? nil : "excluding pack#{options.exclude_packs.size > 1 ? 's' : ''}: #{limited_sentence(options.exclude_packs)}",
|
68
71
|
].compact.join(', ').strip
|
69
72
|
main_title = "#{app_name}: #{focus_info}#{skipped_info != '' ? ' - ' + skipped_info : ''}"
|
70
73
|
sub_title = ""
|
@@ -74,6 +77,14 @@ module VisualizePacks
|
|
74
77
|
"<<b>#{main_title}</b>#{sub_title}>"
|
75
78
|
end
|
76
79
|
|
80
|
+
def self.limited_sentence(list)
|
81
|
+
if list.size <= 2
|
82
|
+
list.join(" and ")
|
83
|
+
else
|
84
|
+
"#{list[0, 2].join(", ")}, and #{list.size - 2} more"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
77
88
|
def self.show_edge_builder(options, all_package_names)
|
78
89
|
return lambda do |start_node, end_node|
|
79
90
|
(
|
@@ -107,11 +118,11 @@ module VisualizePacks
|
|
107
118
|
all_packages.each do |package|
|
108
119
|
violations_by_package = package.violations.group_by(&:to_package_name)
|
109
120
|
violations_by_package.keys.each do |violations_to_package|
|
110
|
-
|
111
|
-
|
121
|
+
todo_types = violations_by_package[violations_to_package].group_by(&:type)
|
122
|
+
todo_types.keys.each do |violation_type|
|
112
123
|
if show_edge.call(package.name, violations_to_package)
|
113
124
|
key = "#{package.name}->#{violations_to_package}:#{violation_type}"
|
114
|
-
violation_counts[key] =
|
125
|
+
violation_counts[key] = todo_types[violation_type].count
|
115
126
|
# violation_counts[key] += 1
|
116
127
|
end
|
117
128
|
end
|
@@ -120,8 +131,8 @@ module VisualizePacks
|
|
120
131
|
violation_counts.values.max
|
121
132
|
end
|
122
133
|
|
123
|
-
def self.filtered(packages, filter_package, filter_folder)
|
124
|
-
return packages unless filter_package || filter_folder
|
134
|
+
def self.filtered(packages, filter_package, filter_folder, exclude_packs)
|
135
|
+
return packages unless filter_package || filter_folder || exclude_packs.any?
|
125
136
|
|
126
137
|
result = packages.map { |pack| pack.name }
|
127
138
|
|
@@ -138,6 +149,10 @@ module VisualizePacks
|
|
138
149
|
result = result.select { |p| p.include? filter_folder }
|
139
150
|
end
|
140
151
|
|
152
|
+
if exclude_packs.any?
|
153
|
+
result = result.reject { |p| exclude_packs.include? p }
|
154
|
+
end
|
155
|
+
|
141
156
|
result.map { |pack_name| ParsePackwerk.find(pack_name) }
|
142
157
|
end
|
143
158
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: visualize_packs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gusto Engineers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-08-
|
11
|
+
date: 2023-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|