octokit 4.15.0 → 4.21.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -23,6 +23,18 @@ module Octokit
23
23
  alias :references :refs
24
24
  alias :list_references :refs
25
25
 
26
+ # Fetch matching refs
27
+ #
28
+ # @param repo [Integer, String, Repository, Hash] A GitHub repository
29
+ # @param ref [String] The ref, e.g. <tt>tags/v0.0.3</tt> or <tt>heads/rails-3</tt>
30
+ # @return [Array<Sawyer::Resource>] The reference matching the given repo and the ref id
31
+ # @see https://developer.github.com/v3/git/refs/#list-matching-references
32
+ # @example Fetch refs matching tags/v2 for sferik/rails_admin
33
+ # Octokit.ref("sferik/rails_admin","tags/v2")
34
+ def matching_refs(repo, ref, options = {})
35
+ paginate "#{Repository.path repo}/git/matching-refs/#{ref}", options
36
+ end
37
+
26
38
  # Fetch a given reference
27
39
  #
28
40
  # @param repo [Integer, String, Repository, Hash] A GitHub repository
@@ -60,11 +72,13 @@ module Octokit
60
72
  # @param repo [Integer, String, Repository, Hash] A GitHub repository
61
73
  # @param ref [String] The ref, e.g. <tt>tags/v0.0.3</tt>
62
74
  # @param sha [String] A SHA, e.g. <tt>827efc6d56897b048c772eb4087f854f46256132</tt>
63
- # @param force [Boolean] A flag indicating one wants to force the update to make sure the update is a fast-forward update.
75
+ # @param force [Boolean] A flag indicating whether to force the update or to make sure the update is a fast-forward update.
64
76
  # @return [Array<Sawyer::Resource>] The list of references updated
65
77
  # @see https://developer.github.com/v3/git/refs/#update-a-reference
66
78
  # @example Force update heads/sc/featureA for octocat/Hello-World with sha aa218f56b14c9653891f9e74264a383fa43fefbd
67
79
  # Octokit.update_ref("octocat/Hello-World", "heads/sc/featureA", "aa218f56b14c9653891f9e74264a383fa43fefbd")
80
+ # @example Fast-forward update heads/sc/featureA for octocat/Hello-World with sha aa218f56b14c9653891f9e74264a383fa43fefbd
81
+ # Octokit.update_ref("octocat/Hello-World", "heads/sc/featureA", "aa218f56b14c9653891f9e74264a383fa43fefbd", false)
68
82
  def update_ref(repo, ref, sha, force = true, options = {})
