organization_audit 1.0.5 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a0184c6c28eeac80b8b0ae076a7fae8d27371084
4
- data.tar.gz: c961aad449d2a8730a70743956b27291c3a63729
2
+ SHA256:
3
+ metadata.gz: 517718feb8aeb0fecbbbfbf2d7ca7ad176d5cc4cf0f2bbecda63a1b26fff7ec8
4
+ data.tar.gz: 800dc8bd5930caa2464d34a342f8aff400199996cf062e797e9ecfa28560d5a5
5
5
  SHA512:
6
- metadata.gz: 13d818408c96d325ad7569eebba2b97dc5a06b7f5efe5e705258fe84e286427aa72371ae35f047fd75ea40ada5f077a782f0681a147b0aed10fc7c2ac54e7e1d
7
- data.tar.gz: 7a3ed8d3db5d7fa1bc374257a0594be39e54704e5eb9f1c690afa61d47764532fe770bed7d2f40fd291f9442ce98a717e75b0965eb5d0e9b21b2505db85ad145
6
+ metadata.gz: 366b21f6b0e542417e4d5f7163b5c47a0d8681576fbcbe1fa0cd1617121bd67d1039dbbe768d26a87fce57bf0ed8fa93f12d97a684736189438bee006b163cd3
7
+ data.tar.gz: df4f289f63d634090c3a68cc0da8755c05ea9df37cedc554fc82f5fdbfd3f19eada81545c9143a1a2a23fef1013f4b10c1f314d9161bc48b4696d8bbba161dfd
@@ -4,16 +4,14 @@ module OrganizationAudit
4
4
  autoload :Repo, 'organization_audit/repo'
5
5
 
6
6
  class << self
7
- def all(options={})
8
- ignore = (options[:ignore] || [])
7
+ def all(ignore: [], ignore_public: false, **options)
9
8
  unless options[:token]
10
- options = options.dup
11
9
  token = `git config github.token`.strip
12
- options[:token] = token unless token.empty?
10
+ options = options.merge(token: token) unless token.empty?
13
11
  end
14
12
 
15
13
  Repo.all(options).reject do |repo|
16
- matches_ignore?(ignore, repo) or (options[:ignore_public] and repo.public?)
14
+ matches_ignore?(ignore, repo) || (ignore_public && repo.public?)
17
15
  end
18
16
  end
19
17
 
@@ -28,8 +26,8 @@ module OrganizationAudit
28
26
  private
29
27
 
30
28
  def matches_ignore?(ignore, repo)
31
- ignore_regexp = ignore.select { |i| i =~ /^\/.*\/$/ }.map { |i| Regexp.new(i[1..-2]) }
32
- ignore.include?(repo.url) or ignore.include?(repo.name) or ignore_regexp.any? { |i| i =~ repo.name }
29
+ ignore_regexp = Regexp.union *ignore.grep(/^\/.*\/$/).map { |i| Regexp.new(i[1..-2]) }
30
+ ignore.include?(repo.url) || ignore.include?(repo.name) || ignore_regexp =~ repo.name
33
31
  end
34
32
  end
35
33
  end
@@ -1,11 +1,22 @@
1
- require "open-uri"
2
1
  require "json"
3
2
  require "base64"
3
+ require "net/http"
4
4
 
5
5
  module OrganizationAudit
6
6
  class Repo
7
7
  HOST = "https://api.github.com"
8
8
 
9
+ class RequestError < StandardError
10
+ attr_reader :url, :code, :body
11
+
12
+ def initialize(message, url=nil, code=500, body='')
13
+ @url = url
14
+ @code = Integer(code)
15
+ @body = body
16
+ super "#{message}\n#{url}\nCode: #{code}\n#{body}"
17
+ end
18
+ end
19
+
9
20
  def initialize(data, token=nil)
10
21
  @data = data
11
22
  @token = token
@@ -31,31 +42,29 @@ module OrganizationAudit
31
42
  api_url.split("/").last
32
43
  end
33
44
 
34
- def self.all(options)
35
- user = if options[:organization]
36
- "orgs/#{options[:organization]}"
37
- elsif options[:user]
38
- "users/#{options[:user]}"
45
+ def self.all(organization: nil, user: nil, max_pages: nil, token: nil)
46
+ user = if organization
47
+ "orgs/#{organization}"
48
+ elsif user
49
+ "users/#{user}"
39
50
  else
40
51
  "user"
41
52
  end
42
53
  url = File.join(HOST, user, "repos")
43
54
 
44
- token = options[:token]
45
- download_all_pages(url, headers(token)).map { |data| Repo.new(data, token) }
55
+ download_all_pages(url, headers(token), max_pages: max_pages).map { |data| Repo.new(data, token) }
46
56
  end
47
57
 
48
58
  def content(file)
49
- @content ||= {}
50
- @content[file] ||= begin
59
+ (@content ||= {})[file] ||= begin
51
60
  if private?
52
61
  download_content_via_api(file)
53
62
  else
54
63
  download_content_via_raw(file)
55
64
  end
56
65
  end
57
- rescue OpenURI::HTTPError => e
58
- raise "Error downloading #{file} from #{url} (#{e})" unless e.message.start_with?("404")
66
+ rescue RequestError => e
67
+ raise unless e.code == 404
59
68
  end
