dependabot-common 0.95.1 → 0.95.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/dependabot.rb +4 -0
  3. data/lib/dependabot/clients/bitbucket.rb +105 -0
  4. data/lib/dependabot/clients/github_with_retries.rb +121 -0
  5. data/lib/dependabot/clients/gitlab.rb +72 -0
  6. data/lib/dependabot/dependency.rb +115 -0
  7. data/lib/dependabot/dependency_file.rb +60 -0
  8. data/lib/dependabot/errors.rb +179 -0
  9. data/lib/dependabot/file_fetchers.rb +18 -0
  10. data/lib/dependabot/file_fetchers/README.md +65 -0
  11. data/lib/dependabot/file_fetchers/base.rb +368 -0
  12. data/lib/dependabot/file_parsers.rb +18 -0
  13. data/lib/dependabot/file_parsers/README.md +45 -0
  14. data/lib/dependabot/file_parsers/base.rb +31 -0
  15. data/lib/dependabot/file_parsers/base/dependency_set.rb +77 -0
  16. data/lib/dependabot/file_updaters.rb +18 -0
  17. data/lib/dependabot/file_updaters/README.md +58 -0
  18. data/lib/dependabot/file_updaters/base.rb +52 -0
  19. data/lib/dependabot/git_commit_checker.rb +412 -0
  20. data/lib/dependabot/metadata_finders.rb +18 -0
  21. data/lib/dependabot/metadata_finders/README.md +53 -0
  22. data/lib/dependabot/metadata_finders/base.rb +117 -0
  23. data/lib/dependabot/metadata_finders/base/changelog_finder.rb +321 -0
  24. data/lib/dependabot/metadata_finders/base/changelog_pruner.rb +177 -0
  25. data/lib/dependabot/metadata_finders/base/commits_finder.rb +221 -0
  26. data/lib/dependabot/metadata_finders/base/release_finder.rb +255 -0
  27. data/lib/dependabot/pull_request_creator.rb +155 -0
  28. data/lib/dependabot/pull_request_creator/branch_namer.rb +170 -0
  29. data/lib/dependabot/pull_request_creator/commit_signer.rb +63 -0
  30. data/lib/dependabot/pull_request_creator/github.rb +277 -0
  31. data/lib/dependabot/pull_request_creator/gitlab.rb +162 -0
  32. data/lib/dependabot/pull_request_creator/labeler.rb +373 -0
  33. data/lib/dependabot/pull_request_creator/message_builder.rb +906 -0
  34. data/lib/dependabot/pull_request_updater.rb +43 -0
  35. data/lib/dependabot/pull_request_updater/github.rb +165 -0
  36. data/lib/dependabot/shared_helpers.rb +224 -0
  37. data/lib/dependabot/source.rb +120 -0
  38. data/lib/dependabot/update_checkers.rb +18 -0
  39. data/lib/dependabot/update_checkers/README.md +67 -0
  40. data/lib/dependabot/update_checkers/base.rb +220 -0
  41. data/lib/dependabot/utils.rb +33 -0
  42. data/lib/dependabot/version.rb +5 -0
  43. data/lib/rubygems_version_patch.rb +14 -0
  44. metadata +44 -2
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/metadata_finders"
4
+
5
+ module Dependabot
6
+ class PullRequestCreator
7
+ require "dependabot/pull_request_creator/github"
8
+ require "dependabot/pull_request_creator/gitlab"
9
+ require "dependabot/pull_request_creator/message_builder"
10
+ require "dependabot/pull_request_creator/branch_namer"
11
+ require "dependabot/pull_request_creator/labeler"
12
+
13
+ class RepoNotFound < StandardError; end
14
+ class RepoArchived < StandardError; end
15
+ class NoHistoryInCommon < StandardError; end
16
+
17
+ attr_reader :source, :dependencies, :files, :base_commit,
18
+ :credentials, :pr_message_footer, :custom_labels,
19
+ :author_details, :signature_key, :vulnerabilities_fixed,
20
+ :reviewers, :assignees, :milestone, :branch_name_separator
21
+
22
+ def initialize(source:, base_commit:, dependencies:, files:, credentials:,
23
+ pr_message_footer: nil, custom_labels: nil,
24
+ author_details: nil, signature_key: nil,
25
+ reviewers: nil, assignees: nil, milestone: nil,
26
+ vulnerabilities_fixed: {}, branch_name_separator: "/",
27
+ label_language: false)
28
+ @dependencies = dependencies
29
+ @source = source
30
+ @base_commit = base_commit
31
+ @files = files
32
+ @credentials = credentials
33
+ @pr_message_footer = pr_message_footer
34
+ @author_details = author_details
35
+ @signature_key = signature_key
36
+ @custom_labels = custom_labels
37
+ @reviewers = reviewers
38
+ @assignees = assignees
39
+ @milestone = milestone
40
+ @vulnerabilities_fixed = vulnerabilities_fixed
41
+ @branch_name_separator = branch_name_separator
42
+ @label_language = label_language
43
+
44
+ check_dependencies_have_previous_version
45
+ end
46
+
47
+ def check_dependencies_have_previous_version
48
+ return if library? && dependencies.all? { |d| requirements_changed?(d) }
49
+ return if dependencies.all?(&:previous_version)
50
+
51
+ raise "Dependencies must have a previous version or changed " \
52
+ "requirement to have a pull request created for them!"
53
+ end
54
+
55
+ def create
56
+ case source.provider
57
+ when "github" then github_creator.create
58
+ when "gitlab" then gitlab_creator.create
59
+ else raise "Unsupported provider #{source.provider}"
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def label_language?
66
+ @label_language
67
+ end
68
+
69
+ def github_creator
70
+ Github.new(
71
+ source: source,
72
+ branch_name: branch_namer.new_branch_name,
73
+ base_commit: base_commit,
74
+ credentials: credentials,
75
+ files: files,
76
+ commit_message: message_builder.commit_message,
77
+ pr_description: message_builder.pr_message,
78
+ pr_name: message_builder.pr_name,
79
+ author_details: author_details,
80
+ signature_key: signature_key,
81
+ labeler: labeler,
82
+ reviewers: reviewers,
83
+ assignees: assignees,
84
+ milestone: milestone
85
+ )
86
+ end
87
+
88
+ def gitlab_creator
89
+ Gitlab.new(
90
+ source: source,
91
+ branch_name: branch_namer.new_branch_name,
92
+ base_commit: base_commit,
93
+ credentials: credentials,
94
+ files: files,
95
+ commit_message: message_builder.commit_message,
96
+ pr_description: message_builder.pr_message,
97
+ pr_name: message_builder.pr_name,
98
+ author_details: author_details,
99
+ labeler: labeler,
100
+ approvers: reviewers,
101
+ assignee: assignees&.first,
102
+ milestone: milestone
103
+ )
104
+ end
105
+
106
+ def message_builder
107
+ @message_builder ||
108
+ MessageBuilder.new(
109
+ source: source,
110
+ dependencies: dependencies,
111
+ files: files,
112
+ credentials: credentials,
113
+ author_details: author_details,
114
+ pr_message_footer: pr_message_footer,
115
+ vulnerabilities_fixed: vulnerabilities_fixed
116
+ )
117
+ end
118
+
119
+ def branch_namer
120
+ @branch_namer ||=
121
+ BranchNamer.new(
122
+ dependencies: dependencies,
123
+ files: files,
124
+ target_branch: source.branch,
125
+ separator: branch_name_separator
126
+ )
127
+ end
128
+
129
+ def labeler
130
+ @labeler ||=
131
+ Labeler.new(
132
+ source: source,
133
+ custom_labels: custom_labels,
134
+ credentials: credentials,
135
+ includes_security_fixes: includes_security_fixes?,
136
+ dependencies: dependencies,
137
+ label_language: label_language?
138
+ )
139
+ end
140
+
141
+ def library?
142
+ return true if files.any? { |file| file.name.end_with?(".gemspec") }
143
+
144
+ dependencies.none?(&:appears_in_lockfile?)
145
+ end
146
+
147
+ def includes_security_fixes?
148
+ vulnerabilities_fixed.values.flatten.any?
149
+ end
150
+
151
+ def requirements_changed?(dependency)
152
+ (dependency.requirements - dependency.previous_requirements).any?
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/metadata_finders"
4
+ require "dependabot/pull_request_creator"
5
+
6
+ module Dependabot
7
+ class PullRequestCreator
8
+ class BranchNamer
9
+ attr_reader :dependencies, :files, :target_branch, :separator
10
+
11
+ def initialize(dependencies:, files:, target_branch:, separator: "/")
12
+ @dependencies = dependencies
13
+ @files = files
14
+ @target_branch = target_branch
15
+ @separator = separator
16
+ end
17
+
18
+ # rubocop:disable Metrics/AbcSize
19
+ # rubocop:disable Metrics/PerceivedComplexity
20
+ # rubocop:disable Metrics/CyclomaticComplexity
21
+ def new_branch_name
22
+ @name ||=
23
+ if dependencies.count > 1 && updating_a_property?
24
+ property_name
25
+ elsif dependencies.count > 1 && updating_a_dependency_set?
26
+ dependency_set.fetch(:group)
27
+ elsif dependencies.count > 1
28
+ dependencies.map(&:name).join("-and-").tr(":", "-")
29
+ elsif library? && ref_changed?(dependencies.first)
30
+ dep = dependencies.first
31
+ "#{dep.name.tr(':', '-')}-#{new_ref(dep)}"
32
+ elsif library?
33
+ dep = dependencies.first
34
+ "#{dep.name.tr(':', '-')}-#{sanitized_requirement(dep)}"
35
+ else
36
+ dep = dependencies.first
37
+ "#{dep.name.tr(':', '-')}-#{new_version(dep)}"
38
+ end
39
+
40
+ branch_name = File.join(prefixes, @name).gsub(%r{/\.}, "/dot-")
41
+
42
+ # Some users need branch names without slashes
43
+ branch_name.gsub("/", separator)
44
+ end
45
+ # rubocop:enable Metrics/AbcSize
46
+ # rubocop:enable Metrics/PerceivedComplexity
47
+ # rubocop:enable Metrics/CyclomaticComplexity
48
+
49
+ private
50
+
51
+ def prefixes
52
+ [
53
+ "dependabot",
54
+ package_manager,
55
+ files.first.directory.tr(" ", "-"),
56
+ target_branch
57
+ ].compact
58
+ end
59
+
60
+ def package_manager
61
+ dependencies.first.package_manager
62
+ end
63
+
64
+ def updating_a_property?
65
+ dependencies.first.
66
+ requirements.
67
+ any? { |r| r.dig(:metadata, :property_name) }
68
+ end
69
+
70
+ def updating_a_dependency_set?
71
+ dependencies.first.
72
+ requirements.
73
+ any? { |r| r.dig(:metadata, :dependency_set) }
74
+ end
75
+
76
+ def property_name
77
+ @property_name ||= dependencies.first.requirements.
78
+ find { |r| r.dig(:metadata, :property_name) }&.
79
+ dig(:metadata, :property_name)
80
+
81
+ raise "No property name!" unless @property_name
82
+
83
+ @property_name
84
+ end
85
+
86
+ def dependency_set
87
+ @dependency_set ||= dependencies.first.requirements.
88
+ find { |r| r.dig(:metadata, :dependency_set) }&.
89
+ dig(:metadata, :dependency_set)
90
+
91
+ raise "No dependency set!" unless @dependency_set
92
+
93
+ @dependency_set
94
+ end
95
+
96
+ def sanitized_requirement(dependency)
97
+ new_library_requirement(dependency).
98
+ delete(" ").
99
+ gsub("!=", "neq-").
100
+ gsub(">=", "gte-").
101
+ gsub("<=", "lte-").
102
+ gsub("~>", "tw-").
103
+ gsub("^", "tw-").
104
+ gsub("||", "or-").
105
+ gsub("~", "approx-").
106
+ gsub("~=", "tw-").
107
+ gsub(/==*/, "eq-").
108
+ gsub(">", "gt-").
109
+ gsub("<", "lt-").
110
+ gsub("*", "star").
111
+ gsub(",", "-and-")
112
+ end
113
+
114
+ def new_version(dependency)
115
+ if dependency.version.match?(/^[0-9a-f]{40}$/)
116
+ return new_ref(dependency) if ref_changed?(dependency)
117
+
118
+ dependency.version[0..6]
119
+ elsif dependency.version == dependency.previous_version &&
120
+ package_manager == "docker"
121
+ dependency.requirements.
122
+ map { |r| r.dig(:source, "digest") || r.dig(:source, :digest) }.
123
+ compact.first.split(":").last[0..6]
124
+ else
125
+ dependency.version
126
+ end
127
+ end
128
+
129
+ def previous_ref(dependency)
130
+ dependency.previous_requirements.map do |r|
131
+ r.dig(:source, "ref") || r.dig(:source, :ref)
132
+ end.compact.first
133
+ end
134
+
135
+ def new_ref(dependency)
136
+ dependency.requirements.map do |r|
137
+ r.dig(:source, "ref") || r.dig(:source, :ref)
138
+ end.compact.first
139
+ end
140
+
141
+ def ref_changed?(dependency)
142
+ previous_ref(dependency) && new_ref(dependency) &&
143
+ previous_ref(dependency) != new_ref(dependency)
144
+ end
145
+
146
+ def new_library_requirement(dependency)
147
+ updated_reqs =
148
+ dependency.requirements - dependency.previous_requirements
149
+
150
+ gemspec =
151
+ updated_reqs.find { |r| r[:file].match?(%r{^[^/]*\.gemspec$}) }
152
+ return gemspec[:requirement] if gemspec
153
+
154
+ updated_reqs.first[:requirement]
155
+ end
156
+
157
+ def library?
158
+ if files.map(&:name).any? { |name| name.end_with?(".gemspec") }
159
+ return true
160
+ end
161
+
162
+ dependencies.none?(&:appears_in_lockfile?)
163
+ end
164
+
165
+ def requirements_changed?(dependency)
166
+ (dependency.requirements - dependency.previous_requirements).any?
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+ require "gpgme"
5
+ require "tmpdir"
6
+ require "dependabot/pull_request_creator"
7
+
8
+ module Dependabot
9
+ class PullRequestCreator
10
+ class CommitSigner
11
+ attr_reader :author_details, :commit_message, :tree_sha, :parent_sha,
12
+ :signature_key
13
+
14
+ def initialize(author_details:, commit_message:, tree_sha:, parent_sha:,
15
+ signature_key:)
16
+ @author_details = author_details
17
+ @commit_message = commit_message
18
+ @tree_sha = tree_sha
19
+ @parent_sha = parent_sha
20
+ @signature_key = signature_key
21
+ end
22
+
23
+ def signature
24
+ email = author_details[:email]
25
+
26
+ dir = Dir.mktmpdir
27
+
28
+ GPGME::Engine.home_dir = dir
29
+ GPGME::Key.import(signature_key)
30
+
31
+ crypto = GPGME::Crypto.new(armor: true)
32
+ opts = { mode: GPGME::SIG_MODE_DETACH, signer: email }
33
+ crypto.sign(commit_object, opts).to_s
34
+ rescue Errno::ENOTEMPTY
35
+ FileUtils.remove_entry(dir, true)
36
+ # This appears to be a Ruby bug which occurs very rarely
37
+ raise if @retrying
38
+
39
+ @retrying = true
40
+ retry
41
+ ensure
42
+ FileUtils.remove_entry(dir, true)
43
+ end
44
+
45
+ private
46
+
47
+ def commit_object
48
+ time_str = Time.parse(author_details[:date]).strftime("%s %z")
49
+ name = author_details[:name]
50
+ email = author_details[:email]
51
+
52
+ [
53
+ "tree #{tree_sha}",
54
+ "parent #{parent_sha}",
55
+ "author #{name} <#{email}> #{time_str}",
56
+ "committer #{name} <#{email}> #{time_str}",
57
+ "",
58
+ commit_message
59
+ ].join("\n")
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,277 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "octokit"
4
+ require "securerandom"
5
+ require "dependabot/clients/github_with_retries"
6
+ require "dependabot/pull_request_creator"
7
+ require "dependabot/pull_request_creator/commit_signer"
8
+
9
+ module Dependabot
10
+ class PullRequestCreator
11
+ class Github
12
+ attr_reader :source, :branch_name, :base_commit, :credentials,
13
+ :files, :pr_description, :pr_name, :commit_message,
14
+ :author_details, :signature_key,
15
+ :labeler, :reviewers, :assignees, :milestone
16
+
17
+ def initialize(source:, branch_name:, base_commit:, credentials:,
18
+ files:, commit_message:, pr_description:, pr_name:,
19
+ author_details:, signature_key:,
20
+ labeler:, reviewers:, assignees:, milestone:)
21
+ @source = source
22
+ @branch_name = branch_name
23
+ @base_commit = base_commit
24
+ @credentials = credentials
25
+ @files = files
26
+ @commit_message = commit_message
27
+ @pr_description = pr_description
28
+ @pr_name = pr_name
29
+ @author_details = author_details
30
+ @signature_key = signature_key
31
+ @labeler = labeler
32
+ @reviewers = reviewers
33
+ @assignees = assignees
34
+ @milestone = milestone
35
+ end
36
+
37
+ def create
38
+ return if branch_exists? && pull_request_exists?
39
+
40
+ commit = create_commit
41
+ branch = create_or_update_branch(commit)
42
+ return unless branch
43
+
44
+ pull_request = create_pull_request
45
+ return unless pull_request
46
+
47
+ annotate_pull_request(pull_request)
48
+
49
+ pull_request
50
+ rescue Octokit::Error => error
51
+ handle_error(error)
52
+ end
53
+
54
+ private
55
+
56
+ def github_client_for_source
57
+ @github_client_for_source ||=
58
+ Dependabot::Clients::GithubWithRetries.for_source(
59
+ source: source,
60
+ credentials: credentials
61
+ )
62
+ end
63
+
64
+ def branch_exists?
65
+ @branch_ref ||=
66
+ github_client_for_source.ref(source.repo, "heads/#{branch_name}")
67
+ if @branch_ref.is_a?(Array)
68
+ @branch_ref.any? { |r| r.ref == "refs/heads/#{branch_name}" }
69
+ else
70
+ @branch_ref.ref == "refs/heads/#{branch_name}"
71
+ end
72
+ rescue Octokit::NotFound
73
+ false
74
+ end
75
+
76
+ def pull_request_exists?
77
+ github_client_for_source.pull_requests(
78
+ source.repo,
79
+ head: "#{source.repo.split('/').first}:#{branch_name}",
80
+ state: "all"
81
+ ).any?
82
+ rescue Octokit::InternalServerError
83
+ # A GitHub bug sometimes means adding `state: all` causes problems.
84
+ # In that case, fall back to making two separate requests.
85
+ open_prs = github_client_for_source.pull_requests(
86
+ source.repo,
87
+ head: "#{source.repo.split('/').first}:#{branch_name}",
88
+ state: "open"
89
+ )
90
+
91
+ closed_prs = github_client_for_source.pull_requests(
92
+ source.repo,
93
+ head: "#{source.repo.split('/').first}:#{branch_name}",
94
+ state: "closed"
95
+ )
96
+
97
+ [*open_prs, *closed_prs].any?
98
+ end
99
+
100
+ def repo_exists?
101
+ github_client_for_source.repo(source.repo)
102
+ true
103
+ rescue Octokit::NotFound
104
+ false
105
+ end
106
+
107
+ def create_commit
108
+ tree = create_tree
109
+
110
+ options = author_details&.any? ? { author: author_details } : {}
111
+
112
+ if options[:author]&.any? && signature_key
113
+ options[:author][:date] = Time.now.utc.iso8601
114
+ options[:signature] = commit_signature(tree, options[:author])
115
+ end
116
+
117
+ github_client_for_source.create_commit(
118
+ source.repo,
119
+ commit_message,
120
+ tree.sha,
121
+ base_commit,
122
+ options
123
+ )
124
+ end
125
+
126
+ def create_tree
127
+ file_trees = files.map do |file|
128
+ if file.type == "submodule"
129
+ {
130
+ path: file.path.sub(%r{^/}, ""),
131
+ mode: "160000",
132
+ type: "commit",
133
+ sha: file.content
134
+ }
135
+ else
136
+ {
137
+ path: file.path.sub(%r{^/}, ""),
138
+ mode: "100644",
139
+ type: "blob",
140
+ content: file.content
141
+ }
142
+ end
143
+ end
144
+
145
+ github_client_for_source.create_tree(
146
+ source.repo,
147
+ file_trees,
148
+ base_tree: base_commit
149
+ )
150
+ end
151
+
152
+ def create_or_update_branch(commit)
153
+ branch_exists? ? update_branch(commit) : create_branch(commit)
154
+ rescue Octokit::UnprocessableEntity
155
+ # A race condition may cause GitHub to fail here, in which case we retry
156
+ retry_count ||= 0
157
+ retry_count += 1
158
+ retry unless retry_count >= 2
159
+ end
160
+
161
+ def create_branch(commit)
162
+ github_client_for_source.create_ref(
163
+ source.repo,
164
+ "heads/#{branch_name}",
165
+ commit.sha
166
+ )
167
+ rescue Octokit::UnprocessableEntity => error
168
+ # Return quietly in the case of a race
169
+ return nil if error.message.match?(/Reference already exists/i)
170
+ raise if @retrying_branch_creation
171
+
172
+ @retrying_branch_creation = true
173
+
174
+ # Branch creation will fail if a branch called `dependabot` already
175
+ # exists, since git won't be able to create a folder with the same name
176
+ @branch_name = SecureRandom.hex[0..3] + @branch_name
177
+ retry
178
+ end
179
+
180
+ def update_branch(commit)
181
+ github_client_for_source.update_ref(
182
+ source.repo,
183
+ "heads/#{branch_name}",
184
+ commit.sha,
185
+ true
186
+ )
187
+ end
188
+
189
+ def annotate_pull_request(pull_request)
190
+ labeler.label_pull_request(pull_request.number)
191
+ add_reviewers_to_pull_request(pull_request) if reviewers&.any?
192
+ add_assignees_to_pull_request(pull_request) if assignees&.any?
193
+ add_milestone_to_pull_request(pull_request) if milestone
194
+ end
195
+
196
+ def add_reviewers_to_pull_request(pull_request)
197
+ reviewers_hash =
198
+ Hash[reviewers.keys.map { |k| [k.to_sym, reviewers[k]] }]
199
+
200
+ github_client_for_source.request_pull_request_review(
201
+ source.repo,
202
+ pull_request.number,
203
+ reviewers: reviewers_hash[:reviewers] || [],
204
+ team_reviewers: reviewers_hash[:team_reviewers] || []
205
+ )
206
+ rescue Octokit::UnprocessableEntity => error
207
+ return if error.message.include?("not a collaborator")
208
+ return if error.message.include?("Could not resolve to a node")
209
+
210
+ raise
211
+ end
212
+
213
+ def add_assignees_to_pull_request(pull_request)
214
+ github_client_for_source.add_assignees(
215
+ source.repo,
216
+ pull_request.number,
217
+ assignees
218
+ )
219
+ end
220
+
221
+ def add_milestone_to_pull_request(pull_request)
222
+ github_client_for_source.update_issue(
223
+ source.repo,
224
+ pull_request.number,
225
+ milestone: milestone
226
+ )
227
+ end
228
+
229
+ def create_pull_request
230
+ github_client_for_source.create_pull_request(
231
+ source.repo,
232
+ source.branch || default_branch,
233
+ branch_name,
234
+ pr_name,
235
+ pr_description
236
+ )
237
+ rescue Octokit::UnprocessableEntity => error
238
+ # Ignore races that we lose
239
+ raise unless error.message.include?("pull request already exists")
240
+ end
241
+
242
+ def default_branch
243
+ @default_branch ||=
244
+ github_client_for_source.repository(source.repo).default_branch
245
+ end
246
+
247
+ def commit_signature(tree, author_details_with_date)
248
+ CommitSigner.new(
249
+ author_details: author_details_with_date,
250
+ commit_message: commit_message,
251
+ tree_sha: tree.sha,
252
+ parent_sha: base_commit,
253
+ signature_key: signature_key
254
+ ).signature
255
+ end
256
+
257
+ def handle_error(error)
258
+ case error
259
+ when Octokit::Forbidden
260
+ raise error unless error.message.include?("Repository was archived")
261
+
262
+ raise RepoArchived, error.message
263
+ when Octokit::NotFound
264
+ raise error if repo_exists?
265
+
266
+ raise RepoNotFound, error.message
267
+ when Octokit::UnprocessableEntity
268
+ raise error unless error.message.include?("no history in common")
269
+
270
+ raise NoHistoryInCommon, error.message
271
+ else
272
+ raise error
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end