Skip to content
This repository has been archived by the owner on Oct 5, 2023. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Lars Klevan committed May 13, 2014
2 parents 3a9868c + 48f946b commit e84083a
Show file tree
Hide file tree
Showing 5 changed files with 515 additions and 36 deletions.
4 changes: 4 additions & 0 deletions bin/git-backportpr
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env ruby

require File.join(File.dirname(__FILE__), '..', 'lib', 'socialcast-git-extensions', 'cli.rb')
Socialcast::Gitx::CLI.start (['backportpr'] + ARGV)
38 changes: 33 additions & 5 deletions lib/socialcast-git-extensions/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ def initialize(*args)
method_option :skip_additional_reviewers, :type => :string, :aliases => '-s', :desc => 'Skips adding additional reviewers'
# @see http://developer.github.com/v3/pulls/
def reviewrequest(*additional_reviewers)
token = authorization_token

update

review_mention = if buddy = socialcast_review_buddy(current_user)
Expand Down Expand Up @@ -63,7 +61,7 @@ def reviewrequest(*additional_reviewers)
description = options[:description] || editor_input(PULL_REQUEST_DESCRIPTION)
branch = current_branch
repo = current_repo
url = create_pull_request token, branch, repo, description, assignee
url = create_pull_request branch, repo, description, assignee
say "Pull request created: #{url}"

short_description = description.split("\n").first(5).join("\n")
Expand All @@ -73,9 +71,8 @@ def reviewrequest(*additional_reviewers)

desc "findpr", "Find pull requests including a given commit"
def findpr(commit_hash)
token = authorization_token
repo = current_repo
data = pull_requests_for_commit(token, repo, commit_hash)
data = pull_requests_for_commit(repo, commit_hash)

if data['items']
data['items'].each do |entry|
Expand All @@ -84,7 +81,38 @@ def findpr(commit_hash)
else
say "No results found", :yellow
end
end

desc "backportpr", "Backport a pull request"
def backportpr(pull_request_num, maintenance_branch)
original_base_branch = ENV['BASE_BRANCH']
ENV['BASE_BRANCH'] = maintenance_branch
repo = current_repo
assignee = github_track_reviewer('Backport')
socialcast_reviewer = socialcast_track_reviewer('Backport')

pull_request_data = github_api_request('GET', "repos/#{repo}/pulls/#{pull_request_num}")
commits_data = github_api_request('GET', pull_request_data['commits_url'])

non_merge_commits_data = commits_data.select { |commit_data| commit_data['parents'].length == 1 }
shas = non_merge_commits_data.map { |commit| commit['sha'] }

backport_branch = "backport_#{pull_request_num}_to_#{maintenance_branch}"
backport_to(backport_branch, shas)

maintenance_branch_url = "https://github.com/#{repo}/tree/#{maintenance_branch}"
description = "Backport ##{pull_request_num} to #{maintenance_branch_url}\n***\n#{pull_request_data['body']}"

pull_request_url = create_pull_request(backport_branch, repo, description, assignee)

review_message = ["#reviewrequest backport ##{pull_request_num} to #{maintenance_branch} #scgitx"]
if socialcast_reviewer
review_message << "/cc @#{socialcast_reviewer} for #backport track"
end
review_message << "/cc @SocialcastDevelopers"
post review_message.join("\n\n"), :url => pull_request_url, :message_type => 'review_request'
ensure
ENV['BASE_BRANCH'] = original_base_branch
end

# TODO: use --no-edit to skip merge messages
Expand Down
21 changes: 21 additions & 0 deletions lib/socialcast-git-extensions/git.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,27 @@ def current_user
`git config -z --global --get github.user`.strip
end

def backport_to(branch, shas)
run_cmd "git checkout #{base_branch}"
run_cmd "git checkout -b #{branch}"
begin
run_cmd "git cherry-pick #{shas.join(' ')}"
rescue => e
while true
proceed = $terminal.ask "Error during cherry-pick. You can proceed by resolving the conflicts and using 'git cherry-pick --continue' to finish the cherry-pick in another terminal. Would you like to proceed (y/n)?"
if proceed.to_s.downcase == 'n'
run_cmd "git cherry-pick --abort"
exit 1
elsif proceed.to_s.downcase == 'y'
break
else
say "Invalid response"
end
end
end
run_cmd "git push origin HEAD"
end

