visualize_packwerk 0.1.3 → 0.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 358f7f6ce6490e9a49b9a898af9d877181c1ecffd99068dea9cbaa5267f1a552
4
- data.tar.gz: 4ff54f1b92868804ce3a49bba54f54668ccc1510b446873fc7dbb3da7d9d4cfe
3
+ metadata.gz: 4684e489b9b59adcca847e4c3261f98d487210b07c3573e9c147f94fd0044424
4
+ data.tar.gz: 9f3e57d74e418e25e7732822193e1b7d3fd06048c46d6d1a9da7d97a60c62a7d
5
5
  SHA512:
6
- metadata.gz: 4933f49f8868128aaecd6b6696d79cc677d8185bea9b18e3f2f7bdbafec85dfd9d055ebe8c196aad7008b52a92f5fcad53d69af5535ffad152b6c8188ebbb8bb
7
- data.tar.gz: 5f72a6c8a97623326a94e4fca096b5a836c9208e801a5f70f720d515115f074a41d35f7b4ccc801f02bb44d01e95e338826d3ce2ee39f50808ce3d8006a6709a
6
+ metadata.gz: 51eaec81b7b964cba8e6bca64c2e1b80298f650d6fcf7fdd1c0817e016f9661a437ab86f068d9a041a903d1c83b8d31212343419a807ebefd117ed5c4dfa6832
7
+ data.tar.gz: e8eb48b12eb1841bd0543f660c29dd8644cf3cfd9cedf31fcab81b51f76e8353d87bfd522197b07ad6e0622cfbfaaa4649d49eee8a6b6d242f719e2510bd4e46
data/README.md CHANGED
@@ -1,8 +1,19 @@
1
1
  # visualize_packwerk
2
2
 
3
- This gem contains rake tasks to help visualize relationships between packwerk packs.
3
+ This gem helps visualize relationships between packwerk packs.
4
4
 
