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