repo_dependency_graph 0.1.5 → 0.2.0

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
  SHA1:
3
- metadata.gz: 578cc8fe75735e91415c393a959f0aad7df3a510
4
- data.tar.gz: f2201b99a11c80a5d7d8d7b870c416dc1181c99b
3
+ metadata.gz: 2f7e2fef071a05082e7a36ae97805147a46337b2
4
+ data.tar.gz: cc00557bde2101f361a08113394076aaa95f36ae
5
5
  SHA512:
6
- metadata.gz: 6245f4ab5d69260041e10ec63614c78d5079eae348f19450d444f3a8bf2a7772daa8a40e5dd14791ed538cf2659766333ed4d057943257e75fd36bc928adc4c6
7
- data.tar.gz: 9d3d92fe452b30a8d38cf7c23ab17345b60542dd58fafd53329883ff6107d0f1194fb3bea2fc5d64a658b68aebe27937f87e16516706a6c3603be610cd12c4e3
6
+ metadata.gz: 41150cb68c3c85cbe4b07b0ff113549b533a89aed9958ef354fd347f4efd9c14f5526f1106f6d27dfc6afdaada8c0fb7a0aa05db7824318a0210326bd6a632dc
7
+ data.tar.gz: 8c2577f04f2d56c2930181ff221ddb59bd561c101fcd1068c9f0f2f953818225b204fa42814bc819de5ee40bddfe63a4945e40dc5792aea4fa53e7e6fe3f5183
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
- require "rubygems"
3
2
  require "optparse"
4
3
 
5
4
  $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
6
- require "repo_dependency_graph"
5
+ require "repo_dependency_graph/cli"
7
6
 
