organization_audit 1.0.5 → 2.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.
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