5
- # Usage
5
+ ![Example of visualization](docs/example.png)
6
+
7
+ # CLI Usage
8
+ ## bin/packs
9
+ For simpler use, add `bin/packs` via `use_packs` (https://github.com/rubyatscale/use_packs)
10
+ ```
11
+ bin/packs visualize # all packs
12
+ bin/packs visualize packs/a packs/b # subset of packs
13
+ bin/packs # enter interactive mode to select what packs to visualize
14
+ ```
15
+
16
+ # Ruby API Usage
6
17
  ## Building a package graph for a selection of packages
7
18
  ```ruby
8
19
  # Select the packs you want to include
@@ -12,7 +23,7 @@ selected_packs = Packs.all.select{ |p| ['Team 1', 'Team 2'].include?(CodeOwnersh
12
23
  VisualizePackwerk.package_graph!(selected_packs)
13
24
  ```
14
25
 
15
- # Building a team graph for specific teams
26
+ ## Building a team graph for specific teams
16
27
  ```ruby
17
28
  # Select the teams you want to include
18
29
  selected_teams = CodeTeams.all
@@ -20,8 +31,5 @@ selected_teams = CodeTeams.all.select{ |t| ['Team 1', 'Team 2'].include?(t.name)
20
31
  VisualizePackwerk.team_graph!(selected_teams)
21
32
  ```
22
33
 
23
- ## bin/packs
24
- For simpler use, use `bin/packs` in `use_packwerk` (https://github.com/rubyatscale/use_packwerk)
25
-
26
34
  # Want to change something or add a feature?
27
35
  Submit a PR or post an issue!
Binary file
@@ -16,7 +16,7 @@ module VisualizePackwerk
16
16
  sig { params(package_nodes: T::Set[PackageNode]).void }
17
17
  def initialize(package_nodes:)
18
18
  @package_nodes = package_nodes
19
- @index_by_name = T.let({}, T::Hash[String, PackageNode])
19
+ @index_by_name = T.let({}, T::Hash[String, T.nilable(PackageNode)])
20
20
  end
21
21
 
22
22
  sig { returns(PackageGraph) }
@@ -46,9 +46,9 @@ module VisualizePackwerk
46
46
  PackageGraph.new(package_nodes: package_nodes)
47
47
  end
48
48
 
49
- sig { params(name: String).returns(PackageNode) }
49
+ sig { params(name: String).returns(T.nilable(PackageNode)) }
50
50
  def package_by_name(name)
51
- @index_by_name[name] ||= T.must(package_nodes.find { |node| node.name == name })
51
+ @index_by_name[name] ||= package_nodes.find { |node| node.name == name }
52
52
  end
53
53
  end
54
54
  end
@@ -45,7 +45,15 @@ module VisualizePackwerk
45
45
  # https://graphviz.org/docs/layouts/
46
46
  default_layout = :dot
47
47
  # other_layout = :sfdp
48
- graphviz_graph = GraphViz.new(:G, type: :digraph, dpi: 100, layout: default_layout)
48
+ graphviz_graph = GraphViz.new(
49
+ :G,
50
+ type: :digraph,
51
+ dpi: 100,
52
+ layout: default_layout,
53
+ label: "Visualization of #{node_names.count} packs, generated using `bin/packs`",
54
+ fontsize: 24,
55
+ labelloc: "t",
56
+ )
49
57
 
50
58
  # Create graph nodes
51
59
  graphviz_nodes = T.let({}, T::Hash[String, GraphViz::Node])
@@ -56,36 +64,32 @@ module VisualizePackwerk
56
64
  graphviz_nodes[node.name] = add_node(node, graphviz_graph)
57
65
  end
58
66
 
59
- max_edge_width = 10
60
-
61
67
  # Draw all edges
62
68
  nodes_to_draw.each do |node|
63
69
  node.dependencies.each do |to_node|
64
70
  next unless node_names.include?(to_node)
65
71
 
66
- graphviz_graph.add_edges(
67
- graphviz_nodes[node.name],
68
- graphviz_nodes[to_node],
69
- { color: 'darkgreen' }
72
+ add_dependency(
73
+ graph: graphviz_graph,
74
+ node1: T.must(graphviz_nodes[node.name]),
75
+ node2: T.must(graphviz_nodes[to_node]),
70
76
  )
71
77
  end
72
78
 
73
79
  node.violations_by_node_name.each do |to_node_name, violation_count|
74
80
  next unless node_names.include?(to_node_name)
75
81
 
76
- edge_width = [
77
- [(violation_count / 5).to_i, 1].max, # rubocop:disable Lint/NumberConversion
78
- max_edge_width,
79
- ].min
80
-
81
- graphviz_graph.add_edges(
82
- graphviz_nodes[node.name],
83
- graphviz_nodes[to_node_name],
84
- { color: 'red', penwidth: edge_width }
82
+ add_violation(
83
+ graph: graphviz_graph,
84
+ node1: T.must(graphviz_nodes[node.name]),
85
+ node2: T.must(graphviz_nodes[to_node_name]),
86
+ violation_count: violation_count
85
87
  )
86
88
  end
87
89
  end
88
90
 
91
+ add_legend(graphviz_graph)
92
+
89
93
  # Save graph to filesystem
90
94
  puts "Outputting to: #{OUTPUT_FILENAME}"
91
95
  graphviz_graph.output(png: OUTPUT_FILENAME)
@@ -106,6 +110,60 @@ module VisualizePackwerk
106
110
 
107
111
  graph.add_nodes(node.name, **node_options)
108
112
  end
113
+
114
+ sig { params(graph: GraphViz).void }
115
+ def add_legend(graph)
116
+ legend = graph.add_graph("legend")
117
+
118
+ # This commented out code was used to generate an image that I edited by hand.
119
+ # I was unable to figure out how to:
120
+ # - put a box around the legend
121
+ # - layout the node pairs in vertical order
122
+ # - give it a title
123
+ # So I just generated this using graphviz and then pulled the image in.
124
+ # a_node = legend.add_nodes("packs/a")
125
+ # b_node = legend.add_nodes("packs/b")
126
+ # c_node = legend.add_nodes("packs/c")
127
+ # d_node = legend.add_nodes("packs/d")
128
+ # e_node = legend.add_nodes("packs/e")
129
+ # f_node = legend.add_nodes("packs/f")
130
+
131
+ # add_dependency(graph: legend, node1: a_node, node2: b_node, label: 'Dependency in package.yml')
132
+ # add_violation(graph: legend, node1: c_node, node2: d_node, violation_count: 1, label: 'Violations (few)')
133
+ # add_violation(graph: legend, node1: e_node, node2: f_node, violation_count: 30, label: 'Violations (many)')
134
+
135
+ image = legend.add_node("",
136
+ shape: "image",
137
+ image: Pathname.new(__dir__).join("./legend.png").to_s,
138
+ )
139
+ end
140
+
141
+ sig { params(graph: GraphViz, node1: GraphViz::Node, node2: GraphViz::Node, violation_count: Integer, label: T.nilable(String)).void }
142
+ def add_violation(graph:, node1:, node2:, violation_count:, label: nil)
143
+ max_edge_width = 10
144
+
145
+ edge_width = [
146
+ [(violation_count / 5).to_i, 1].max, # rubocop:disable Lint/NumberConversion
147
+ max_edge_width,
148
+ ].min
149
+
150
+ opts = { color: 'red', style: 'dashed', penwidth: edge_width }
151
+ if label
152
+ opts.merge!(label: label)
153
+ end
154
+
155
+ graph.add_edges(node1, node2, opts)
156
+ end
157
+
158
+ sig { params(graph: GraphViz, node1: GraphViz::Node, node2: GraphViz::Node, label: T.nilable(String)).void }
159
+ def add_dependency(graph:, node1:, node2:, label: nil)
160
+ opts = { color: 'darkgreen' }
161
+ if label
162
+ opts.merge!(label: label)
163
+ end
164
+
165
+ graph.add_edges(node1, node2, opts)
166
+ end
109
167
  end
110
168
 
111
169
  private_constant :PackageRelationships
@@ -26,7 +26,9 @@ module VisualizePackwerk
26
26
  package_nodes_for_team.map(&:violations_by_package).each do |new_violations_by_package|
27
27
  new_violations_by_package.each do |pack_name, count|
28
28
  # We first get the pack owner of the violated package
29
- other_team = package_graph.package_by_name(pack_name).team_name
29
+ other_package = package_graph.package_by_name(pack_name)
30
+ next if other_package.nil?
31
+ other_team = other_package.team_name
30
32
  violations_by_team[other_team] ||= 0
31
33
  # Then we add the violations on that team together
32
34
  # TODO: We may want to ignore this if team == other_team to avoid arrows pointing to self, but maybe not!
@@ -36,7 +38,9 @@ module VisualizePackwerk
36
38
 
37
39
  dependencies = Set.new
38
40
  package_nodes_for_team.map(&:dependencies).reduce(Set.new, :+).each do |dependency|
39
- dependencies << package_graph.package_by_name(dependency).team_name
41
+ other_pack = package_graph.package_by_name(dependency)
42
+ next if other_pack.nil?
43
+ dependencies << other_pack.team_name
40
44
  end
41
45
 
42
46
  team_nodes << TeamNode.new(
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: visualize_packwerk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.1
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-04-09 00:00:00.000000000 Z
11
+ date: 2023-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sorbet-runtime
@@ -160,6 +160,7 @@ files:
160
160
  - README.md
161
161
  - lib/visualize_packwerk.rb
162
162
  - lib/visualize_packwerk/graph_interface.rb
163
+ - lib/visualize_packwerk/legend.png
163
164
  - lib/visualize_packwerk/node_interface.rb
164
165
  - lib/visualize_packwerk/package_graph.rb
165
166
  - lib/visualize_packwerk/package_node.rb