Last active
March 21, 2017 00:29
-
-
Save shippy/6ea23180e89a1d19935ec9931d8d647c to your computer and use it in GitHub Desktop.
Mirroring a repository with issues, labels & milestones from one GitHub server to another
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'pry' | |
require 'octokit' | |
require 'json' | |
# Part 0: Extract bare repo and push it to GH: | |
# Follow https://help.github.com/enterprise/2.2/admin/articles/moving-a-repository-from-github-com-to-github-enterprise/ | |
# Part 1: Extract issues & everything else from the source repo | |
## Setup | |
Octokit.configure do |c| | |
c.api_endpoint = 'https://git.yale.edu/api/v3/' | |
c.auto_paginate = true | |
end | |
# set ENTERPRISE_TOKEN prior to this line | |
yalegit = Octokit::Client.new(:access_token => ENTERPRISE_TOKEN) | |
repoName = 'levylab/RNA_PTB_task' | |
## Action | |
opts = {:state => :all, :sort => :created, :direction => :asc} | |
labels = yalegit.labels(repoName, {:state => :all}) | |
issuesAndPRs = yalegit.issues(repoName, opts) | |
pulls = yalegit.pull_requests(repoName, opts) | |
milestones = yalegit.milestones(repoName, opts) | |
comments = yalegit.issues_comments(repoName, opts) | |
## Intermediate save | |
# Returned objects are Sawyer resources; we need | |
# `sawyer_resource.map(&:to_h)` to serialize them. | |
File.open('labels.json', 'w') do |f| | |
f.write(labels.map(&:to_h).to_json) | |
end | |
File.open('issuesAndPRs.json', 'w') do |f| | |
f.write(issuesAndPRs.map(&:to_h).to_json) | |
end | |
File.open('milestones.json', 'w') do |f| | |
f.write(milestones.map(&:to_h).to_json) | |
end | |
File.open('comments.json', 'w') do |f| | |
f.write(comments.map(&:to_h).to_json) | |
end | |
File.open('pulls.json', 'w') do |f| | |
f.write(pulls.map(&:to_h).to_json) | |
end | |
## Investigate | |
binding.pry |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'octokit' | |
require 'json' | |
# Part 3: Upload everything to target repo on GitHub | |
## Setup | |
Octokit.configure do |c| | |
c.api_endpoint = 'https://api.github.com/' | |
c.auto_paginate = true | |
end | |
# set GITHUB_TOKEN prior to this line | |
github = Octokit::Client.new(:access_token => GITHUB_TOKEN) | |
repo = 'shippy/test_PTF' | |
## 1. Labels | |
### 1a. Delete default labels | |
github.labels(repo).each do |l| | |
github.delete_label!(repo, l[:name]) | |
end | |
### 1b. Upload labels to GH | |
labels = JSON.parse(File.read('labels.json'), {symbolize_names: true}) | |
labels.each do |l| | |
begin | |
github.add_label(repo, l[:name], l[:color]) | |
puts "Added #{l[:name]} - ##{l[:color]}" | |
rescue Exception => e | |
puts "#{l[:name]} already exists, updating:" if e.class == Octokit::UnprocessableEntity | |
github.update_label(repo, l[:name], {color: l[:color]}) | |
end | |
end | |
## 2. Upload milestones to GH with the right numbering | |
### 2a. Create milestones, including placeholders | |
milestones = JSON.parse(File.read('milestones.json'), {symbolize_names: true}).sort_by {|m| m[:number]} | |
current_milestone = 0 | |
fake_milestones = [] | |
milestones.each do |m| | |
current_milestone = current_milestone + 1 | |
while m[:number] > current_milestone | |
github.create_milestone(repo, "fake #{current_milestone}") | |
fake_milestones << current_milestone | |
current_milestone = current_milestone + 1 | |
end | |
github.create_milestone(repo, m[:title], {state: m[:state], description: m[:description]}) | |
end | |
### 2b. Remove the placeholder milestones | |
fake_milestones.each do |fake| | |
github.delete_milestone(repo, fake) | |
end | |
## 3. Upload issues/PRs with the right labels and milestones | |
issuesAndPRs = JSON.parse(File.read('issuesAndPRs.json'), {symbolize_names: true}).sort_by { |p| p[:number] } | |
pulls = JSON.parse(File.read('pulls.json'), {symbolize_names: true}).sort_by { |p| p[:number] } | |
comments = JSON.parse(File.read('comments.json'), {symbolize_names: true}).sort_by { |p| p[:id] } | |
# In case uploading was interrupted, note the uploaded issues | |
issues_uploaded = github.issues(repo, {state: :all, sort: :created, direction: :desc}) | |
issuesAndPRs.each do |i| | |
### 3a. Extract identifiers from the issue | |
# Skip existing issues | |
issue_number = i[:number] | |
unless issues_uploaded.empty? | |
last_issue_id = issues_uploaded[0][:number] | |
if issue_number <= last_issue_id | |
next | |
end | |
end | |
issue_url = i[:url] | |
issue_labels = i[:labels].map { |l| l[:name] } | |
begin | |
issue_milestone = i[:milestone][:number] | |
rescue Exception | |
issue_milestone = nil | |
end | |
### 3b. create issue | |
sleep(3) # to avoid rate limiting | |
github.create_issue(repo, i[:title], i[:body], {milestone: issue_milestone, labels: issue_labels}) | |
### 3c. convert into open PR / note the history for closed PR | |
if i.key?(:pull_request) | |
current_pull = pulls.select { |p| p[:number] == issue_number }[0] | |
base = current_pull[:base][:ref] | |
head = current_pull[:head][:ref] | |
if i[:state] == "open" | |
github.create_pull_request_for_issue(repo, base, head, issue_number) | |
else | |
merge_commit_sha = current_pull[:merge_commit_sha] | |
base_sha = current_pull[:base][:sha] | |
head_sha = current_pull[:head][:sha] | |
pull_note = "**Migration note**: This was a pull request to merge " | |
pull_note << "`#{head}` at #{head_sha} into `#{base}` at #{base_sha}. " | |
pull_note << "It was merged in #{merge_commit_sha}.\n\n" | |
new_body = pull_note + current_pull[:body] | |
github.update_issue(repo, issue_number, { body: new_body }) | |
end | |
end | |
### 4d. Add all comments left in the issue | |
comments.select { |c| c[:issue_url] == issue_url }.each do |c| | |
github.add_comment(repo, issue_number, c[:body]) | |
end | |
### 4e. close if appropriate | |
if i[:state] != 'open' | |
github.close_issue(repo, issue_number) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment