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
data/bin/gitrob DELETED
@@ -1,260 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # coding: utf-8
3
-
4
- require 'gitrob'
5
-
6
- class App
7
- include Methadone::Main
8
-
9
- leak_exceptions true
10
-
11
- main do
12
- Thread.abort_on_exception = true
13
- Paint.mode = 0 if options.include?('no-color')
14
-
15
- puts Paint[Gitrob::banner, :bright, :blue] unless options.include?('no-banner')
16
- Gitrob::status("Starting Gitrob version #{Gitrob::VERSION} at #{Time.now.strftime("%Y-%m-%d %I:%S %Z")}")
17
-
18
- if !Gitrob::agreement_accepted?
19
- puts Gitrob::agreement
20
- print Paint["\n\nDo you agree to the terms of use? [y/n]: ", :bright, :green]
21
- choice = $stdin.gets.chomp
22
- if %w{y yes}.include?(choice)
23
- Gitrob::agreement_accepted
24
- else
25
- Gitrob::fatal("Exiting Gitrob.")
26
- end
27
- end
28
-
29
- if !Gitrob::configured? || options.include?('configure')
30
- Gitrob::status("Starting Gitrob configuration wizard\n\n")
31
-
32
- hostname = ask("#{Paint[" Enter PostgreSQL hostname:", :bright, :white]} ") { |q| q.default = "localhost" }
33
- port = ask("#{Paint[" Enter PostgreSQL port:", :bright, :white]} ", Integer) { |q| q.default = 5432; q.in = 1..65535 }
34
- username = ask("#{Paint[" Enter PostgreSQL username:", :bright, :white]} ")
35
- password = ask("#{Paint[" Enter PostgreSQL password for #{username} (masked):", :bright, :white]} ") { |q| q.echo = 'x' }
36
- database = ask("#{Paint[" Enter PostgreSQL database name:", :bright, :white]} ") { |q| q.default = 'gitrob' }
37
-
38
- access_tokens = Array.new
39
- while access_tokens.uniq.empty?
40
- access_tokens = ask(Paint[" Enter GitHub access tokens (blank line to stop):", :bright, :white],
41
- lambda { |ans| ans =~ /[a-f0-9]{40}/ ? ans : nil } ) do |q|
42
- q.gather = ""
43
- end
44
- end
45
-
46
- config = {
47
- 'sql_connection_uri' => "postgres://#{username}:#{password}@#{hostname}:#{port}/#{database}",
48
- 'github_access_tokens' => access_tokens.uniq
49
- }
50
-
51
- Gitrob::task("Saving configuration to file...") do
52
- Gitrob::save_configuration!(config)
53
- end
54
-
55
- exit unless options['organization']
56
- end
57
-
58
- Gitrob::task("Loading configuration...") do
59
- Gitrob::load_configuration!
60
- end
61
-
62
- Gitrob::task("Preparing SQL database...") do
63
- Gitrob::prepare_database!
64
- end
65
-
66
- if options['reset-db']
67
- Gitrob::task("Resetting database tables...") do
68
- DataMapper::auto_migrate!
69
- end
70
- exit
71
- end
72
-
73
- if options['delete']
74
- Gitrob::delete_organization(options['delete'])
75
- exit
76
- end
77
-
78
- if options['start-server']
79
- Gitrob::status("Starting web application on http://#{options['bind-address']}:#{options['port']}/...")
80
- puts "\n\n"
81
- Gitrob::WebApp.run!(:port => options['port'].to_i, :bind => options['bind-address'])
82
- exit
83
- end
84
-
85
- org_name = options['organization']
86
- repo_count = 0
87
- members = Array.new
88
- http_client = Gitrob::Github::HttpClient.new({:access_tokens => Gitrob::configuration['github_access_tokens']})
89
- observers = Gitrob::Observers.constants.collect { |c| Gitrob::Observers::const_get(c) }
90
-
91
- Gitrob::task("Loading file patterns...") do
92
- Gitrob::Observers::SensitiveFiles::load_patterns!
93
- end
94
-
95
- begin
96
- org = Gitrob::Github::Organization.new(org_name, http_client)
97
-
98
- Gitrob::task("Collecting organization repositories...") do
99
- repo_count = org.repositories.count
100
- end
101
- rescue Gitrob::Github::HttpClient::ClientError => e
102
- if e.status == 404
103
- Gitrob::fatal("Cannot find GitHub organization with that name; exiting.")
104
- else
105
- raise e
106
- end
107
- end
108
-
109
- Gitrob::task("Collecting organization members...") do
110
- members = org.members
111
- end
112
-
113
- progress = Gitrob::ProgressBar.new("Collecting member repositories...",
114
- :total => members.count
115
- )
116
- thread_pool = Thread.pool(options['threads'].to_i)
117
-
118
- members.each do |member|
119
- thread_pool.process do
120
- if member.repositories.count > 0
121
- repo_count += member.repositories.count
122
- progress.log("Collected #{Gitrob::Util::pluralize(member.repositories.count, 'repository', 'repositories')} from #{Paint[member.username, :bright, :white]}")
123
- end
124
- progress.increment
125
- end
126
- end
127
-
128
- thread_pool.shutdown
129
-
130
- if repo_count.zero?
131
- Gitrob::fatal("Organization has no repositories to check; exiting.")
132
- end
133
-
134
- progress = Gitrob::ProgressBar.new("Processing repositories...",
135
- :total => repo_count
136
- )
137
-
138
- db_org = org.save_to_database!
139
- thread_pool = Thread.pool(options['threads'].to_i)
140
-
141
- org.repositories.each do |repo|
142
- thread_pool.process do
143
- begin
144
- if repo.contents.count > 0
145
- db_repo = repo.save_to_database!(db_org)
146
- findings = 0
147
-
148
- repo.contents.each do |blob|
149
- db_blob = blob.to_model(db_org, db_repo)
150
-
151
- observers.each do |observer|
152
- observer.observe(db_blob)
153
- end
154
-
155
- db_blob.findings.each do |f|
156
- db_blob.findings_count += 1
157
- findings += 1
158
- f.organization = db_org
159
- f.repo = db_repo
160
- end
161
-
162
- db_blob.save
163
- end
164
- progress.log("Processed #{Gitrob::Util::pluralize(repo.contents.count, 'file', 'files')} from #{Paint[repo.full_name, :bright, :white]} with #{findings.zero? ? 'no findings' : Paint[Gitrob::Util.pluralize(findings, 'finding', 'findings'), :yellow]}")
165
- end
166
- progress.increment
167
- rescue Exception => e
168
- progress.log_error("Encountered error when processing #{Paint[repo.full_name, :bright, :white]} (#{e.class.name})")
169
- progress.increment
170
- end
171
- end
172
- end
173
-
174
- org.members.each do |member|
175
- thread_pool.process do
176
- begin
177
- db_user = member.save_to_database!(db_org)
178
-
179
- member.repositories.each do |repo|
180
- if repo.contents.count > 0
181
- db_repo = repo.save_to_database!(db_org, db_user)
182
- findings = 0
183
-
184
- repo.contents.each do |blob|
185
- db_blob = blob.to_model(db_org, db_repo)
186
-
187
- observers.each do |observer|
188
- observer.observe(db_blob)
189
- end
190
-
191
- db_blob.findings.each do |f|
192
- db_blob.findings_count += 1
193
- findings += 1
194
- f.organization = db_org
195
- f.repo = db_repo
196
- f.user = db_user
197
- end
198
-
199
- db_blob.save
200
- end
201
- progress.log("Processed #{Gitrob::Util::pluralize(repo.contents.count, 'file', 'files')} from #{Paint[repo.full_name, :bright, :white]} with #{findings.zero? ? 'no findings' : Paint[Gitrob::Util.pluralize(findings, 'finding', 'findings'), :yellow]}")
202
- end
203
- progress.increment
204
- end
205
- rescue Exception => e
206
- progress.log_error("Encountered error when processing #{Paint[repo.full_name, :bright, :white]} (#{e.class.name})")
207
- progress.increment
208
- end
209
- end
210
- end
211
-
212
- thread_pool.shutdown
213
-
214
- if !options.include?('no-server')
215
- Gitrob::status("Starting web application on port #{options['port']}...")
216
- Gitrob::status("Browse to http://#{options['bind-address']}:#{options['port']}/ to see results!")
217
- puts "\n\n"
218
- Gitrob::WebApp.run!(:port => options[:port].to_i, :bind => options['bind-address'])
219
- exit
220
- end
221
- end
222
-
223
- version Gitrob::VERSION
224
- description "Reconnaissance tool for GitHub organizations."
225
-
226
- options['bind-address'] = '127.0.0.1'
227
- options['port'] = 9393
228
- options['threads'] = 3
229
-
230
- on('-o', '--organization NAME', "Name of GitHub organization")
231
- on('-s', '--start-server', "Start web server, don't run any checks")
232
- on('-p', '--port PORT', "Port to bind web server to")
233
- on('-b', '--bind-address ADDRESS', "Address to bind web server to")
234
- on('-t', '--threads THREADS', "Number of threads to use")
235
- on('--delete NAME', "Delete an organization in the database")
236
- on('--reset-db', "Resets the database")
237
- on('--configure', "Start configuration wizard")
238
- on('--no-server', "Don't start the server when finished")
239
- on('--no-color', "Don't colorize output")
240
- on('--no-banner', "Don't print Gitrob banner")
241
-
242
- begin
243
- if ARGV.empty?
244
- Gitrob::fatal("No options given; see gitrob --help for options.")
245
- end
246
-
247
- go!
248
- rescue Gitrob::Github::HttpClient::MissingAccessTokensError
249
- Gitrob::fatal("Configuration file does not contain any GitHub access tokens. Run Gitrob with --configure flag to set it up.")
250
- rescue Gitrob::Github::HttpClient::AccessTokensDepletedError
251
- Gitrob::fatal("All GitHub access tokens under rate limiting. Go have a cup of coffee and try again.")
252
- rescue Gitrob::Github::HttpClient::RequestError => e
253
- Gitrob::fatal("A request to the GitHub API failed: #{e.message}")
254
- rescue Interrupt
255
- print "\b\b\n"; # Remove ^C from screen
256
- Gitrob::fatal("Caught interrupt; exiting.")
257
- rescue => e
258
- Gitrob::fatal("An error occurred: #{e.class.name}: #{e.message}")
259
- end
260
- end
@@ -1,41 +0,0 @@
1
- module Gitrob
2
- module Github
3
- class Blob
4
- attr_reader :path, :size, :repository
5
-
6
- def initialize(path, size, repository)
7
- @path, @size, @repository = path, size, repository
8
- end
9
-
10
- def extension
11
- File.extname(path)[1..-1]
12
- end
13
-
14
- def filename
15
- File.basename(path)
16
- end
17
-
18
- def dirname
19
- File.dirname(path)
20
- end
21
-
22
- def url
23
- "https://github.com/#{URI.escape(repository.owner)}/#{URI.escape(repository.name)}/blob/master/#{URI.escape(path)}"
24
- end
25
-
26
- def to_model(organization, repository)
27
- repository.blobs.new(
28
- :path => self.path,
29
- :filename => self.filename,
30
- :extension => self.extension,
31
- :size => self.size,
32
- :organization => organization
33
- )
34
- end
35
-
36
- def save_to_database!(organization, repository)
37
- self.to_model(organization, repository).tap { |m| m.save }
38
- end
39
- end
40
- end
41
- end
@@ -1,127 +0,0 @@
1
- module Gitrob
2
- module Github
3
- class HttpClient
4
- include HTTParty
5
- base_uri 'https://api.github.com'
6
-
7
- class HttpError < StandardError; end
8
- class ConnectionError < HttpError; end
9
-
10
- class RequestError < HttpError
11
- attr_reader :status, :body
12
- def initialize(method, path, status, body, options)
13
- @status = status
14
- @body = body
15
- super("#{method} to #{path} returned status #{status} - options: #{options.inspect}")
16
- end
17
- end
18
-
19
- class ClientError < RequestError; end
20
- class ServerError < RequestError; end
21
-
22
- class UnhandledError < StandardError; end
23
-
24
- class AccessTokenError < StandardError; end
25
- class MissingAccessTokensError < AccessTokenError; end
26
- class AccessTokensDepletedError < AccessTokenError; end
27
-
28
- DEFAULT_TIMEOUT = 0.5 #seconds
29
- DEFAULT_RETRIES = 3
30
-
31
- Response = Struct.new(:status, :headers, :body)
32
-
33
- RETRIABLE_EXCEPTIONS = [
34
- ServerError,
35
- AccessTokenError,
36
- Timeout::Error,
37
- Errno::ETIMEDOUT,
38
- Errno::ECONNRESET,
39
- Errno::ECONNREFUSED,
40
- Errno::ENETUNREACH,
41
- Errno::EHOSTUNREACH,
42
- EOFError
43
- ]
44
-
45
- def initialize(options)
46
- @config = {
47
- :timeout => DEFAULT_TIMEOUT,
48
- :retries => DEFAULT_RETRIES
49
- }.merge(options)
50
- raise MissingAccessTokensErrors.new("No access tokens given") unless @config[:access_tokens]
51
- default_timeout = @config[:timeout]
52
- end
53
-
54
- def do_get(path, params=nil, options={})
55
- do_request(:get, path, {:query => params}.merge(options))
56
- end
57
-
58
- def do_post(path, params=nil, options={})
59
- do_request(:post, path, {:query => params}.merge(options))
60
- end
61
-
62
- def do_put(path, params=nil, options={})
63
- do_request(:put, path, {:query => params}.merge(options))
64
- end
65
-
66
- def do_delete(path, params=nil, options={})
67
- do_request(:delete, path, {:query => params}.merge(options))
68
- end
69
-
70
- private
71
-
72
- def do_request(method, path, options)
73
- with_retries do
74
- access_token = get_access_token!
75
- response = self.class.send(method, path, {
76
- :headers => {
77
- 'Authorization' => "token #{access_token}",
78
- 'User-Agent' => "Gitrob v#{Gitrob::VERSION}"
79
- }
80
- }.merge(options))
81
- handle_possible_error!(method, path, response, options, access_token)
82
- Response.new(response.code, response.headers, response.body)
83
- end
84
- end
85
-
86
- def with_retries(&block)
87
- tries = @config[:retries]
88
- yield
89
- rescue *RETRIABLE_EXCEPTIONS => ex
90
- if (tries -= 1) > 0
91
- sleep 0.2
92
- retry
93
- else
94
- raise ex
95
- end
96
- end
97
-
98
- def handle_possible_error!(method, path, response, options, access_token)
99
- if access_token_rate_limited?(response) || access_token_unauthorized?(response)
100
- access_tokens.delete(access_token)
101
- raise AccessTokenError
102
- elsif response.code >= 500
103
- raise ServerError.new(method, path, response.code, response.body, options)
104
- elsif response.code >= 400
105
- raise ClientError.new(method, path, response.code, response.body, options)
106
- end
107
- end
108
-
109
- def access_token_rate_limited?(response)
110
- response.code == 403 && response.headers['X-RateLimit-Remaining'].to_i.zero?
111
- end
112
-
113
- def access_token_unauthorized?(response)
114
- response.code == 401
115
- end
116
-
117
- def get_access_token!
118
- raise AccessTokensDepletedError.new("Rate limit on all access tokens has been used up") if access_tokens.count.zero?
119
- access_tokens.sample
120
- end
121
-
122
- def access_tokens
123
- @config[:access_tokens]
124
- end
125
- end
126
- end
127
- end
@@ -1,99 +0,0 @@
1
- module Gitrob
2
- module Github
3
- class Organization
4
- attr_reader :name, :http_client
5
-
6
- def initialize(name, http_client)
7
- @name, @http_client = name, http_client
8
- end
9
-
10
- def display_name
11
- info['name'].to_s.empty? ? info['login'] : info['name']
12
- end
13
-
14
- def login
15
- info['login']
16
- end
17
-
18
- def website
19
- info['blog']
20
- end
21
-
22
- def location
23
- info['location']
24
- end
25
-
26
- def email
27
- info['email']
28
- end
29
-
30
- def url
31
- "https://github.com/#{name}"
32
- end
33
-
34
- def avatar_url
35
- info['avatar_url']
36
- end
37
-
38
- def repositories
39
- @repositories ||= recursive_repositories
40
- end
41
-
42
- def members
43
- @members ||= recursive_members
44
- end
45
-
46
- def to_model
47
- Gitrob::Organization.new(
48
- :name => self.display_name,
49
- :login => self.login,
50
- :website => self.website,
51
- :location => self.location,
52
- :email => self.email,
53
- :avatar_url => self.avatar_url,
54
- :url => self.url
55
- )
56
- end
57
-
58
- def save_to_database!
59
- self.to_model.tap { |m| m.save }
60
- end
61
-
62
- private
63
-
64
- def recursive_members(page = 1)
65
- members = Array.new
66
- response = http_client.do_get("/orgs/#{name}/members?page=#{page.to_i}")
67
- JSON.parse(response.body).each do |member|
68
- members << User.new(member['login'], http_client)
69
- end
70
-
71
- if response.headers.include?('link') && response.headers['link'].include?('rel="next"')
72
- members += recursive_members(page + 1)
73
- end
74
- members
75
- end
76
-
77
- def recursive_repositories(page = 1)
78
- repositories = Array.new
79
- response = http_client.do_get("/orgs/#{name}/repos?page=#{page.to_i}")
80
- JSON.parse(response.body).each do |repo|
81
- next if repo['fork']
82
- repositories << Repository.new(name, repo['name'], http_client)
83
- end
84
-
85
- if response.headers.include?('link') && response.headers['link'].include?('rel="next"')
86
- repositories += recursive_repositories(page + 1)
87
- end
88
- repositories
89
- end
90
-
91
- def info
92
- if !@info
93
- @info = JSON.parse(http_client.do_get("/orgs/#{name}").body)
94
- end
95
- @info
96
- end
97
- end
98
- end
99
- end
@@ -1,72 +0,0 @@
1
- module Gitrob
2
- module Github
3
- class Repository
4
-
5
- attr_reader :owner, :name, :http_client
6
- def initialize(owner, name, http_client)
7
- @owner, @name, @http_client = owner, name, http_client
8
- end
9
-
10
- def contents
11
- if !@contents
12
- @contents = []
13
- response = JSON.parse(http_client.do_get("/repos/#{owner}/#{name}/git/trees/master?recursive=1").body)
14
- response['tree'].each do |object|
15
- next unless object['type'] == 'blob'
16
- @contents << Blob.new(object['path'], object['size'], self)
17
- end
18
- end
19
- @contents
20
- rescue HttpClient::ClientError => ex
21
- if ex.status == 409 || ex.status == 404
22
- @contents = []
23
- else
24
- raise ex
25
- end
26
- end
27
-
28
- def full_name
29
- [owner, name].join('/')
30
- end
31
-
32
- def url
33
- info['html_url']
34
- end
35
-
36
- def description
37
- info['description']
38
- end
39
-
40
- def website
41
- info['homepage']
42
- end
43
-
44
- def to_model(organization, user = nil)
45
- Gitrob::Repo.new(
46
- :name => self.name,
47
- :owner_name => self.owner,
48
- :description => self.description,
49
- :website => self.website,
50
- :url => self.url,
51
- :organization => organization,
52
- :user => user
53
- )
54
- end
55
-
56
- def save_to_database!(organization, user = nil)
57
- self.to_model(organization, user).tap { |m| m.save }
58
- rescue DataMapper::SaveFailureError => e
59
- puts e.resource.errors.inspect
60
- end
61
-
62
- private
63
-
64
- def info
65
- if !@info
66
- @info = JSON.parse(http_client.do_get("/repos/#{owner}/#{name}").body)
67
- end
68
- @info
69
- end
70
- end
71
- end
72
- end
@@ -1,84 +0,0 @@
1
- module Gitrob
2
- module Github
3
- class User
4
-
5
- attr_reader :username, :http_client
6
-
7
- def initialize(username, http_client)
8
- @username, @http_client = username, http_client
9
- end
10
-
11
- def name
12
- info['name'] || username
13
- end
14
-
15
- def email
16
- info['email']
17
- end
18
-
19
- def website
20
- info['blog']
21
- end
22
-
23
- def location
24
- info['location']
25
- end
26
-
27
- def bio
28
- info['bio']
29
- end
30
-
31
- def url
32
- info['html_url']
33
- end
34
-
35
- def avatar_url
36
- info['avatar_url']
37
- end
38
-
39
- def repositories
40
- @repositories ||= recursive_repositories
41
- end
42
-
43
- def to_model(organization)
44
- organization.users.new(
45
- :username => self.username,
46
- :name => self.name,
47
- :website => self.website,
48
- :location => self.location,
49
- :email => self.email,
50
- :bio => self.bio,
51
- :url => self.url,
52
- :avatar_url => self.avatar_url
53
- )
54
- end
55
-
56
- def save_to_database!(organization)
57
- self.to_model(organization).tap { |m| m.save }
58
- end
59
-
60
- private
61
-
62
- def recursive_repositories(page = 1)
63
- repositories = Array.new
64
- response = http_client.do_get("/users/#{username}/repos?page=#{page.to_i}")
65
- JSON.parse(response.body).each do |repo|
66
- next if repo['fork']
67
- repositories << Repository.new(username, repo['name'], http_client)
68
- end
69
-
70
- if response.headers.include?('link') && response.headers['link'].include?('rel="next"')
71
- repositories += recursive_repositories(page + 1)
72
- end
73
- repositories
74
- end
75
-
76
- def info
77
- if !@info
78
- @info = JSON.parse(http_client.do_get("/users/#{username}").body)
79
- end
80
- @info
81
- end
82
- end
83
- end
84
- end