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