This repository has been archived by the owner on Oct 20, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
/
migrate_issues.rb
208 lines (177 loc) · 6.89 KB
/
migrate_issues.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
require 'octokit'
require 'pry'
require 'excon'
require 'colored'
require 'json'
class Hendl
# e.g. KrauseFx/fastlane
attr_accessor :source
attr_accessor :destination
attr_accessor :open_only
# Reason on why this was necessary
attr_accessor :reason
def initialize(source: nil, destination: nil, reason: nil, open_only: false)
self.source = source
self.destination = destination
self.reason = reason
self.open_only = open_only
self.start
end
def client
@client ||= Octokit::Client.new(access_token: ENV["GITHUB_API_TOKEN"])
end
def start
client.auto_paginate = true
puts "Fetching issues from '#{source}'..."
counter = 0
client.issues(source, per_page: 1000, state: "all").each do |original|
if self.open_only and original.state != "open"
puts "Skipping #{original.number} as it's not an open one"
next
end
next unless original.pull_request.nil? # no PRs for now
labels = original.labels.collect { |a| a[:name] }
if labels.include?("migrated") or labels.include?("migration_failed")
puts "Skipping #{original.number} because it's already migrated or failed"
next
end
hendl(original)
smart_sleep
counter += 1
end
puts "[SUCCESS] Migrated #{counter} issues / PRs"
end
def hendl(original)
puts "Hendling #{original.number}"
if original.pull_request.nil?
hendl_issue(original)
else
# hendl_pr(original)
end
end
def smart_sleep
# via https://developer.github.com/guides/best-practices-for-integrators/#dealing-with-abuse-rate-limits
# at least one second between requests
# also https://developer.github.com/v3/#rate-limiting
# maximum of 5000 requests an hour => 83 requests per minute
sleep 2.5
end
def table(user_id, body)
"<table>
<tr>
<td>
<img src='https://avatars0.githubusercontent.com/u/#{user_id}?v=3&s=70' width='35'>
</td>
<td>
#{body}
</td>
</tr>
</table>"
end
# We copy over all the issues, and also mention everyone
# so that people are automatically subscribed to notifications
def hendl_issue(original)
original_comments = client.issue_comments(source, original.number)
comments = []
original_comments.each do |original_comment|
table_code = table(original_comment.user.id, "@#{original_comment.user.login} commented")
body = [table_code, original_comment.body]
comments << {
created_at: original_comment.created_at.iso8601,
body: body.join("\n\n")
}
end
actual_label = original.labels.collect { |a| a[:name] }
tool_name_label = source.split("/").last
table_link = "Imported from <a href='#{original.html_url}'>#{source}##{original.number}</a>"
table_code = table(original.user.id, "Original issue by @#{original.user.login} - #{table_link}")
body = [table_code, original.body]
data = {
issue: {
title: original.title,
body: body.join("\n\n"),
created_at: original.created_at.iso8601,
labels: actual_label + [tool_name_label],
closed: original.state != "open"
},
comments: comments
}
data[:issue][:closed_at] = original.closed_at.iso8601 if original.state != "open"
response = Excon.post("https://api.github.com/repos/#{destination}/import/issues", body: data.to_json, headers: request_headers)
response = JSON.parse(response.body)
status_url = response['url']
puts response
new_issue_url = nil
begin
(5..35).each do |request_num|
sleep(request_num)
puts "Sending #{status_url}"
async_response = Excon.get(status_url, headers: request_headers) # if this crashes, make sure to have a valid token with admin permission to the actual repo
async_response = JSON.parse(async_response.body)
puts async_response.to_s.yellow
new_issue_url = async_response['issue_url']
break if new_issue_url.to_s.length > 0
puts "unable to get new issue url for #{original.number} after #{request_num - 4} requests".yellow
end
rescue => ex
puts "Something went wrong, wups"
puts ex.to_s
# If the error message is
# {"message"=>"Not Found", "documentation_url"=>"https://developer.github.com/v3"}
# that just means that fastlane-bot doesn't have admin access
end
if new_issue_url.to_s.length > 0
new_issue_url.gsub!("api.github.com/repos", "github.com")
client.update_issue(source, original.number, labels: (actual_label + ["migrated"]))
# reason, link to the new issue
puts "closing old issue #{original.number}"
body = []
body << "This issue was migrated to #{new_issue_url}. Please post all further comments there."
body << reason
puts new_issue_url
client.add_comment(source, original.number, body.join("\n\n"))
smart_sleep
client.close_issue(source, original.number) unless original.state == "closed"
else
puts "unable to find new issue url, not closing or commenting".red
client.update_issue(source, original.number, labels: (actual_label + ["migration_failed"]))
puts "Status URL: #{status_url}"
# This means we have to manually migrate the issue
# if you want to try it again, just remove the migration_failed tag
end
end
def request_headers
{
"Accept" => "application/vnd.github.golden-comet-preview+json",
"Authorization" => ("token " + ENV["GITHUB_API_TOKEN"]),
"Content-Type" => "application/x-www-form-urlencoded",
"User-Agent" => "fastlane bot"
}
end
# We want to comment on PRs and tell the user to re-submit it
# on the new repo, as we can't migrate them automatically
def hendl_pr(original)
puts "#{original.number} is a pull request"
if original.state != "open"
puts "#{original.number} is already closed - nothing to do here"
return
end
body = ["Hello @#{original.user.login},"]
body << reason
body << "Sorry for the troubles, we'd appreciate if you could re-submit your Pull Request with these changes to the new repository"
client.add_comment(source, original.number, body.join("\n\n"))
smart_sleep
client.close_pull_request(source, original.number)
end
end
require './tools'
destination = "fastlane/fastlane" # TODO: Should be fastlane
names = Array(ENV["TOOL"] || @tools.delete_if { |a| a == "fastlane" }) # we don't want to import issues from our own repo
open_only = !ENV["ALL"]
puts "Migrating #{names.join(', ')}"
names.each do |current|
Hendl.new(source: "fastlane/#{current}",
destination: destination,
reason: "`fastlane` is now a mono repo, you can read more about the change in our [blog post](https://krausefx.com/blog/our-goal-to-unify-fastlane-tools). All tools are now available in the [fastlane main repo](https://github.com/fastlane/fastlane) :rocket:",
open_only: open_only)
end