60
69
 
61
70
  def private?
@@ -91,15 +100,10 @@ module OrganizationAudit
91
100
  private
92
101
 
93
102
  def list(dir)
94
- @list ||= {}
95
- @list[dir] ||= begin
103
+ (@list ||= {})[dir] ||= begin
96
104
  call_api("contents/#{dir == "." ? "" : dir}?branch=#{branch}")
97
- rescue OpenURI::HTTPError => e
98
- if e.message =~ /404 Not Found/
99
- []
100
- else
101
- raise
102
- end
105
+ rescue RequestError => e
106
+ e.code == 404 ? [] : raise
103
107
  end
104
108
  end
105
109
 
@@ -107,24 +111,45 @@ module OrganizationAudit
107
111
  file_list.grep(/\.gemspec$/).first
108
112
  end
109
113
 
110
- def self.download_all_pages(url, headers)
114
+ def self.download_all_pages(url, headers, max_pages: nil)
111
115
  results = []
112
116
  page = 1
113
117
  loop do
114
- response = decorate_errors do
115
- open("#{url}?page=#{page}", headers).read
116
- end
118
+ response = http_get("#{url}?page=#{page}", headers)
117
119
  result = JSON.parse(response)
118
- if result.size == 0
119
- break
120
- else
121
- results.concat(result)
122
- page += 1
123
- end
120
+ results.concat(result)
121
+
122
+ break if result.size == 0 || (max_pages && page >= max_pages) # stop on empty page or over max
123
+ page += 1
124
124
  end
125
125
  results
126
126
  end
127
127
 
128
+ def self.http_get(url, headers, retried: false)
129
+ uri = URI(url)
130
+ request = Net::HTTP::Get.new(uri, headers)
131
+ http = Net::HTTP.new(uri.hostname, uri.port)
132
+ http.use_ssl = true if uri.instance_of? URI::HTTPS
133
+ response =
134
+ begin
135
+ http.start { |http| http.request(request) }
136
+ rescue
137
+ raise RequestError.new("#{$!.class} error during request #{url}", url)
138
+ end
139
+
140
+ return response.body if response.code == '200'
141
+
142
+ # github sends 403 with 0-limit header when rate limit is exceeded
143
+ if !retried && response["x-ratelimit-remaining"] == "0"
144
+ wait = Integer(response["x-ratelimit-reset"]) - Time.now.to_i
145
+ warn "Github rate limit exhausted, retrying in #{wait}"
146
+ sleep wait
147
+ return http_get(url, headers, retried: true)
148
+ end
149
+
150
+ raise RequestError.new("HTTP get error", url, response.code, response.body)
151
+ end
152
+
128
153
  def branch
129
154
  @data["default_branch"] || @data["master_branch"] || "master"
130
155
  end
@@ -134,38 +159,29 @@ module OrganizationAudit
134
159
  end
135
160
 
136
161
  def raw_url
137
- url.sub("://", "://raw.")
162
+ url.dup.sub!("://github.com", "://raw.githubusercontent.com") || raise("Unable to determine raw url for #{url}")
138
163
  end
139
164
 
140
165
  # increases api rate limit
141
166
  def download_content_via_api(file)
142
- content = call_api("contents/#{file}?branch=#{branch}")["content"]
167
+ content = call_api("contents/#{file}?branch=#{branch}").fetch("content")
143
168
  Base64.decode64(content)
144
169
  end
145
170
 
146
171
  def call_api(path)
147
- content = self.class.decorate_errors do
148
- download(File.join(api_url, path), self.class.headers(@token))
149
- end
172
+ content = download(File.join(api_url, path), self.class.headers(@token))
150
173
  JSON.load(content)
151
174
  end
152
175
 
153
- def self.decorate_errors
154
- yield
155
- rescue OpenURI::HTTPError => e
156
- e.message << " -- body: " << e.io.read
157
- raise e
158
- end
159
-
160
176
  # unlimited
161
177
  def download_content_via_raw(file)
162
178
  download(File.join(raw_url, branch, file))
163
179
  end
164
180
 
165
181
  def download(url, headers={})
166
- open(url, headers).read
167
- rescue OpenURI::HTTPError => e
168
- if e.message.start_with?("503 Connection timed out")
182
+ self.class.http_get(url, headers)
183
+ rescue RequestError => e
184
+ if e.message.include? "Timeout"
169
185
  retries ||= 0
170
186
  retries += 1
171
187
  retry if retries < 3
@@ -1,3 +1,3 @@
1
1
  module OrganizationAudit
2
- VERSION = "1.0.5"
2
+ VERSION = "2.0.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: organization_audit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Grosser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-31 00:00:00.000000000 Z
11
+ date: 2019-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -46,7 +46,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
46
46
  requirements:
47
47
  - - ">="
48
48
  - !ruby/object:Gem::Version
49
- version: '0'
49
+ version: 2.3.0
50
50
  required_rubygems_version: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
@@ -54,7 +54,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
54
54
  version: '0'
55
55
  requirements: []
56
56
  rubyforge_project:
57
- rubygems_version: 2.2.2
57
+ rubygems_version: 2.7.6
58
58
  signing_key:
59
59
  specification_version: 4
60
60
  summary: Audit all repos of your organization or user