Skip to content

Instantly share code, notes, and snippets.

@shippy
Last active March 21, 2017 00:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shippy/6ea23180e89a1d19935ec9931d8d647c to your computer and use it in GitHub Desktop.
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
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
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