69
83
  parameters = {
70
84
  :sha => sha,
@@ -79,11 +93,13 @@ module Octokit
79
93
  # @param repo [Integer, String, Repository, Hash] A GitHub repository
80
94
  # @param branch [String] The ref, e.g. <tt>feature/new-shiny</tt>
81
95
  # @param sha [String] A SHA, e.g. <tt>827efc6d56897b048c772eb4087f854f46256132</tt>
82
- # @param force [Boolean] A flag indicating one wants to force the update to make sure the update is a fast-forward update.
96
+ # @param force [Boolean] A flag indicating whether to force the update or to make sure the update is a fast-forward update.
83
97
  # @return [Array<Sawyer::Resource>] The list of references updated
84
98
  # @see https://developer.github.com/v3/git/refs/#update-a-reference
85
99
  # @example Force update heads/sc/featureA for octocat/Hello-World with sha aa218f56b14c9653891f9e74264a383fa43fefbd
86
- # Octokit.update_branch("octocat/Hello-World","sc/featureA", "aa218f56b14c9653891f9e74264a383fa43fefbd")
100
+ # Octokit.update_branch("octocat/Hello-World", "sc/featureA", "aa218f56b14c9653891f9e74264a383fa43fefbd")
101
+ # @example Fast-forward update heads/sc/featureA for octocat/Hello-World with sha aa218f56b14c9653891f9e74264a383fa43fefbd
102
+ # Octokit.update_branch("octocat/Hello-World", "sc/featureA", "aa218f56b14c9653891f9e74264a383fa43fefbd", false)
87
103
  def update_branch(repo, branch, sha, force = true, options = {})
88
104
  update_ref repo, "heads/#{branch}", sha, force, options
89
105
  end
@@ -32,7 +32,7 @@ module Octokit
32
32
 
33
33
  # Edit a repository
34
34
  #
35
- # @see https://developer.github.com/v3/repos/#edit
35
+ # @see https://developer.github.com/v3/repos/#update-a-repository
36
36
  # @param repo [String, Hash, Repository] A GitHub repository
37
37
  # @param options [Hash] Repository information to update
38
38
  # @option options [String] :name Name of the repo
@@ -429,7 +429,7 @@ module Octokit
429
429
  # @example List topics for octokit/octokit.rb
430
430
  # Octokit.topics('octokit/octokit.rb')
431
431
  # @example List topics for octokit/octokit.rb
432
- # client.topics('octokit/octokit.rb')
432
+ # client.topics('octokit/octokit.rb')
433
433
  def topics(repo, options = {})
434
434
  opts = ensure_api_media_type(:topics, options)
435
435
  paginate "#{Repository.path repo}/topics", opts
@@ -586,14 +586,14 @@ module Octokit
586
586
  #
587
587
  # @param repo [Integer, String, Hash, Repository] A GitHub repository.
588
588
  # @param branch [String] Branch name
589
- # @option options [Hash] :required_status_checks If not null, the following keys are required:
590
- # <tt>:enforce_admins [boolean] Enforce required status checks for repository administrators.</tt>
591
- # <tt>:strict [boolean] Require branches to be up to date before merging.</tt>
592
- # <tt>:contexts [Array] The list of status checks to require in order to merge into this branch</tt>
589
+ # @option options [Hash] :required_status_checks If not null, the following keys are required:
590
+ # <tt>:enforce_admins [boolean] Enforce required status checks for repository administrators.</tt>
591
+ # <tt>:strict [boolean] Require branches to be up to date before merging.</tt>
592
+ # <tt>:contexts [Array] The list of status checks to require in order to merge into this branch</tt>
593
593
  #
594
594
  # @option options [Hash] :restrictions If not null, the following keys are required:
595
- # <tt>:users [Array] The list of user logins with push access</tt>
596
- # <tt>:teams [Array] The list of team slugs with push access</tt>.
595
+ # <tt>:users [Array] The list of user logins with push access</tt>
596
+ # <tt>:teams [Array] The list of team slugs with push access</tt>.
597
597
  #
598
598
  # Teams and users restrictions are only available for organization-owned repositories.
599
599
  # @return [Sawyer::Resource] The protected branch
@@ -640,6 +640,24 @@ module Octokit
640
640
  boolean_from_response :delete, "#{Repository.path repo}/branches/#{branch}/protection", opts
641
641
  end
642
642
 
643
+ # Rename a single branch from a repository
644
+ #
645
+ # Requires authenticated client
646
+ #
647
+ # @param repo [Integer, String, Hash, Repository] A GitHub repository.
648
+ # @param branch [String] Current branch name
649
+ # @param new_name [String] New branch name
650
+ # @return [Sawyer::Resource] The renamed branch
651
+ # @see https://developer.github.com/v3/repos/#rename-a-branch
652
+ # @example
653
+ # @client.rename_branch('octokit/octokit.rb', 'master', 'main')
654
+ def rename_branch(repo, branch, new_name, options = {})
655
+ params = {
656
+ new_name: new_name,
657
+ }
658
+ post "#{Repository.path repo}/branches/#{branch}/rename", params.merge(options)
659
+ end
660
+
643
661
  # List users available for assigning to issues.
644
662
  #
645
663
  # Requires authenticated client for private repos.
@@ -720,6 +738,62 @@ module Octokit
720
738
  def delete_subscription(repo, options = {})
721
739
  boolean_from_response :delete, "#{Repository.path repo}/subscription", options
722
740
  end
741
+
742
+ # Create a repository dispatch event
743
+ #
744
+ # @param repo [Integer, String, Hash, Repository] A GitHub repository.
745
+ # @param event_type [String] A custom webhook event name.
746
+ # @option options [Hash] :client_payload payload with extra information
747
+ # about the webhook event that your action or worklow may use.
748
+ #
749
+ # @return [Boolean] True if event was dispatched, false otherwise.
750
+ # @see https://developer.github.com/v3/repos/#create-a-repository-dispatch-event
751
+ def dispatch_event(repo, event_type, options = {})
752
+ boolean_from_response :post, "#{Repository.path repo}/dispatches", options.merge({ event_type: event_type })
753
+ end
754
+
755
+ # Check to see if vulnerability alerts are enabled for a repository
756
+ #
757
+ # The authenticated user must have admin access to the repository.
758
+ #
759
+ # @param repo [Integer, String, Hash, Repository] A GitHub repository.
760
+ # @return [Boolean] True if vulnerability alerts are enabled, false otherwise.
761
+ # @see https://docs.github.com/en/rest/reference/repos#check-if-vulnerability-alerts-are-enabled-for-a-repository
762
+ #
763
+ # @example
764
+ # @client.vulnerability_alerts_enabled?("octokit/octokit.rb")
765
+ def vulnerability_alerts_enabled?(repo, options = {})
766
+ opts = ensure_api_media_type(:vulnerability_alerts, options)
767
+ boolean_from_response(:get, "#{Repository.path repo}/vulnerability-alerts", opts)
768
+ end
769
+
770
+ # Enable vulnerability alerts for a repository
771
+ #
772
+ # @param repo [Integer, String, Hash, Repository] A GitHub repository.
773
+ # @param options [Hash]
774
+ #
775
+ # @return [Boolean] True if vulnerability alerts enabled, false otherwise.
776
+ # @see https://docs.github.com/en/rest/reference/repos#enable-vulnerability-alerts
777
+ # @example Enable vulnerability alerts for a repository
778
+ # @client.enable_vulnerability_alerts("octokit/octokit.rb")
779
+ def enable_vulnerability_alerts(repo, options = {})
780
+ opts = ensure_api_media_type(:vulnerability_alerts, options)
781
+ boolean_from_response(:put, "#{Repository.path repo}/vulnerability-alerts", opts)
782
+ end
783
+
784
+ # Disable vulnerability alerts for a repository
785
+ #
786
+ # @param repo [Integer, String, Hash, Repository] A GitHub repository.
787
+ # @param options [Hash]
788
+ #
789
+ # @return [Boolean] True if vulnerability alerts disabled, false otherwise.
790
+ # @see https://docs.github.com/en/rest/reference/repos#disable-vulnerability-alerts
791
+ # @example Disable vulnerability alerts for a repository
792
+ # @client.disable_vulnerability_alerts("octokit/octokit.rb")
793
+ def disable_vulnerability_alerts(repo, options = {})
794
+ opts = ensure_api_media_type(:vulnerability_alerts, options)
795
+ boolean_from_response(:delete, "#{Repository.path repo}/vulnerability-alerts", opts)
796
+ end
723
797
  end
724
798
  end
725
799
  end
@@ -44,7 +44,7 @@ module Octokit
44
44
  # @option options [Integer] :page Page of paginated results
45
45
  # @option options [Integer] :per_page Number of items per page
46
46
  # @return [Sawyer::Resource] Search results object
47
- # @see https://developer.github.com/v3/search/#search-issues
47
+ # @see https://developer.github.com/v3/search/#search-issues-and-pull-requests
48
48
  def search_issues(query, options = {})
49
49
  search "search/issues", query, options
50
50
  end
@@ -340,6 +340,92 @@ module Octokit
340
340
  end
341
341
  alias :watched :subscriptions
342
342
 
343
+ # Initiates the generation of a migration archive.
344
+ #
345
+ # Requires authenticated user.
346
+ #
347
+ # @param repositories [Array<String>] :repositories Repositories for the organization.
348
+ # @option options [Boolean, optional] :lock_repositories Indicates whether repositories should be locked during migration
349
+ # @option options [Boolean, optional] :exclude_attachments Exclude attachments fro the migration data
350
+ # @return [Sawyer::Resource] Hash representing the new migration.
351
+ # @example
352
+ # @client.start_migration(['octocat/hello-world'])
353
+ # @see https://docs.github.com/en/rest/reference/migrations#start-a-user-migration
354
+ def start_user_migration(repositories, options = {})
355
+ options = ensure_api_media_type(:migrations, options)
356
+ options[:repositories] = repositories
357
+ post "user/migrations", options
358
+ end
359
+
360
+ # Lists the most recent migrations.
361
+ #
362
+ # Requires authenticated user.
363
+ #
364
+ # @return [Array<Sawyer::Resource>] Array of migration resources.
365
+ # @see https://docs.github.com/en/rest/reference/migrations#list-user-migrations
366
+ def user_migrations(options = {})
367
+ options = ensure_api_media_type(:migrations, options)
368
+ paginate "user/migrations", options
369
+ end
370
+
371
+ # Fetches the status of a migration.
372
+ #
373
+ # Requires authenticated user.
374
+ #
375
+ # @param id [Integer] ID number of the migration.
376
+ # @see https://docs.github.com/en/rest/reference/migrations#get-a-user-migration-status
377
+ def user_migration_status(id, options = {})
378
+ options = ensure_api_media_type(:migrations, options)
379
+ get "user/migrations/#{id}", options
380
+ end
381
+
382
+ # Fetches the URL to a migration archive.
383
+ #
384
+ # Requires authenticated user.
385
+ #
386
+ # @param id [Integer] ID number of the migration.
387
+ # @see https://docs.github.com/en/rest/reference/migrations#download-a-user-migration-archive
388
+ def user_migration_archive_url(id, options = {})
389
+ options = ensure_api_media_type(:migrations, options)
390
+ url = "user/migrations/#{id}/archive"
391
+
392
+ response = client_without_redirects(options).get(url)
393
+ response.headers['location']
394
+ end
395
+
396
+ # Deletes a previous migration archive.
397
+ #
398
+ # Requires authenticated user.
399
+ #
400
+ # @param id [Integer] ID number of the migration.
401
+ # @see https://docs.github.com/en/rest/reference/migrations#delete-a-user-migration-archive
402
+ def delete_user_migration_archive(id, options = {})
403
+ options = ensure_api_media_type(:migrations, options)
404
+ delete "user/migrations/#{id}/archive", options
405
+ end
406
+
407
+ # List repositories for a user migration.
408
+ #
409
+ # Requires authenticated user.
410
+ #
411
+ # @param id [Integer] ID number of the migration.
412
+ # @see https://docs.github.com/en/rest/reference/migrations#list-repositories-for-a-user-migration
413
+ def user_migration_repositories(id, options = {})
414
+ options = ensure_api_media_type(:migrations, options)
415
+ get "user/migrations/#{id}/repositories", options
416
+ end
417
+
418
+ # Unlock a user repository which has been locked by a migration.
419
+ #
420
+ # Requires authenticated user.
421
+ #
422
+ # @param id [Integer] ID number of the migration.
423
+ # @param repo [String] Name of the repository.
424
+ # @see https://docs.github.com/en/rest/reference/migrations#unlock-a-user-repository
425
+ def unlock_user_repository(id, repo, options = {})
426
+ options = ensure_api_media_type(:migrations, options)
427
+ delete "user/migrations/#{id}/repos/#{repo}/lock", options
428
+ end
343
429
  end
344
430
 
345
431
  private
@@ -113,7 +113,7 @@ module Octokit
113
113
  elsif bearer_authenticated?
114
114
  http.authorization 'Bearer', @bearer_token
115
115
  elsif application_authenticated?
116
- http.params = http.params.merge application_authentication
116
+ http.basic_auth(@client_id, @client_secret)
117
117
  end
118
118
  end
119
119
  end
@@ -155,6 +155,9 @@ module Octokit
155
155
 
156
156
  @last_response = response = agent.call(method, Addressable::URI.parse(path.to_s).normalize.to_s, data, options)
157
157
  response.data
158
+ rescue Octokit::Error => error
159
+ @last_response = nil
160
+ raise error
158
161
  end
159
162
 
160
163
  # Executes the request, checking if it was successful
@@ -162,7 +165,7 @@ module Octokit
162
165
  # @return [Boolean] True on success, false otherwise
163
166
  def boolean_from_response(method, path, options = {})
164
167
  request(method, path, options)
165
- @last_response.status == 204
168
+ [201, 202, 204].include? @last_response.status
166
169
  rescue Octokit::NotFound
167
170
  false
168
171
  end
@@ -177,12 +180,12 @@ module Octokit
177
180
  conn_opts[:proxy] = @proxy if @proxy
178
181
  if conn_opts[:ssl].nil?
179
182
  conn_opts[:ssl] = { :verify_mode => @ssl_verify_mode } if @ssl_verify_mode
180
- else
181
- if @connection_options[:ssl][:verify] == false
182
- conn_opts[:ssl] = { :verify_mode => 0}
183
- else
184
- conn_opts[:ssl] = { :verify_mode => @ssl_verify_mode }
185
- end
183
+ else
184
+ verify = @connection_options[:ssl][:verify]
185
+ conn_opts[:ssl] = {
186
+ :verify => verify,
187
+ :verify_mode => verify == false ? 0 : @ssl_verify_mode
188
+ }
186
189
  end
187
190
  opts[:faraday] = Faraday.new(conn_opts)
188
191
 
data/lib/octokit/error.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module Octokit
2
2
  # Custom error class for rescuing from all GitHub errors
3
3
  class Error < StandardError
4
-
4
+ attr_reader :context
5
5
  # Returns the appropriate Octokit::Error subclass based
6
6
  # on status and response message
7
7
  #
@@ -21,7 +21,7 @@ module Octokit
21
21
  when 406 then Octokit::NotAcceptable
22
22
  when 409 then Octokit::Conflict
23
23
  when 415 then Octokit::UnsupportedMediaType
24
- when 422 then Octokit::UnprocessableEntity
24
+ when 422 then error_for_422(body)
25
25
  when 451 then Octokit::UnavailableForLegalReasons
26
26
  when 400..499 then Octokit::ClientError
27
27
  when 500 then Octokit::InternalServerError
@@ -34,9 +34,16 @@ module Octokit
34
34
  end
35
35
  end
36
36
 
37
+ def build_error_context
38
+ if RATE_LIMITED_ERRORS.include?(self.class)
39
+ @context = Octokit::RateLimit.from_response(@response)
40
+ end
41
+ end
42
+
37
43
  def initialize(response=nil)
38
44
  @response = response
39
45
  super(build_error_message)
46
+ build_error_context
40
47
  end
41
48
 
42
49
  # Documentation URL returned by the API for some errors
@@ -77,6 +84,8 @@ module Octokit
77
84
  Octokit::BillingIssue
78
85
  elsif body =~ /Resource protected by organization SAML enforcement/i
79
86
  Octokit::SAMLProtected
87
+ elsif body =~ /suspended your access|This installation has been suspended/i
88
+ Octokit::InstallationSuspended
80
89
  else
81
90
  Octokit::Forbidden
82
91
  end
@@ -92,6 +101,18 @@ module Octokit
92
101
  end
93
102
  end
94
103
 
104
+ # Return most appropriate error for 422 HTTP status code
105
+ # @private
106
+ def self.error_for_422(body)
107
+ if body =~ /PullRequestReviewComment/i && body =~ /(commit_id|end_commit_oid) is not part of the pull request/i
108
+ Octokit::CommitIsNotPartOfPullRequest
109
+ elsif body =~ /Path diff too large/i
110
+ Octokit::PathDiffTooLarge
111
+ else
112
+ Octokit::UnprocessableEntity
113
+ end
114
+ end
115
+
95
116
  # Array of validation errors
96
117
  # @return [Array<Hash>] Error info
97
118
  def errors
@@ -265,6 +286,10 @@ module Octokit
265
286
  # and body matches 'Resource protected by organization SAML enforcement'
266
287
  class SAMLProtected < Forbidden; end
267
288
 
289
+ # Raised when GitHub returns a 403 HTTP status code
290
+ # and body matches 'suspended your access'
291
+ class InstallationSuspended < Forbidden; end
292
+
268
293
  # Raised when GitHub returns a 404 HTTP status code
269
294
  class NotFound < ClientError; end
270
295
 
@@ -287,6 +312,14 @@ module Octokit
287
312
  # Raised when GitHub returns a 422 HTTP status code
288
313
  class UnprocessableEntity < ClientError; end
289
314
 
315
+ # Raised when GitHub returns a 422 HTTP status code
316
+ # and body matches 'PullRequestReviewComment' and 'commit_id (or end_commit_oid) is not part of the pull request'
317
+ class CommitIsNotPartOfPullRequest < UnprocessableEntity; end
318
+
319
+ # Raised when GitHub returns a 422 HTTP status code and body matches 'Path diff too large'.
320
+ # It could occur when attempting to post review comments on a "too large" file.
321
+ class PathDiffTooLarge < UnprocessableEntity; end
322
+
290
323
  # Raised when GitHub returns a 451 HTTP status code
291
324
  class UnavailableForLegalReasons < ClientError; end
292
325
 
@@ -315,4 +348,5 @@ module Octokit
315
348
  # Raised when a repository is created with an invalid format
316
349
  class InvalidRepository < ArgumentError; end
317
350
 
351
+ RATE_LIMITED_ERRORS = [Octokit::TooManyRequests, Octokit::AbuseDetected]
318
352
  end
@@ -4,10 +4,11 @@ module Octokit
4
4
  module Preview
5
5
 
6
6
  PREVIEW_TYPES = {
7
+ :applications_api => 'application/vnd.github.doctor-strange-preview+json'.freeze,
7
8
  :branch_protection => 'application/vnd.github.luke-cage-preview+json'.freeze,
8
- :checks => 'application/vnd.github.antiope-preview+json'.freeze,
9
9
  :commit_search => 'application/vnd.github.cloak-preview+json'.freeze,
10
10
  :commit_pulls => 'application/vnd.github.groot-preview+json'.freeze,
11
+ :commit_branches => 'application/vnd.github.groot-preview+json'.freeze,
11
12
  :migrations => 'application/vnd.github.wyandotte-preview+json'.freeze,
12
13
  :licenses => 'application/vnd.github.drax-preview+json'.freeze,
13
14
  :source_imports => 'application/vnd.github.barred-rock-preview'.freeze,
@@ -18,13 +19,12 @@ module Octokit
18
19
  :pages => 'application/vnd.github.mister-fantastic-preview+json'.freeze,
19
20
  :projects => 'application/vnd.github.inertia-preview+json'.freeze,
20
21
  :traffic => 'application/vnd.github.spiderman-preview'.freeze,
21
- :integrations => 'application/vnd.github.machine-man-preview+json'.freeze,
22
22
  :topics => 'application/vnd.github.mercy-preview+json'.freeze,
23
23
  :community_profile => 'application/vnd.github.black-panther-preview+json'.freeze,
24
24
  :strict_validation => 'application/vnd.github.speedy-preview+json'.freeze,
25
- :drafts => 'application/vnd.github.shadow-cat-preview'.freeze,
26
25
  :template_repositories => 'application/vnd.github.baptiste-preview+json'.freeze,
27
- :uninstall_github_app => 'application/vnd.github.gambit-preview+json'.freeze,
26
+ :project_card_events => 'application/vnd.github.starfox-preview+json'.freeze,
27
+ :vulnerability_alerts => 'application/vnd.github.dorian-preview+json'.freeze,
28
28
  }
29
29
 
30
30
  def ensure_api_media_type(type, options)
@@ -20,7 +20,7 @@ module Octokit
20
20
  # @return [RateLimit]
21
21
  def self.from_response(response)
22
22
  info = new
23
- if response && !response.headers.nil?
23
+ if response && response.respond_to?(:headers) && !response.headers.nil?
24
24
  info.limit = (response.headers['X-RateLimit-Limit'] || 1).to_i
25
25
  info.remaining = (response.headers['X-RateLimit-Remaining'] || 1).to_i
26
26
  info.resets_at = Time.at((response.headers['X-RateLimit-Reset'] || Time.now).to_i)