octodoggy 4.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/CONTRIBUTING.md +22 -0
- data/LICENSE.md +20 -0
- data/README.md +714 -0
- data/Rakefile +22 -0
- data/lib/ext/sawyer/relation.rb +10 -0
- data/lib/octokit.rb +59 -0
- data/lib/octokit/arguments.rb +14 -0
- data/lib/octokit/authentication.rb +82 -0
- data/lib/octokit/client.rb +238 -0
- data/lib/octokit/client/authorizations.rb +244 -0
- data/lib/octokit/client/commit_comments.rb +95 -0
- data/lib/octokit/client/commits.rb +239 -0
- data/lib/octokit/client/contents.rb +162 -0
- data/lib/octokit/client/deployments.rb +62 -0
- data/lib/octokit/client/downloads.rb +50 -0
- data/lib/octokit/client/emojis.rb +18 -0
- data/lib/octokit/client/events.rb +151 -0
- data/lib/octokit/client/feeds.rb +33 -0
- data/lib/octokit/client/gists.rb +233 -0
- data/lib/octokit/client/gitignore.rb +43 -0
- data/lib/octokit/client/hooks.rb +297 -0
- data/lib/octokit/client/integrations.rb +77 -0
- data/lib/octokit/client/issues.rb +321 -0
- data/lib/octokit/client/labels.rb +156 -0
- data/lib/octokit/client/legacy_search.rb +42 -0
- data/lib/octokit/client/licenses.rb +45 -0
- data/lib/octokit/client/markdown.rb +27 -0
- data/lib/octokit/client/meta.rb +21 -0
- data/lib/octokit/client/milestones.rb +87 -0
- data/lib/octokit/client/notifications.rb +171 -0
- data/lib/octokit/client/objects.rb +141 -0
- data/lib/octokit/client/organizations.rb +768 -0
- data/lib/octokit/client/pages.rb +63 -0
- data/lib/octokit/client/projects.rb +314 -0
- data/lib/octokit/client/pub_sub_hubbub.rb +111 -0
- data/lib/octokit/client/pull_requests.rb +301 -0
- data/lib/octokit/client/rate_limit.rb +54 -0
- data/lib/octokit/client/reactions.rb +158 -0
- data/lib/octokit/client/refs.rb +118 -0
- data/lib/octokit/client/releases.rb +163 -0
- data/lib/octokit/client/repositories.rb +654 -0
- data/lib/octokit/client/repository_invitations.rb +103 -0
- data/lib/octokit/client/reviews.rb +174 -0
- data/lib/octokit/client/say.rb +19 -0
- data/lib/octokit/client/search.rb +76 -0
- data/lib/octokit/client/service_status.rb +38 -0
- data/lib/octokit/client/source_import.rb +161 -0
- data/lib/octokit/client/stats.rb +105 -0
- data/lib/octokit/client/statuses.rb +47 -0
- data/lib/octokit/client/traffic.rb +69 -0
- data/lib/octokit/client/users.rb +354 -0
- data/lib/octokit/configurable.rb +147 -0
- data/lib/octokit/connection.rb +199 -0
- data/lib/octokit/default.rb +166 -0
- data/lib/octokit/enterprise_admin_client.rb +40 -0
- data/lib/octokit/enterprise_admin_client/admin_stats.rb +120 -0
- data/lib/octokit/enterprise_admin_client/license.rb +18 -0
- data/lib/octokit/enterprise_admin_client/orgs.rb +27 -0
- data/lib/octokit/enterprise_admin_client/search_indexing.rb +83 -0
- data/lib/octokit/enterprise_admin_client/users.rb +128 -0
- data/lib/octokit/enterprise_management_console_client.rb +50 -0
- data/lib/octokit/enterprise_management_console_client/management_console.rb +176 -0
- data/lib/octokit/error.rb +286 -0
- data/lib/octokit/gist.rb +36 -0
- data/lib/octokit/middleware/follow_redirects.rb +131 -0
- data/lib/octokit/organization.rb +17 -0
- data/lib/octokit/preview.rb +38 -0
- data/lib/octokit/rate_limit.rb +33 -0
- data/lib/octokit/repo_arguments.rb +19 -0
- data/lib/octokit/repository.rb +93 -0
- data/lib/octokit/response/feed_parser.rb +21 -0
- data/lib/octokit/response/raise_error.rb +21 -0
- data/lib/octokit/user.rb +19 -0
- data/lib/octokit/version.rb +17 -0
- data/lib/octokit/warnable.rb +17 -0
- data/octokit.gemspec +22 -0
- metadata +160 -0
@@ -0,0 +1,176 @@
|
|
1
|
+
module Octokit
|
2
|
+
class EnterpriseManagementConsoleClient
|
3
|
+
|
4
|
+
# Methods for the Enterprise Management Console API
|
5
|
+
#
|
6
|
+
# @see https://developer.github.com/v3/enterprise/management_console
|
7
|
+
module ManagementConsole
|
8
|
+
|
9
|
+
# Uploads a license for the first time
|
10
|
+
#
|
11
|
+
# @param license [String] The path to your .ghl license file.
|
12
|
+
# @param settings [Hash] A hash configuration of the initial settings.
|
13
|
+
#
|
14
|
+
# @see http: //git.io/j5NT
|
15
|
+
# @return nil
|
16
|
+
def upload_license(license, settings = nil)
|
17
|
+
conn = faraday_configuration
|
18
|
+
|
19
|
+
params = { }
|
20
|
+
params[:license] = Faraday::UploadIO.new(license, 'binary')
|
21
|
+
params[:password] = @management_console_password
|
22
|
+
params[:settings] = "#{settings.to_json}" unless settings.nil?
|
23
|
+
|
24
|
+
@last_response = conn.post("/setup/api/start", params)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Start a configuration process.
|
28
|
+
#
|
29
|
+
# @return nil
|
30
|
+
def start_configuration
|
31
|
+
post "/setup/api/configure", password_hash
|
32
|
+
end
|
33
|
+
|
34
|
+
# Upgrade an Enterprise installation
|
35
|
+
#
|
36
|
+
# @param license [String] The path to your .ghl license file.
|
37
|
+
#
|
38
|
+
# @return nil
|
39
|
+
def upgrade(license)
|
40
|
+
conn = faraday_configuration
|
41
|
+
|
42
|
+
params = { }
|
43
|
+
params[:license] = Faraday::UploadIO.new(license, 'binary')
|
44
|
+
params[:api_key] = @management_console_password
|
45
|
+
@last_response = conn.post("/setup/api/upgrade", params)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Get information about the Enterprise installation
|
49
|
+
#
|
50
|
+
# @return [Sawyer::Resource] The installation information
|
51
|
+
def config_status
|
52
|
+
get "/setup/api/configcheck", password_hash
|
53
|
+
end
|
54
|
+
alias :config_check :config_status
|
55
|
+
|
56
|
+
# Get information about the Enterprise installation
|
57
|
+
#
|
58
|
+
# @return [Sawyer::Resource] The settings
|
59
|
+
def settings
|
60
|
+
get "/setup/api/settings", password_hash
|
61
|
+
end
|
62
|
+
alias :get_settings :settings
|
63
|
+
|
64
|
+
# Modify the Enterprise settings
|
65
|
+
#
|
66
|
+
# @param settings [Hash] A hash configuration of the new settings
|
67
|
+
#
|
68
|
+
# @return [nil]
|
69
|
+
def edit_settings(settings)
|
70
|
+
queries = password_hash
|
71
|
+
queries[:query][:settings] = "#{settings.to_json}"
|
72
|
+
put "/setup/api/settings", queries
|
73
|
+
end
|
74
|
+
|
75
|
+
# Get information about the Enterprise maintenance status
|
76
|
+
#
|
77
|
+
# @return [Sawyer::Resource] The maintenance status
|
78
|
+
def maintenance_status
|
79
|
+
get "/setup/api/maintenance", password_hash
|
80
|
+
end
|
81
|
+
alias :get_maintenance_status :maintenance_status
|
82
|
+
|
83
|
+
# Start (or turn off) the Enterprise maintenance mode
|
84
|
+
#
|
85
|
+
# @param maintenance [Hash] A hash configuration of the maintenance settings
|
86
|
+
# @return [nil]
|
87
|
+
def set_maintenance_status(maintenance)
|
88
|
+
queries = password_hash
|
89
|
+
queries[:query][:maintenance] = "#{maintenance.to_json}"
|
90
|
+
post "/setup/api/maintenance", queries
|
91
|
+
end
|
92
|
+
alias :edit_maintenance_status :set_maintenance_status
|
93
|
+
|
94
|
+
# Fetch the authorized SSH keys on the Enterprise install
|
95
|
+
#
|
96
|
+
# @return [Sawyer::Resource] An array of authorized SSH keys
|
97
|
+
def authorized_keys
|
98
|
+
get "/setup/api/settings/authorized-keys", password_hash
|
99
|
+
end
|
100
|
+
alias :get_authorized_keys :authorized_keys
|
101
|
+
|
102
|
+
# Add an authorized SSH keys on the Enterprise install
|
103
|
+
#
|
104
|
+
# @param key Either the file path to a key, a File handler to the key, or the contents of the key itself
|
105
|
+
# @return [Sawyer::Resource] An array of authorized SSH keys
|
106
|
+
def add_authorized_key(key)
|
107
|
+
queries = password_hash
|
108
|
+
case key
|
109
|
+
when String
|
110
|
+
if File.exist?(key)
|
111
|
+
key = File.open(key, "r")
|
112
|
+
content = key.read.strip
|
113
|
+
key.close
|
114
|
+
else
|
115
|
+
content = key
|
116
|
+
end
|
117
|
+
when File
|
118
|
+
content = key.read.strip
|
119
|
+
key.close
|
120
|
+
end
|
121
|
+
|
122
|
+
queries[:query][:authorized_key] = content
|
123
|
+
post "/setup/api/settings/authorized-keys", queries
|
124
|
+
end
|
125
|
+
|
126
|
+
# Removes an authorized SSH keys from the Enterprise install
|
127
|
+
#
|
128
|
+
# @param key Either the file path to a key, a File handler to the key, or the contents of the key itself
|
129
|
+
# @return [Sawyer::Resource] An array of authorized SSH keys
|
130
|
+
def remove_authorized_key(key)
|
131
|
+
queries = password_hash
|
132
|
+
case key
|
133
|
+
when String
|
134
|
+
if File.exist?(key)
|
135
|
+
key = File.open(key, "r")
|
136
|
+
content = key.read.strip
|
137
|
+
key.close
|
138
|
+
else
|
139
|
+
content = key
|
140
|
+
end
|
141
|
+
when File
|
142
|
+
content = key.read.strip
|
143
|
+
key.close
|
144
|
+
end
|
145
|
+
|
146
|
+
queries[:query][:authorized_key] = content
|
147
|
+
delete "/setup/api/settings/authorized-keys", queries
|
148
|
+
end
|
149
|
+
alias :delete_authorized_key :remove_authorized_key
|
150
|
+
|
151
|
+
end
|
152
|
+
private
|
153
|
+
|
154
|
+
def password_hash
|
155
|
+
{ :query => { :api_key => @management_console_password } }
|
156
|
+
end
|
157
|
+
|
158
|
+
# We fall back to raw Faraday for handling the licenses because I'm suspicious
|
159
|
+
# that Sawyer isn't handling binary POSTs correctly: http://git.io/jMir
|
160
|
+
def faraday_configuration
|
161
|
+
@faraday_configuration ||= Faraday.new(:url => @management_console_endpoint) do |http|
|
162
|
+
http.headers[:user_agent] = user_agent
|
163
|
+
http.request :multipart
|
164
|
+
http.request :url_encoded
|
165
|
+
|
166
|
+
# Disabling SSL is essential for certain self-hosted Enterprise instances
|
167
|
+
if self.connection_options[:ssl] && !self.connection_options[:ssl][:verify]
|
168
|
+
http.ssl[:verify] = false
|
169
|
+
end
|
170
|
+
|
171
|
+
http.use Octokit::Response::RaiseError
|
172
|
+
http.adapter Faraday.default_adapter
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,286 @@
|
|
1
|
+
module Octokit
|
2
|
+
# Custom error class for rescuing from all GitHub errors
|
3
|
+
class Error < StandardError
|
4
|
+
|
5
|
+
# Returns the appropriate Octokit::Error subclass based
|
6
|
+
# on status and response message
|
7
|
+
#
|
8
|
+
# @param [Hash] response HTTP response
|
9
|
+
# @return [Octokit::Error]
|
10
|
+
def self.from_response(response)
|
11
|
+
status = response[:status].to_i
|
12
|
+
body = response[:body].to_s
|
13
|
+
headers = response[:response_headers]
|
14
|
+
|
15
|
+
if klass = case status
|
16
|
+
when 400 then Octokit::BadRequest
|
17
|
+
when 401 then error_for_401(headers)
|
18
|
+
when 403 then error_for_403(body)
|
19
|
+
when 404 then error_for_404(body)
|
20
|
+
when 405 then Octokit::MethodNotAllowed
|
21
|
+
when 406 then Octokit::NotAcceptable
|
22
|
+
when 409 then Octokit::Conflict
|
23
|
+
when 415 then Octokit::UnsupportedMediaType
|
24
|
+
when 422 then Octokit::UnprocessableEntity
|
25
|
+
when 451 then Octokit::UnavailableForLegalReasons
|
26
|
+
when 400..499 then Octokit::ClientError
|
27
|
+
when 500 then Octokit::InternalServerError
|
28
|
+
when 501 then Octokit::NotImplemented
|
29
|
+
when 502 then Octokit::BadGateway
|
30
|
+
when 503 then Octokit::ServiceUnavailable
|
31
|
+
when 500..599 then Octokit::ServerError
|
32
|
+
end
|
33
|
+
klass.new(response)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(response=nil)
|
38
|
+
@response = response
|
39
|
+
super(build_error_message)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Documentation URL returned by the API for some errors
|
43
|
+
#
|
44
|
+
# @return [String]
|
45
|
+
def documentation_url
|
46
|
+
data[:documentation_url] if data.is_a? Hash
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns most appropriate error for 401 HTTP status code
|
50
|
+
# @private
|
51
|
+
def self.error_for_401(headers)
|
52
|
+
if Octokit::OneTimePasswordRequired.required_header(headers)
|
53
|
+
Octokit::OneTimePasswordRequired
|
54
|
+
else
|
55
|
+
Octokit::Unauthorized
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns most appropriate error for 403 HTTP status code
|
60
|
+
# @private
|
61
|
+
def self.error_for_403(body)
|
62
|
+
if body =~ /rate limit exceeded/i
|
63
|
+
Octokit::TooManyRequests
|
64
|
+
elsif body =~ /login attempts exceeded/i
|
65
|
+
Octokit::TooManyLoginAttempts
|
66
|
+
elsif body =~ /abuse/i
|
67
|
+
Octokit::AbuseDetected
|
68
|
+
elsif body =~ /repository access blocked/i
|
69
|
+
Octokit::RepositoryUnavailable
|
70
|
+
elsif body =~ /email address must be verified/i
|
71
|
+
Octokit::UnverifiedEmail
|
72
|
+
elsif body =~ /account was suspended/i
|
73
|
+
Octokit::AccountSuspended
|
74
|
+
else
|
75
|
+
Octokit::Forbidden
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Return most appropriate error for 404 HTTP status code
|
80
|
+
# @private
|
81
|
+
def self.error_for_404(body)
|
82
|
+
if body =~ /Branch not protected/i
|
83
|
+
Octokit::BranchNotProtected
|
84
|
+
else
|
85
|
+
Octokit::NotFound
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Array of validation errors
|
90
|
+
# @return [Array<Hash>] Error info
|
91
|
+
def errors
|
92
|
+
if data && data.is_a?(Hash)
|
93
|
+
data[:errors] || []
|
94
|
+
else
|
95
|
+
[]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Status code returned by the GitHub server.
|
100
|
+
#
|
101
|
+
# @return [Integer]
|
102
|
+
def response_status
|
103
|
+
@response[:status]
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def data
|
109
|
+
@data ||=
|
110
|
+
if (body = @response[:body]) && !body.empty?
|
111
|
+
if body.is_a?(String) &&
|
112
|
+
@response[:response_headers] &&
|
113
|
+
@response[:response_headers][:content_type] =~ /json/
|
114
|
+
|
115
|
+
Sawyer::Agent.serializer.decode(body)
|
116
|
+
else
|
117
|
+
body
|
118
|
+
end
|
119
|
+
else
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def response_message
|
125
|
+
case data
|
126
|
+
when Hash
|
127
|
+
data[:message]
|
128
|
+
when String
|
129
|
+
data
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def response_error
|
134
|
+
"Error: #{data[:error]}" if data.is_a?(Hash) && data[:error]
|
135
|
+
end
|
136
|
+
|
137
|
+
def response_error_summary
|
138
|
+
return nil unless data.is_a?(Hash) && !Array(data[:errors]).empty?
|
139
|
+
|
140
|
+
summary = "\nError summary:\n"
|
141
|
+
summary << data[:errors].map do |error|
|
142
|
+
if error.is_a? Hash
|
143
|
+
error.map { |k,v| " #{k}: #{v}" }
|
144
|
+
else
|
145
|
+
" #{error}"
|
146
|
+
end
|
147
|
+
end.join("\n")
|
148
|
+
|
149
|
+
summary
|
150
|
+
end
|
151
|
+
|
152
|
+
def build_error_message
|
153
|
+
return nil if @response.nil?
|
154
|
+
|
155
|
+
message = "#{@response[:method].to_s.upcase} "
|
156
|
+
message << redact_url(@response[:url].to_s) + ": "
|
157
|
+
message << "#{@response[:status]} - "
|
158
|
+
message << "#{response_message}" unless response_message.nil?
|
159
|
+
message << "#{response_error}" unless response_error.nil?
|
160
|
+
message << "#{response_error_summary}" unless response_error_summary.nil?
|
161
|
+
message << " // See: #{documentation_url}" unless documentation_url.nil?
|
162
|
+
message
|
163
|
+
end
|
164
|
+
|
165
|
+
def redact_url(url_string)
|
166
|
+
%w[client_secret access_token].each do |token|
|
167
|
+
url_string.gsub!(/#{token}=\S+/, "#{token}=(redacted)") if url_string.include? token
|
168
|
+
end
|
169
|
+
url_string
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Raised on errors in the 400-499 range
|
174
|
+
class ClientError < Error; end
|
175
|
+
|
176
|
+
# Raised when GitHub returns a 400 HTTP status code
|
177
|
+
class BadRequest < ClientError; end
|
178
|
+
|
179
|
+
# Raised when GitHub returns a 401 HTTP status code
|
180
|
+
class Unauthorized < ClientError; end
|
181
|
+
|
182
|
+
# Raised when GitHub returns a 401 HTTP status code
|
183
|
+
# and headers include "X-GitHub-OTP"
|
184
|
+
class OneTimePasswordRequired < ClientError
|
185
|
+
#@private
|
186
|
+
OTP_DELIVERY_PATTERN = /required; (\w+)/i
|
187
|
+
|
188
|
+
#@private
|
189
|
+
def self.required_header(headers)
|
190
|
+
OTP_DELIVERY_PATTERN.match headers['X-GitHub-OTP'].to_s
|
191
|
+
end
|
192
|
+
|
193
|
+
# Delivery method for the user's OTP
|
194
|
+
#
|
195
|
+
# @return [String]
|
196
|
+
def password_delivery
|
197
|
+
@password_delivery ||= delivery_method_from_header
|
198
|
+
end
|
199
|
+
|
200
|
+
private
|
201
|
+
|
202
|
+
def delivery_method_from_header
|
203
|
+
if match = self.class.required_header(@response[:response_headers])
|
204
|
+
match[1]
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# Raised when GitHub returns a 403 HTTP status code
|
210
|
+
class Forbidden < ClientError; end
|
211
|
+
|
212
|
+
# Raised when GitHub returns a 403 HTTP status code
|
213
|
+
# and body matches 'rate limit exceeded'
|
214
|
+
class TooManyRequests < Forbidden; end
|
215
|
+
|
216
|
+
# Raised when GitHub returns a 403 HTTP status code
|
217
|
+
# and body matches 'login attempts exceeded'
|
218
|
+
class TooManyLoginAttempts < Forbidden; end
|
219
|
+
|
220
|
+
# Raised when GitHub returns a 403 HTTP status code
|
221
|
+
# and body matches 'abuse'
|
222
|
+
class AbuseDetected < Forbidden; end
|
223
|
+
|
224
|
+
# Raised when GitHub returns a 403 HTTP status code
|
225
|
+
# and body matches 'repository access blocked'
|
226
|
+
class RepositoryUnavailable < Forbidden; end
|
227
|
+
|
228
|
+
# Raised when GitHub returns a 403 HTTP status code
|
229
|
+
# and body matches 'email address must be verified'
|
230
|
+
class UnverifiedEmail < Forbidden; end
|
231
|
+
|
232
|
+
# Raised when GitHub returns a 403 HTTP status code
|
233
|
+
# and body matches 'account was suspended'
|
234
|
+
class AccountSuspended < Forbidden; end
|
235
|
+
|
236
|
+
# Raised when GitHub returns a 404 HTTP status code
|
237
|
+
class NotFound < ClientError; end
|
238
|
+
|
239
|
+
# Raised when GitHub returns a 404 HTTP status code
|
240
|
+
# and body matches 'Branch not protected'
|
241
|
+
class BranchNotProtected < ClientError; end
|
242
|
+
|
243
|
+
# Raised when GitHub returns a 405 HTTP status code
|
244
|
+
class MethodNotAllowed < ClientError; end
|
245
|
+
|
246
|
+
# Raised when GitHub returns a 406 HTTP status code
|
247
|
+
class NotAcceptable < ClientError; end
|
248
|
+
|
249
|
+
# Raised when GitHub returns a 409 HTTP status code
|
250
|
+
class Conflict < ClientError; end
|
251
|
+
|
252
|
+
# Raised when GitHub returns a 414 HTTP status code
|
253
|
+
class UnsupportedMediaType < ClientError; end
|
254
|
+
|
255
|
+
# Raised when GitHub returns a 422 HTTP status code
|
256
|
+
class UnprocessableEntity < ClientError; end
|
257
|
+
|
258
|
+
# Raised when GitHub returns a 451 HTTP status code
|
259
|
+
class UnavailableForLegalReasons < ClientError; end
|
260
|
+
|
261
|
+
# Raised on errors in the 500-599 range
|
262
|
+
class ServerError < Error; end
|
263
|
+
|
264
|
+
# Raised when GitHub returns a 500 HTTP status code
|
265
|
+
class InternalServerError < ServerError; end
|
266
|
+
|
267
|
+
# Raised when GitHub returns a 501 HTTP status code
|
268
|
+
class NotImplemented < ServerError; end
|
269
|
+
|
270
|
+
# Raised when GitHub returns a 502 HTTP status code
|
271
|
+
class BadGateway < ServerError; end
|
272
|
+
|
273
|
+
# Raised when GitHub returns a 503 HTTP status code
|
274
|
+
class ServiceUnavailable < ServerError; end
|
275
|
+
|
276
|
+
# Raised when client fails to provide valid Content-Type
|
277
|
+
class MissingContentType < ArgumentError; end
|
278
|
+
|
279
|
+
# Raised when a method requires an application client_id
|
280
|
+
# and secret but none is provided
|
281
|
+
class ApplicationCredentialsRequired < StandardError; end
|
282
|
+
|
283
|
+
# Raised when a repository is created with an invalid format
|
284
|
+
class InvalidRepository < ArgumentError; end
|
285
|
+
|
286
|
+
end
|