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 +5 -5
- data/lib/organization_audit.rb +5 -7
- data/lib/organization_audit/repo.rb +61 -45
- data/lib/organization_audit/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 517718feb8aeb0fecbbbfbf2d7ca7ad176d5cc4cf0f2bbecda63a1b26fff7ec8
|
4
|
+
data.tar.gz: 800dc8bd5930caa2464d34a342f8aff400199996cf062e797e9ecfa28560d5a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 366b21f6b0e542417e4d5f7163b5c47a0d8681576fbcbe1fa0cd1617121bd67d1039dbbe768d26a87fce57bf0ed8fa93f12d97a684736189438bee006b163cd3
|
7
|
+
data.tar.gz: df4f289f63d634090c3a68cc0da8755c05ea9df37cedc554fc82f5fdbfd3f19eada81545c9143a1a2a23fef1013f4b10c1f314d9161bc48b4696d8bbba161dfd
|
data/lib/organization_audit.rb
CHANGED
@@ -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
|
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)
|
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.
|
32
|
-
ignore.include?(repo.url)
|
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(
|
35
|
-
user = if
|
36
|
-
"orgs/#{
|
37
|
-
elsif
|
38
|
-
"users/#{
|
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
|
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
|
58
|
-
raise
|
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
|
98
|
-
|
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 =
|
115
|
-
open("#{url}?page=#{page}", headers).read
|
116
|
-
end
|
118
|
+
response = http_get("#{url}?page=#{page}", headers)
|
117
119
|
result = JSON.parse(response)
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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}")
|
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.
|
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
|
-
|
167
|
-
rescue
|
168
|
-
if e.message.
|
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
|
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:
|
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:
|
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:
|
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.
|
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
|