# retrieve a list of branches
def branches(options = {})
branches = []
Expand Down
70 changes: 46 additions & 24 deletions lib/socialcast-git-extensions/github.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,49 +32,34 @@ def authorization_token

# returns the url of the created pull request
# @see http://developer.github.com/v3/pulls/
def create_pull_request(token, branch, repo, body, assignee)
def create_pull_request(branch, repo, body, assignee)
payload = {:title => branch, :base => base_branch, :head => branch, :body => body}.to_json
say "Creating pull request for "
say "#{branch} ", :green
say "against "
say "#{base_branch} ", :green
say "in "
say repo, :green
response = RestClient::Request.new(:url => "https://api.github.com/repos/#{repo}/pulls", :method => "POST", :payload => payload, :headers => {:accept => :json, :content_type => :json, 'Authorization' => "token #{token}"}).execute
data = JSON.parse response.body

assign_pull_request(token, branch, assignee, data) if assignee ## Unfortunately this needs to be done in a seperate request.
data = github_api_request("POST", "repos/#{repo}/pulls", payload)
assign_pull_request(branch, assignee, data) if assignee ## Unfortunately this needs to be done in a seperate request.

url = data['html_url']
url
rescue RestClient::Exception => e
process_error e
throw e
end

# find the PRs matching the given commit hash
# https://developer.github.com/v3/search/#search-issues
def pull_requests_for_commit(token, repo, commit_hash)
def pull_requests_for_commit(repo, commit_hash)
query = "#{commit_hash}+type:pr+repo:#{repo}"
say "Searching github pull requests for #{commit_hash}"
response = RestClient::Request.new(:url => "https://api.github.com/search/issues?q=#{query}", :method => "GET", :headers => {:accept => :json, :content_type => :json, 'Authorization' => "token #{token}"}).execute
JSON.parse response.body
rescue RestClient::Exception => e
process_error e
throw e
github_api_request "GET", "search/issues?q=#{query}"
end

def assign_pull_request(token, branch, assignee, data)
def assign_pull_request(branch, assignee, data)
issue_payload = { :title => branch, :assignee => assignee }.to_json
RestClient::Request.new(:url => data['issue_url'], :method => "PATCH", :payload => issue_payload, :headers => {:accept => :json, :content_type => :json, 'Authorization' => "token #{token}"}).execute
rescue RestClient::Exception => e
data = JSON.parse e.http_body
say "Failed to assign pull request: #{data['message']}", :red
end

def process_error(e)
data = JSON.parse e.http_body
say "Failed to create pull request: #{data['message']}", :red
github_api_request "PATCH", data['issue_url'], issue_payload
rescue => e
say "Failed to assign pull request: #{e.message}", :red
end

# @returns [String] socialcast username to assign the review to
Expand All @@ -94,6 +79,43 @@ def github_review_buddy(current_user)
end
end

# @returns [String] github username responsible for the track
# @returns [nil] when user not found
def github_track_reviewer(track)
github_username_for_socialcast_username(socialcast_track_reviewer(track))
end

# @returns [String] Socialcast username responsible for the track
# @returns [nil] when user not found
def socialcast_track_reviewer(track)
specialty_reviewers.values.each do |reviewer_hash|
return reviewer_hash['socialcast_username'] if reviewer_hash['label'].to_s.downcase == track.downcase
end
nil
end

# @returns [String] github username corresponding to the Socialcast username
# @returns [nil] when user not found
def github_username_for_socialcast_username(socialcast_username)
return if socialcast_username.nil? || socialcast_username == ""

review_buddies.each_pair do |github_username, review_buddy_hash|
return github_username if review_buddy_hash['socialcast_username'] == socialcast_username
end
end

def github_api_request(method, path, payload = nil)
url = path.include?('http') ? path : "https://api.github.com/#{path}"
JSON.parse RestClient::Request.new(:url => url, :method => method, :payload => payload, :headers => { :accept => :json, :content_type => :json, 'Authorization' => "token #{authorization_token}", :user_agent => 'socialcast-git-extensions' }).execute
rescue RestClient::Exception => e
process_error e
throw e
end

def process_error(e)
data = JSON.parse e.http_body
say "GitHub request failed: #{data['message']}", :red
end
end
end
end
Loading

0 comments on commit e84083a

Please sign in to comment.