gitrob 0.0.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +30 -7
  3. data/.rspec +0 -1
  4. data/.rubocop.yml +55 -0
  5. data/.travis.yml +4 -0
  6. data/CHANGELOG.md +42 -0
  7. data/CONTRIBUTING.md +137 -9
  8. data/Gemfile +11 -1
  9. data/Guardfile +42 -0
  10. data/LICENSE.txt +17 -18
  11. data/README.md +79 -29
  12. data/Rakefile +6 -0
  13. data/bin/console +34 -0
  14. data/bin/setup +7 -0
  15. data/db/migrations/001_create_assessments.rb +19 -0
  16. data/db/migrations/002_create_github_access_tokens.rb +11 -0
  17. data/db/migrations/003_create_owners.rb +24 -0
  18. data/db/migrations/004_create_repositories.rb +23 -0
  19. data/db/migrations/005_create_blobs.rb +16 -0
  20. data/db/migrations/006_create_flags.rb +13 -0
  21. data/db/migrations/007_create_comparisons.rb +17 -0
  22. data/db/migrations/008_create_blobs_comparisons.rb +8 -0
  23. data/db/migrations/009_create_comparisons_repositories.rb +8 -0
  24. data/db/migrations/010_create_comparisons_owners.rb +8 -0
  25. data/exe/gitrob +6 -0
  26. data/gitrob.gemspec +25 -18
  27. data/lib/gitrob/blob_observer.rb +103 -0
  28. data/lib/gitrob/cli/command.rb +58 -0
  29. data/lib/gitrob/cli/commands/accept_terms_of_use.rb +61 -0
  30. data/lib/gitrob/cli/commands/analyze/analysis.rb +75 -0
  31. data/lib/gitrob/cli/commands/analyze/gathering.rb +101 -0
  32. data/lib/gitrob/cli/commands/analyze.rb +63 -0
  33. data/lib/gitrob/cli/commands/banner.rb +25 -0
  34. data/lib/gitrob/cli/commands/configure.rb +123 -0
  35. data/lib/gitrob/cli/commands/server.rb +21 -0
  36. data/lib/gitrob/cli/progress_bar.rb +47 -0
  37. data/lib/gitrob/cli.rb +213 -0
  38. data/lib/gitrob/github/client_manager.rb +46 -0
  39. data/lib/gitrob/github/data_manager.rb +121 -0
  40. data/lib/gitrob/jobs/assessment.rb +12 -0
  41. data/lib/gitrob/jobs/comparison.rb +55 -0
  42. data/lib/gitrob/models/assessment.rb +96 -0
  43. data/lib/gitrob/models/blob.rb +50 -0
  44. data/lib/gitrob/models/comparison.rb +15 -0
  45. data/lib/gitrob/models/flag.rb +15 -0
  46. data/lib/gitrob/models/github_access_token.rb +17 -0
  47. data/lib/gitrob/models/owner.rb +23 -0
  48. data/lib/gitrob/models/repository.rb +20 -0
  49. data/lib/gitrob/utils.rb +19 -0
  50. data/lib/gitrob/version.rb +1 -1
  51. data/lib/gitrob/web_app.rb +292 -0
  52. data/lib/gitrob.rb +30 -113
  53. data/public/css/bootstrap.min.css +11 -0
  54. data/public/css/main.css +130 -0
  55. data/public/css/tomorrow-night.css +75 -0
  56. data/public/fonts/glyphicons-halflings-regular.eot +0 -0
  57. data/public/fonts/glyphicons-halflings-regular.svg +273 -214
  58. data/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  59. data/public/fonts/glyphicons-halflings-regular.woff +0 -0
  60. data/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
  61. data/public/images/blob_spinner.gif +0 -0
  62. data/public/images/gear_spinner.gif +0 -0
  63. data/public/js/bootstrap.min.js +7 -0
  64. data/public/js/highlight.pack.js +2 -0
  65. data/public/js/highlight.worker.js +13 -0
  66. data/public/js/jquery-2.1.4.min.js +4 -0
  67. data/public/js/main.js +239 -0
  68. data/public/robots.txt +2 -0
  69. data/signatures.json +541 -0
  70. data/views/assessments/_assessments.erb +57 -0
  71. data/views/assessments/_comparable_assessments.erb +38 -0
  72. data/views/assessments/_comparisons.erb +111 -0
  73. data/views/assessments/compare.erb +22 -0
  74. data/views/assessments/findings.erb +55 -0
  75. data/views/assessments/repositories.erb +35 -0
  76. data/views/assessments/show.erb +1 -0
  77. data/views/assessments/users.erb +46 -0
  78. data/views/blobs/show.erb +37 -0
  79. data/views/comparisons/show.erb +125 -0
  80. data/views/errors/internal_server_error.erb +9 -0
  81. data/views/errors/not_found.erb +5 -0
  82. data/views/index.erb +43 -28
  83. data/views/layout.erb +38 -12
  84. data/views/repositories/show.erb +49 -0
  85. data/views/users/show.erb +54 -0
  86. metadata +217 -106
  87. data/bin/gitrob +0 -260
  88. data/lib/gitrob/github/blob.rb +0 -41
  89. data/lib/gitrob/github/http_client.rb +0 -127
  90. data/lib/gitrob/github/organization.rb +0 -99
  91. data/lib/gitrob/github/repository.rb +0 -72
  92. data/lib/gitrob/github/user.rb +0 -84
  93. data/lib/gitrob/observers/sensitive_files.rb +0 -83
  94. data/lib/gitrob/progressbar.rb +0 -52
  95. data/lib/gitrob/util.rb +0 -11
  96. data/lib/gitrob/webapp.rb +0 -76
  97. data/models/blob.rb +0 -35
  98. data/models/finding.rb +0 -14
  99. data/models/organization.rb +0 -32
  100. data/models/repo.rb +0 -22
  101. data/models/user.rb +0 -28
  102. data/patterns.json +0 -394
  103. data/public/javascripts/bootstrap.min.js +0 -7
  104. data/public/javascripts/gitrob.js +0 -75
  105. data/public/javascripts/jquery-2.1.1.min.js +0 -4
  106. data/public/javascripts/lang-apollo.js +0 -2
  107. data/public/javascripts/lang-basic.js +0 -3
  108. data/public/javascripts/lang-clj.js +0 -18
  109. data/public/javascripts/lang-css.js +0 -2
  110. data/public/javascripts/lang-dart.js +0 -3
  111. data/public/javascripts/lang-erlang.js +0 -2
  112. data/public/javascripts/lang-go.js +0 -1
  113. data/public/javascripts/lang-hs.js +0 -2
  114. data/public/javascripts/lang-lisp.js +0 -3
  115. data/public/javascripts/lang-llvm.js +0 -1
  116. data/public/javascripts/lang-lua.js +0 -2
  117. data/public/javascripts/lang-matlab.js +0 -6
  118. data/public/javascripts/lang-ml.js +0 -2
  119. data/public/javascripts/lang-mumps.js +0 -2
  120. data/public/javascripts/lang-n.js +0 -4
  121. data/public/javascripts/lang-pascal.js +0 -3
  122. data/public/javascripts/lang-proto.js +0 -1
  123. data/public/javascripts/lang-r.js +0 -2
  124. data/public/javascripts/lang-rd.js +0 -1
  125. data/public/javascripts/lang-scala.js +0 -2
  126. data/public/javascripts/lang-sql.js +0 -2
  127. data/public/javascripts/lang-tcl.js +0 -3
  128. data/public/javascripts/lang-tex.js +0 -1
  129. data/public/javascripts/lang-vb.js +0 -2
  130. data/public/javascripts/lang-vhdl.js +0 -3
  131. data/public/javascripts/lang-wiki.js +0 -2
  132. data/public/javascripts/lang-xq.js +0 -3
  133. data/public/javascripts/lang-yaml.js +0 -2
  134. data/public/javascripts/prettify.js +0 -30
  135. data/public/javascripts/run_prettify.js +0 -34
  136. data/public/stylesheets/bootstrap.min.css +0 -7
  137. data/public/stylesheets/bootstrap.min.css.vanilla +0 -5
  138. data/public/stylesheets/gitrob.css +0 -88
  139. data/public/stylesheets/prettify.css +0 -51
  140. data/spec/lib/gitrob/observers/sensitive_files_spec.rb +0 -691
  141. data/spec/spec_helper.rb +0 -127
  142. data/views/blob.erb +0 -22
  143. data/views/organization.erb +0 -126
  144. data/views/repository.erb +0 -51
  145. data/views/user.erb +0 -51
@@ -0,0 +1,25 @@
1
+ module Gitrob
2
+ class CLI
3
+ module Commands
4
+ class Banner < Gitrob::CLI::Command
5
+ def initialize(options)
6
+ @options = options
7
+ output banner if options[:banner]
8
+ info "Starting Gitrob version #{Gitrob::VERSION} " \
9
+ "at #{Time.now.strftime('%Y-%m-%d %H:%M %Z')}"
10
+ debug "Debugging mode enabled"
11
+ end
12
+
13
+ private
14
+
15
+ def banner
16
+ " _ _ _\n" \
17
+ " ___|_| |_ ___ ___| |_\n" \
18
+ "| . | | _| _| . | . |\n" \
19
+ "|_ |_|_| |_| |___|___|\n" \
20
+ "|___|".light_blue + " By @michenriksen\n\n".light_white
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,123 @@
1
+ module Gitrob
2
+ class CLI
3
+ module Commands
4
+ class Configure < Gitrob::CLI::Command
5
+ CONFIGURATION_FILE_PATH = File.join(Dir.home, ".gitrobrc")
6
+
7
+ class ConfigurationError < StandardError; end
8
+ class ConfigurationFileNotFound < ConfigurationError; end
9
+ class ConfigurationFileNotReadable < ConfigurationError; end
10
+ class ConfigurationFileCorrupt < ConfigurationError; end
11
+
12
+ def initialize(options)
13
+ @options = options
14
+ info("Starting Gitrob configuration wizard")
15
+ return unless agree_to_overwrite?
16
+ config = gather_configuration
17
+ task("Saving configuration to #{CONFIGURATION_FILE_PATH}") do
18
+ save_configuration(config)
19
+ end
20
+ end
21
+
22
+ def self.configured?
23
+ File.exist?(CONFIGURATION_FILE_PATH)
24
+ end
25
+
26
+ def self.load_configuration!
27
+ fail ConfigurationFileNotFound \
28
+ unless File.exist?(CONFIGURATION_FILE_PATH)
29
+ fail ConfigurationFileNotReadable \
30
+ unless File.readable?(CONFIGURATION_FILE_PATH)
31
+ YAML.load(File.read(CONFIGURATION_FILE_PATH))
32
+ rescue Psych::SyntaxError
33
+ raise ConfigurationFileCorrupt
34
+ end
35
+
36
+ private
37
+
38
+ def agree_to_overwrite?
39
+ return true unless self.class.configured?
40
+ warn("Configuration file already exists\n")
41
+ agree(
42
+ "Proceed and overwrite existing configuration file? (y/n): ")
43
+ end
44
+
45
+ def gather_configuration
46
+ {
47
+ :hostname => gather_hostname,
48
+ :port => gather_port,
49
+ :username => gather_username,
50
+ :password => gather_password,
51
+ :database => gather_database,
52
+ :access_tokens => gather_access_tokens
53
+ }
54
+ end
55
+
56
+ def gather_hostname
57
+ ask("Enter PostgreSQL hostname: ") do |q|
58
+ q.default = "localhost"
59
+ end
60
+ end
61
+
62
+ def gather_port
63
+ ask("Enter PostgreSQL port: |5432| ", Integer) do |q|
64
+ q.default = 5432
65
+ q.in = 1..65_535
66
+ end
67
+ end
68
+
69
+ def gather_username
70
+ ask("Enter PostgreSQL username: ") do |q|
71
+ q.default = "gitrob"
72
+ end
73
+ end
74
+
75
+ def gather_password
76
+ ask("Enter PostgreSQL password (masked): ") do |q|
77
+ q.echo = "x"
78
+ end
79
+ end
80
+
81
+ def gather_database
82
+ ask("Enter PostgreSQL database name: ") do |q|
83
+ q.default = "gitrob"
84
+ end
85
+ end
86
+
87
+ def gather_access_tokens
88
+ tokens = []
89
+ while tokens.uniq.empty?
90
+ tokens = ask("Enter GitHub access tokens (blank line to stop):",
91
+ ->(ans) { ans =~ /[a-f0-9]{40}/ ? ans : nil }) do |q|
92
+ q.gather = ""
93
+ end
94
+ end
95
+ tokens
96
+ end
97
+
98
+ def save_configuration(config)
99
+ File.open(CONFIGURATION_FILE_PATH, "w") do |file|
100
+ file.write(build_yaml(config))
101
+ end
102
+ end
103
+
104
+ def make_connection_uri(username, password, hostname, port, database)
105
+ "postgres://#{username}:#{password}@#{hostname}:#{port}/#{database}"
106
+ end
107
+
108
+ def build_yaml(config)
109
+ YAML.dump(
110
+ "sql_connection_uri" => make_connection_uri(
111
+ config[:username],
112
+ config[:password],
113
+ config[:hostname],
114
+ config[:port],
115
+ config[:database]
116
+ ),
117
+ "github_access_tokens" => config[:access_tokens]
118
+ )
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,21 @@
1
+ module Gitrob
2
+ class CLI
3
+ module Commands
4
+ class Server < Gitrob::CLI::Command
5
+ def initialize(options)
6
+ @options = options
7
+ info "Starting web application on port #{options[:port]}..."
8
+
9
+ if debugging_enabled?
10
+ Sequel::Model.db.logger = QueryLogger.new(STDOUT)
11
+ end
12
+
13
+ Gitrob::WebApp.run!(
14
+ :port => options[:port].to_i,
15
+ :bind => options[:bind_address]
16
+ )
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,47 @@
1
+ module Gitrob
2
+ class CLI
3
+ class ProgressBar
4
+ def initialize(message, options={})
5
+ @options = {
6
+ :format =>
7
+ "#{'[*]'.light_blue} %t %c/%C %B %j% %e",
8
+ :progress_mark => "|".light_blue,
9
+ :remainder_mark => "|"
10
+ }.merge(options)
11
+ @mutex = Mutex.new
12
+ Gitrob::CLI.info(message)
13
+ @progress_bar = ::ProgressBar.create(@options)
14
+ end
15
+
16
+ def finish
17
+ progress_bar.finish
18
+ end
19
+
20
+ def info(message)
21
+ progress_bar.log("#{'[+]'.light_blue} #{message}")
22
+ end
23
+
24
+ def error(message)
25
+ progress_bar.log("#{'[!]'.light_red} #{message}")
26
+ end
27
+
28
+ def warn(message)
29
+ progress_bar.log("#{'[!]'.light_yellow} #{message}")
30
+ end
31
+
32
+ def method_missing(method, *args, &block)
33
+ if progress_bar.respond_to?(method)
34
+ progress_bar.send(method, *args, &block)
35
+ else
36
+ super
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def progress_bar
43
+ @mutex.synchronize { @progress_bar }
44
+ end
45
+ end
46
+ end
47
+ end
data/lib/gitrob/cli.rb ADDED
@@ -0,0 +1,213 @@
1
+ module Gitrob
2
+ class QueryLogger < Logger
3
+ def format_message(_severity, _timestamp, _progname, msg)
4
+ "#{msg}\n".cyan
5
+ end
6
+ end
7
+
8
+ class CLI < Thor
9
+ HELP_COMMANDS = %w(help h --help -h)
10
+ package_name "Gitrob"
11
+
12
+ attr_reader :configuration
13
+
14
+ DB_MIGRATIONS_PATH = File.expand_path(
15
+ "../../../db/migrations", __FILE__
16
+ )
17
+
18
+ class_option :bind_address,
19
+ :type => :string,
20
+ :banner => "ADDRESS",
21
+ :default => "127.0.0.1",
22
+ :desc => "Address to bind web server to"
23
+ class_option :port,
24
+ :type => :numeric,
25
+ :default => 9393,
26
+ :desc => "Port to run web server on"
27
+ class_option :access_tokens,
28
+ :type => :array,
29
+ :banner => "TOKENS",
30
+ :desc => "GitHub API tokens to use " \
31
+ "instead of what has been configured"
32
+ class_option :color,
33
+ :type => :boolean,
34
+ :default => true,
35
+ :desc => "Colorize or don't colorize output"
36
+ class_option :banner,
37
+ :type => :boolean,
38
+ :default => true,
39
+ :desc => "Show or don't show Gitrob banner"
40
+ class_option :debug,
41
+ :type => :boolean,
42
+ :default => false,
43
+ :desc => "Show or don't show debugging information"
44
+
45
+ def initialize(*args)
46
+ super
47
+ self.class.enable_debugging if options[:debug]
48
+ String.disable_colorization(!options[:color])
49
+ return if help_command?
50
+ banner
51
+ configure unless configured?
52
+ load_configuration
53
+ prepare_database
54
+ end
55
+
56
+ desc "analyze TARGETS", "Analyze one or more organizations or users"
57
+ option :title,
58
+ :type => :string,
59
+ :desc => "Give assessment a custom title"
60
+ option :threads,
61
+ :type => :numeric,
62
+ :default => 5,
63
+ :desc => "Number of threads to use"
64
+ option :server,
65
+ :type => :boolean,
66
+ :default => true,
67
+ :desc => "Start or don't start web server after assessment"
68
+ option :endpoint,
69
+ :type => :string,
70
+ :banner => "URL",
71
+ :default => "https://api.github.com",
72
+ :desc => "Specify a URL for a custom GitHub Enterprise API"
73
+ option :site,
74
+ :type => :string,
75
+ :banner => "URL",
76
+ :default => "https://github.com",
77
+ :desc => "Specify a URL for a custom GitHub Enterprise site"
78
+ option :verify_ssl,
79
+ :type => :boolean,
80
+ :default => true,
81
+ :desc => "Verify or don't verify SSL connection (careful here)"
82
+ def analyze(targets)
83
+ accept_tos
84
+ Gitrob::CLI::Commands::Analyze.start(targets, options)
85
+ end
86
+
87
+ desc "server", "Start web server"
88
+ def server
89
+ accept_tos
90
+ Gitrob::CLI::Commands::Server.start(options)
91
+ end
92
+
93
+ desc "configure", "Start configuration wizard"
94
+ def configure
95
+ Gitrob::CLI::Commands::Configure.start(options)
96
+ end
97
+
98
+ desc "banner", "Print Gitrob banner", :hide => true
99
+ def banner
100
+ Gitrob::CLI::Commands::Banner.start(options)
101
+ end
102
+
103
+ desc "accept-tos", "Accept Terms Of Use", :hide => true
104
+ def accept_tos
105
+ Gitrob::CLI::Commands::AcceptTermsOfUse.start(options)
106
+ end
107
+
108
+ no_commands do
109
+ def help_command?
110
+ !ENV["GITROB_ENV"] == "TEST" &&
111
+ (ARGV.empty? || HELP_COMMANDS.include?(ARGV.first.downcase))
112
+ end
113
+
114
+ def configured?
115
+ Gitrob::CLI::Commands::Configure.configured?
116
+ end
117
+
118
+ def load_configuration
119
+ self.class.task("Loading configuration...", true) do
120
+ @configuration = Gitrob::CLI::Commands::Configure.load_configuration!
121
+ self.class.configuration = @configuration
122
+ end
123
+ end
124
+
125
+ def prepare_database
126
+ self.class.task("Preparing database...", true) do
127
+ Sequel.extension :migration, :core_extensions
128
+ db = Sequel.connect(configuration["sql_connection_uri"])
129
+ Sequel::Migrator.run(db, DB_MIGRATIONS_PATH)
130
+ Sequel::Model.raise_on_save_failure = true
131
+ Sequel::Model.db = db
132
+ Sequel::Model.plugin :validation_helpers
133
+ Sequel::Model.plugin :timestamps
134
+ load_models
135
+ end
136
+ end
137
+
138
+ def load_models
139
+ require "gitrob/models/assessment"
140
+ require "gitrob/models/github_access_token"
141
+ require "gitrob/models/owner"
142
+ require "gitrob/models/repository"
143
+ require "gitrob/models/blob"
144
+ require "gitrob/models/flag"
145
+ require "gitrob/models/comparison"
146
+ end
147
+ end
148
+
149
+ def self.info(message)
150
+ output "[*]".light_blue + " #{message}\n"
151
+ end
152
+
153
+ def self.task(message, fatal_error=false, &block)
154
+ output "[*]".light_blue + " #{message}"
155
+ yield block
156
+ output " done\n".light_green
157
+ rescue => e
158
+ output " failed\n".light_red
159
+ output_failed_task(e, fatal_error)
160
+ end
161
+
162
+ def self.output_failed_task(exception, fatal_error)
163
+ message = "#{exception.class}: #{exception.message}"
164
+ debug exception.backtrace.join("\n")
165
+ if fatal_error
166
+ fatal message
167
+ else
168
+ error message
169
+ end
170
+ end
171
+
172
+ def self.warn(message)
173
+ output "[!]".light_yellow + " #{message}\n"
174
+ end
175
+
176
+ def self.error(message)
177
+ output "[!]".light_red + " #{message}\n"
178
+ end
179
+
180
+ def self.fatal(message)
181
+ output "[!]".light_white.on_red + " #{message}\n"
182
+ exit(1)
183
+ end
184
+
185
+ def self.debug(message)
186
+ output "[#]".light_cyan + " #{message}\n" if debugging_enabled?
187
+ end
188
+
189
+ def self.output(string)
190
+ print string
191
+ end
192
+
193
+ def self.enable_debugging
194
+ @debugging_enabled = true
195
+ end
196
+
197
+ def self.disable_debugging
198
+ @debugging_enabled = false
199
+ end
200
+
201
+ def self.debugging_enabled? # rubocop:disable Style/TrivialAccessors
202
+ @debugging_enabled
203
+ end
204
+
205
+ def self.configuration=(config)
206
+ @config = config
207
+ end
208
+
209
+ def self.configuration
210
+ @config
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,46 @@
1
+ module Gitrob
2
+ module Github
3
+ class ClientManager
4
+ USER_AGENT = "Gitrob v#{Gitrob::VERSION}"
5
+
6
+ attr_reader :clients
7
+
8
+ class NoClientsError < StandardError; end
9
+
10
+ def initialize(config)
11
+ @config = config
12
+ @mutex = Mutex.new
13
+ @clients = []
14
+ config[:access_tokens].each do |token|
15
+ clients << create_client(token)
16
+ end
17
+ end
18
+
19
+ def sample
20
+ @mutex.synchronize do
21
+ fail NoClientsError if clients.count.zero?
22
+ clients.sample
23
+ end
24
+ end
25
+
26
+ def remove(client)
27
+ @mutex.synchronize do
28
+ clients.delete(client)
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def create_client(access_token)
35
+ ::Github.new(
36
+ :oauth_token => access_token,
37
+ :endpoint => @config[:endpoint],
38
+ :site => @config[:site],
39
+ :ssl => @config[:verify_ssl],
40
+ :user_agent => USER_AGENT,
41
+ :auto_pagination => true
42
+ )
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,121 @@
1
+ module Gitrob
2
+ module Github
3
+ class DataManager
4
+ attr_reader :client_manager,
5
+ :unknown_logins,
6
+ :owners,
7
+ :repositories
8
+
9
+ def initialize(logins, client_manager)
10
+ @logins = logins
11
+ @client_manager = client_manager
12
+ @unknown_logins = []
13
+ @owners = []
14
+ @repositories = []
15
+ @repositories_for_owners = {}
16
+ @mutex = Mutex.new
17
+ end
18
+
19
+ def gather_owners(thread_pool)
20
+ @logins.each do |login|
21
+ next unless owner = get_owner(login)
22
+ @owners << owner
23
+ @repositories_for_owners[owner["login"]] = []
24
+ next unless owner["type"] == "Organization"
25
+ get_members(owner, thread_pool) if owner["type"] == "Organization"
26
+ end
27
+ @owners = @owners.uniq { |o| o["login"] }
28
+ end
29
+
30
+ def gather_repositories(thread_pool)
31
+ owners.each do |owner|
32
+ thread_pool.process do
33
+ repositories = get_repositories(owner)
34
+ with_mutex do
35
+ save_repositories(owner, repositories)
36
+ end
37
+ yield owner, repositories if block_given?
38
+ end
39
+ end
40
+ end
41
+
42
+ def repositories_for_owner(owner)
43
+ @repositories_for_owners[owner["login"]]
44
+ end
45
+
46
+ def blobs_for_repository(repository)
47
+ get_blobs(repository)
48
+ rescue ::Github::Error::Forbidden => e
49
+ # Hidden GitHub feature?
50
+ raise e unless e.message.include?("403 Repository access blocked")
51
+ []
52
+ rescue ::Github::Error::NotFound
53
+ []
54
+ rescue ::Github::Error::ServiceError => e
55
+ raise e unless e.message.include?("409 Git Repository is empty")
56
+ []
57
+ end
58
+
59
+ private
60
+
61
+ def get_owner(login)
62
+ github_client do |client|
63
+ client.users.get(:user => login)
64
+ end
65
+ rescue ::Github::Error::NotFound
66
+ @unknown_logins << login
67
+ nil
68
+ end
69
+
70
+ def get_members(org, thread_pool)
71
+ github_client do |client|
72
+ client.orgs.members.list(:org_name => org["login"]) do |owner|
73
+ thread_pool.process do
74
+ owner = get_owner(owner["login"])
75
+ with_mutex do
76
+ @owners << owner
77
+ @repositories_for_owners[owner["login"]] = []
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ def get_repositories(owner)
85
+ github_client do |client|
86
+ client.repos.list(
87
+ :user => owner["login"]).reject { |r| r["fork"] }
88
+ end
89
+ end
90
+
91
+ def get_blobs(repository)
92
+ github_client do |client|
93
+ client.get_request(
94
+ "repos/#{repository[:full_name]}/git/trees/" \
95
+ "#{repository[:default_branch]}",
96
+ ::Github::ParamsHash.new(:recursive => 1))["tree"]
97
+ .reject { |b| b["type"] != "blob" }
98
+ end
99
+ end
100
+
101
+ def github_client
102
+ client = @client_manager.sample
103
+ yield client
104
+ rescue ::Github::Error::Forbidden => e
105
+ raise e unless e.message.include?("API rate limit exceeded")
106
+ @client_manager.remove(client)
107
+ rescue ::Github::Error::Unauthorized
108
+ @client_manager.remove(client)
109
+ end
110
+
111
+ def save_repositories(owner, repositories)
112
+ @repositories += repositories
113
+ @repositories_for_owners[owner["login"]] = repositories
114
+ end
115
+
116
+ def with_mutex
117
+ @mutex.synchronize { yield }
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,12 @@
1
+ module Gitrob
2
+ module Jobs
3
+ class Assessment
4
+ include SuckerPunch::Job
5
+
6
+ def perform(targets, options)
7
+ options[:server] = false
8
+ Gitrob::CLI::Commands::Analyze.new(targets, options)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,55 @@
1
+ module Gitrob
2
+ module Jobs
3
+ class Comparison
4
+ include SuckerPunch::Job
5
+
6
+ def perform(primary_assessment, secondary_assessment)
7
+ @comparison = Gitrob::Models::Comparison.new
8
+ @primary_assessment = primary_assessment
9
+ @secondary_assessment = secondary_assessment
10
+ @comparison.primary_assessment = primary_assessment
11
+ @comparison.secondary_assessment = secondary_assessment
12
+ @comparison.save
13
+ compare_blobs
14
+ compare_repositories
15
+ compare_owners
16
+ @comparison.finished = true
17
+ @comparison.save
18
+ end
19
+
20
+ private
21
+
22
+ def compare_blobs
23
+ old_blob_shas = @secondary_assessment.blobs_dataset.select_map(:sha)
24
+ @primary_assessment.blobs_dataset.eager(:flags).all.each do |blob|
25
+ next if old_blob_shas.include?(blob.sha)
26
+ @comparison.add_blob(blob)
27
+ @comparison.blobs_count += 1
28
+ @comparison.findings_count += 1 unless blob.flags_count.zero?
29
+ end
30
+ end
31
+
32
+ def compare_repositories
33
+ old_repository_github_ids = @secondary_assessment
34
+ .repositories_dataset
35
+ .select_map(:github_id)
36
+ @primary_assessment.repositories.each do |repository|
37
+ next if old_repository_github_ids.include?(repository.github_id)
38
+ @comparison.add_repository(repository)
39
+ @comparison.repositories_count += 1
40
+ end
41
+ end
42
+
43
+ def compare_owners
44
+ old_owner_github_ids = @secondary_assessment
45
+ .owners_dataset
46
+ .select_map(:github_id)
47
+ @primary_assessment.owners.each do |owner|
48
+ next if old_owner_github_ids.include?(owner.github_id)
49
+ @comparison.add_owner(owner)
50
+ @comparison.owners_count += 1
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end