8
- exit RepoDependencyGraph.run(ARGV)
7
+ exit RepoDependencyGraph::CLI.run(ARGV)
@@ -0,0 +1,62 @@
1
+ require 'repo_dependency_graph'
2
+ require 'repo_dependency_graph/output'
3
+
4
+ module RepoDependencyGraph
5
+ module CLI
6
+ class << self
7
+ def run(argv)
8
+ options = parse_options(argv)
9
+ RepoDependencyGraph::Output.draw(
10
+ RepoDependencyGraph.dependencies(options), options
11
+ )
12
+ 0
13
+ end
14
+
15
+ private
16
+
17
+ def parse_options(argv)
18
+ options = {
19
+ :user => git_config("github.user")
20
+ }
21
+ OptionParser.new do |opts|
22
+ opts.banner = <<-BANNER.gsub(/^ /, "")
23
+ Draw repo dependency graph from your organization
24
+
25
+ Usage:
26
+ repo-dependency-graph
27
+
28
+ Options:
29
+ BANNER
30
+ opts.on("--token TOKEN", "Use token") { |token| options[:token] = token }
31
+ opts.on("--user USER", "Use user") { |user| options[:user] = user }
32
+ opts.on("--draw TYPE", "png, html, table (default: png)") { |draw| options[:draw] = draw }
33
+ opts.on("--organization ORGANIZATION", "Use organization") { |organization| options[:organization] = organization }
34
+ opts.on("--private", "Only show private repos") { options[:private] = true }
35
+ opts.on("--external", "Also include external projects in graph (can get super-messy)") { options[:external] = true }
36
+ opts.on("--map SEARCH=REPLACE", "Replace in project name to find them as internal: 'foo=bar' -> replace foo in repo names to bar") do |map|
37
+ options[:map] = map.split("=")
38
+ options[:map][0] = Regexp.new(options[:map][0])
39
+ options[:map][1] = options[:map][1].to_s
40
+ end
41
+ opts.on("--only TYPE", String, "Only this type (chef,gem), default: all") { |t| options[:only] = t }
42
+ opts.on("--select REGEX", "Only include repos with matching names") { |regex| options[:select] = Regexp.new(regex) }
43
+ opts.on("--reject REGEX", "Exclude repos with matching names") { |regex| options[:reject] = Regexp.new(regex) }
44
+ opts.on("-h", "--help", "Show this.") { puts opts; exit }
45
+ opts.on("-v", "--version", "Show Version"){ puts RepoDependencyGraph::VERSION; exit}
46
+ end.parse!(argv)
47
+
48
+ options[:token] ||= begin
49
+ token = `git config github.token`.strip
50
+ token if $?.success?
51
+ end
52
+
53
+ options
54
+ end
55
+
56
+ def git_config(thing)
57
+ result = `git config #{thing}`.strip
58
+ result.empty? ? nil : result
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,155 @@
1
+ module RepoDependencyGraph
2
+ module Output
3
+ MAX_HEX = 255
4
+
5
+ class << self
6
+ def draw(dependencies, options)
7
+ case options[:draw]
8
+ when "html"
9
+ draw_js(dependencies)
10
+ when "table"
11
+ draw_table(dependencies)
12
+ else
13
+ draw_png(dependencies)
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def draw_js(dependencies)
20
+ nodes, edges = convert_to_graphviz(dependencies)
21
+ html = <<-HTML.gsub(/^ /, "")
22
+ <!doctype html>
23
+ <html>
24
+ <head>
25
+ <title>Network</title>
26
+ <style>
27
+ #mynetwork {
28
+ width: 2000px;
29
+ height: 2000px;
30
+ border: 1px solid lightgray;
31
+ background: #F3F3F3;
32
+ }
33
+ </style>
34
+
35
+ <script type="text/javascript" src="http://visjs.org/dist/vis.js"></script>
36
+ <link href="http://visjs.org/dist/vis.css" rel="stylesheet" type="text/css" />
37
+
38
+ <script type="text/javascript">
39
+ var nodes = null;
40
+ var edges = null;
41
+ var network = null;
42
+
43
+ function draw() {
44
+ nodes = #{nodes.values.to_json};
45
+ edges = #{edges.to_json};
46
+
47
+ var container = document.getElementById('mynetwork');
48
+ var data = {
49
+ nodes: nodes,
50
+ edges: edges
51
+ };
52
+ var options = {stabilize: false};
53
+
54
+ new vis.Network(container, data, options);
55
+ }
56
+ </script>
57
+ </head>
58
+
59
+ <body onload="draw()">
60
+ <div id="mynetwork"></div>
61
+ </body>
62
+ </html>
63
+ HTML
64
+ File.write("out.html", html)
65
+ end
66
+
67
+ def draw_table(dependencies)
68
+ tables = dependencies.map do |name, uses|
69
+ used = dependencies.map do |d, uses|
70
+ used = uses.detect { |d| d.first == name }
71
+ [d, used.last] if used
72
+ end.compact
73
+ size = [used.size, uses.size, 1].max
74
+ table = []
75
+ size.times do |i|
76
+ table[i] = [
77
+ (used[i] || []).join(": "),
78
+ (name if i == 0),
79
+ (uses[i] || []).join(": ")
80
+ ]
81
+ end
82
+ table.unshift ["Used", "", "Uses"]
83
+ table
84
+ end
85
+ tables.map! { |t| "<table>\n#{t.map { |t| "<tr>#{t.map { |t| "<td>#{t}</td>" }.join("")}</tr>" }.join("\n")}\n</table>" }
86
+
87
+ html = <<-HTML.gsub(/^ /, "")
88
+ <!doctype html>
89
+ <html>
90
+ <head>
91
+ <title>Network</title>
92
+ <style>
93
+ table { width: 600px; }
94
+ </style>
95
+ </head>
96
+ <body>
97
+ #{tables.join("<br>\n<br>\n")}
98
+ </body>
99
+ </html>
100
+ HTML
101
+ File.write("out.html", html)
102
+ end
103
+
104
+ def draw_png(dependencies)
105
+ nodes, edges = convert_to_graphviz(dependencies)
106
+ require 'graphviz'
107
+ g = GraphViz.new(:G, :type => :digraph)
108
+
109
+ nodes = Hash[nodes.map do |_, data|
110
+ node = g.add_node(data[:id], :color => data[:color], :style => "filled")
111
+ [data[:id], node]
112
+ end]
113
+
114
+ edges.each do |edge|
115
+ g.add_edge(nodes[edge[:from]], nodes[edge[:to]], :label => edge[:label])
116
+ end
117
+
118
+ g.output(:png => "out.png")
119
+ end
120
+
121
+ def convert_to_graphviz(dependencies)
122
+ counts = dependency_counts(dependencies)
123
+ range = counts.values.min..counts.values.max
124
+ nodes = Hash[counts.each_with_index.map do |(name, count), i|
125
+ [name, {:id => name, :color => color(count, range)}]
126
+ end]
127
+ edges = dependencies.map do |name, dependencies|
128
+ dependencies.map do |dependency, version|
129
+ {:from => nodes[name][:id], :to => nodes[dependency][:id], :label => (version || '')}
130
+ end
131
+ end.flatten
132
+ [nodes, edges]
133
+ end
134
+
135
+
136
+ def color(value, range)
137
+ value -= range.min # lowest -> green
138
+ max = range.max - range.min
139
+
140
+ i = (value * MAX_HEX / max);
141
+ i *= 0.6 # green-blue gradient instead of green-green
142
+ half = MAX_HEX * 0.5
143
+ values = [0,2,4].map { |v| (Math.sin(0.024 * i + v) * half + half).round.to_s(16).rjust(2, "0") }
144
+ "##{values.join}"
145
+ end
146
+
147
+ def dependency_counts(dependencies)
148
+ all = (dependencies.keys + dependencies.values.map { |v| v.map(&:first) }).flatten.uniq
149
+ Hash[all.map do |k|
150
+ [k, dependencies.values.map(&:first).count { |name, _| name == k } ]
151
+ end]
152
+ end
153
+ end
154
+ end
155
+ end
@@ -1,3 +1,3 @@
1
1
  module RepoDependencyGraph
2
- VERSION = "0.1.5"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -4,175 +4,6 @@ require "bundler" # get all dependency for lockfile_parser
4
4
 
5
5
  module RepoDependencyGraph
6
6
  class << self
7
- MAX_HEX = 255
8
-
9
- def run(argv)
10
- options = parse_options(argv)
11
- draw(dependencies(options), options)
12
- 0
13
- end
14
-
15
- private
16
-
17
- def parse_options(argv)
18
- options = {
19
- :user => git_config("github.user")
20
- }
21
- OptionParser.new do |opts|
22
- opts.banner = <<-BANNER.gsub(/^ {10}/, "")
23
- Draw repo dependency graph from your organization
24
-
25
- Usage:
26
- repo-dependency-graph
27
-
28
- Options:
29
- BANNER
30
- opts.on("--token TOKEN", "Use token") { |token| options[:token] = token }
31
- opts.on("--user USER", "Use user") { |user| options[:user] = user }
32
- opts.on("--draw TYPE", "png, html, table (default: png)") { |draw| options[:draw] = draw }
33
- opts.on("--organization ORGANIZATION", "Use organization") { |organization| options[:organization] = organization }
34
- opts.on("--private", "Only show private repos") { options[:private] = true }
35
- opts.on("--external", "Also include external projects in graph (can get super-messy)") { options[:external] = true }
36
- opts.on("--map SEARCH=REPLACE", "Replace in project name to find them as internal: 'foo=bar' -> replace foo in repo names to bar") do |map|
37
- options[:map] = map.split("=")
38
- options[:map][0] = Regexp.new(options[:map][0])
39
- options[:map][1] = options[:map][1].to_s
40
- end
41
- opts.on("--chef", "Parse chef metadata.rb files") { options[:chef] = true }
42
- opts.on("--select REGEX", "Only include repos with matching names") { |regex| options[:select] = Regexp.new(regex) }
43
- opts.on("--reject REGEX", "Exclude repos with matching names") { |regex| options[:reject] = Regexp.new(regex) }
44
- opts.on("-h", "--help", "Show this.") { puts opts; exit }
45
- opts.on("-v", "--version", "Show Version"){ puts RepoDependencyGraph::VERSION; exit}
46
- end.parse!(argv)
47
-
48
- options[:token] ||= begin
49
- token = `git config github.token`.strip
50
- token if $?.success?
51
- end
52
-
53
- options
54
- end
55
-
56
- def git_config(thing)
57
- result = `git config #{thing}`.strip
58
- result.empty? ? nil : result
59
- end
60
-
61
- def draw(dependencies, options)
62
- case options[:draw]
63
- when "html"
64
- nodes, edges = convert_to_graphviz(dependencies)
65
- html = <<-HTML.gsub(/^ /, "")
66
- <!doctype html>
67
- <html>
68
- <head>
69
- <title>Network</title>
70
- <style>
71
- #mynetwork {
72
- width: 2000px;
73
- height: 2000px;
74
- border: 1px solid lightgray;
75
- background: #F3F3F3;
76
- }
77
- </style>
78
-
79
- <script type="text/javascript" src="http://visjs.org/dist/vis.js"></script>
80
- <link href="http://visjs.org/dist/vis.css" rel="stylesheet" type="text/css" />
81
-
82
- <script type="text/javascript">
83
- var nodes = null;
84
- var edges = null;
85
- var network = null;
86
-
87
- function draw() {
88
- nodes = #{nodes.values.to_json};
89
- edges = #{edges.to_json};
90
-
91
- var container = document.getElementById('mynetwork');
92
- var data = {
93
- nodes: nodes,
94
- edges: edges
95
- };
96
- var options = {stabilize: false};
97
-
98
- new vis.Network(container, data, options);
99
- }
100
- </script>
101
- </head>
102
-
103
- <body onload="draw()">
104
- <div id="mynetwork"></div>
105
- </body>
106
- </html>
107
- HTML
108
- File.write("out.html", html)
109
- when "table"
110
- tables = dependencies.map do |name, uses|
111
- used = dependencies.map do |d, uses|
112
- used = uses.detect { |d| d.first == name }
113
- [d, used.last] if used
114
- end.compact
115
- size = [used.size, uses.size, 1].max
116
- table = []
117
- size.times do |i|
118
- table[i] = [
119
- (used[i] || []).join(": "),
120
- (name if i == 0),
121
- (uses[i] || []).join(": ")
122
- ]
123
- end
124
- table.unshift ["Used", "", "Uses"]
125
- table
126
- end
127
- tables.map!{ |t| "<table>\n#{t.map{|t| "<tr>#{t.map{|t| "<td>#{t}</td>" }.join("")}</tr>" }.join("\n")}\n</table>" }
128
-
129
- html = <<-HTML.gsub(/^ /, "")
130
- <!doctype html>
131
- <html>
132
- <head>
133
- <title>Network</title>
134
- <style>
135
- table { width: 600px; }
136
- </style>
137
- </head>
138
- <body>
139
- #{tables.join("<br>\n<br>\n")}
140
- </body>
141
- </html>
142
- HTML
143
- File.write("out.html", html)
144
- else
145
- nodes, edges = convert_to_graphviz(dependencies)
146
- require 'graphviz'
147
- g = GraphViz.new(:G, :type => :digraph)
148
-
149
- nodes = Hash[nodes.map do |_, data|
150
- node = g.add_node(data[:id], :color => data[:color], :style => "filled")
151
- [data[:id], node]
152
- end]
153
-
154
- edges.each do |edge|
155
- g.add_edge(nodes[edge[:from]], nodes[edge[:to]], :label => edge[:label])
156
- end
157
-
158
- g.output(:png => "out.png")
159
- end
160
- end
161
-
162
- def convert_to_graphviz(dependencies)
163
- counts = dependency_counts(dependencies)
164
- range = counts.values.min..counts.values.max
165
- nodes = Hash[counts.each_with_index.map do |(name, count), i|
166
- [name, {:id => name, :color => color(count, range)}]
167
- end]
168
- edges = dependencies.map do |name, dependencies|
169
- dependencies.map do |dependency, version|
170
- {:from => nodes[name][:id], :to => nodes[dependency][:id], :label => (version || '')}
171
- end
172
- end.flatten
173
- [nodes, edges]
174
- end
175
-
176
7
  def dependencies(options)
177
8
  if options[:map] && options[:external]
178
9
  raise ArgumentError, "Map only makes sense when searching for internal repos"
@@ -187,7 +18,7 @@ module RepoDependencyGraph
187
18
  possible.map! { |p| p.sub(options[:map][0], options[:map][1].to_s) } if options[:map]
188
19
 
189
20
  dependencies = all.map do |repo|
190
- found = dependent_repos(repo, options) || []
21
+ found = dependent_repos(repo, options)
191
22
  found.select! { |f| possible.include?(f.first) } unless options[:external]
192
23
  next if found.empty?
193
24
  puts "#{repo.name}: #{found.map { |n,v| "#{n}: #{v}" }.join(", ")}"
@@ -196,13 +27,19 @@ module RepoDependencyGraph
196
27
  Hash[dependencies]
197
28
  end
198
29
 
30
+ private
31
+
199
32
  def dependent_repos(repo, options)
200
- if options[:chef]
33
+ repos = []
34
+
35
+ if !options[:only] || options[:only] == "chef"
201
36
  if content = repo.content("metadata.rb")
202
- scan_chef_metadata(content)
37
+ repos.concat scan_chef_metadata(content)
203
38
  end
204
- else
205
- if repo.gem? && spec = load_spec(repo.gemspec_content)
39
+ end
40
+
41
+ if !options[:only] || options[:only] == "gem"
42
+ gems = if repo.gem? && spec = load_spec(repo.gemspec_content)
206
43
  spec.runtime_dependencies.map do |d|
207
44
  r = d.requirement.to_s
208
45
  r = nil if r == ">= 0"
@@ -213,7 +50,10 @@ module RepoDependencyGraph
213
50
  elsif content = repo.content("Gemfile")
214
51
  scan_gemfile(content)
215
52
  end
53
+ repos.concat gems if gems
216
54
  end
55
+
56
+ repos
217
57
  end
218
58
 
219
59
  def scan_chef_metadata(content)
@@ -239,23 +79,5 @@ module RepoDependencyGraph
239
79
  $stderr.puts "Error when parsing content:\n#{content}\n\n#{$!}"
240
80
  nil
241
81
  end
242
-
243
- def color(value, range)
244
- value -= range.min # lowest -> green
245
- max = range.max - range.min
246
-
247
- i = (value * MAX_HEX / max);
248
- i *= 0.6 # green-blue gradient instead of green-green
249
- half = MAX_HEX * 0.5
250
- values = [0,2,4].map { |v| (Math.sin(0.024 * i + v) * half + half).round.to_s(16).rjust(2, "0") }
251
- "##{values.join}"
252
- end
253
-
254
- def dependency_counts(dependencies)
255
- all = (dependencies.keys + dependencies.values.map { |v| v.map(&:first) }).flatten.uniq
256
- Hash[all.map do |k|
257
- [k, dependencies.values.map(&:first).count { |name, _| name == k } ]
258
- end]
259
- end
260
82
  end
261
83
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: repo_dependency_graph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Grosser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-06 00:00:00.000000000 Z
11
+ date: 2014-09-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: organization_audit
@@ -33,6 +33,8 @@ extra_rdoc_files: []
33
33
  files:
34
34
  - bin/repo-dependency-graph
35
35
  - lib/repo_dependency_graph.rb
36
+ - lib/repo_dependency_graph/cli.rb
37
+ - lib/repo_dependency_graph/output.rb
36
38
  - lib/repo_dependency_graph/version.rb
37
39
  homepage: http://github.com/grosser/repo_dependency_graph
38
40
  licenses: