gitrob 0.0.6 → 1.0.0

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.
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