class GithubMergeSign::PullRequest

Constants

BASE_URL

Public Class Methods

new(pr_number) click to toggle source
# File lib/github_merge_sign.rb, line 11
def initialize(pr_number)
  @pr_number = pr_number
end

Public Instance Methods

commit_message() click to toggle source
# File lib/github_merge_sign.rb, line 48
    def commit_message
      <<-EOT
Merge pull request ##{@pr_number} from #{details.fetch('head').fetch('user').fetch('login')}/#{head_ref}

#{details.fetch('title')}
      EOT
    end
external_pr?() click to toggle source
# File lib/github_merge_sign.rb, line 31
def external_pr?
  details.fetch("base").fetch("repo").fetch("full_name") !=
    details.fetch("head").fetch("repo").fetch("full_name")
end
head_commit_id() click to toggle source
# File lib/github_merge_sign.rb, line 40
def head_commit_id
  details.fetch("head").fetch("sha")
end
head_ref() click to toggle source
# File lib/github_merge_sign.rb, line 44
def head_ref
  details.fetch("head").fetch("ref")
end
merge!() click to toggle source
# File lib/github_merge_sign.rb, line 56
def merge!
  raise "PR is not open" unless open?
  raise "PR is not mergeable" unless mergeable?
  raise "PR has not passed all status checks" unless status_checks_passed?

  # Check working directorty is clean
  unless Kernel.system('git diff --quiet --exit-code HEAD')
    raise "Working directory is not clean. Aborting..."
  end

  info "Checking out and updating #{target_branch}"
  execute_command("git checkout #{target_branch}")
  execute_command("git pull --ff-only origin #{target_branch}")

  if external_pr?
    info "Fetching commits from PR #{@pr_number}"
    execute_command("git fetch origin refs/pull/#{@pr_number}/head")
  end

  info "Merging PR with commit message:\n#{commit_message}"
  execute_command('git', 'merge', '--no-ff', '-S', '-m', commit_message, head_commit_id)

  info "Pushing to origin"
  execute_command("git push origin #{target_branch}")

  unless external_pr?
    info "Deleting remote branch #{head_ref}"
    execute_command("git push origin :#{head_ref}")
  end

  info "Done."
end
mergeable?() click to toggle source
# File lib/github_merge_sign.rb, line 23
def mergeable?
  !! details.fetch("mergeable")
end
open?() click to toggle source
# File lib/github_merge_sign.rb, line 19
def open?
  details.fetch("state") == "open"
end
repo() click to toggle source
# File lib/github_merge_sign.rb, line 15
def repo
  @repo ||= read_repo_from_git
end
status_checks_passed?() click to toggle source
# File lib/github_merge_sign.rb, line 27
def status_checks_passed?
  status.fetch("state") == "success" || status.fetch("statuses").empty?
end
target_branch() click to toggle source
# File lib/github_merge_sign.rb, line 36
def target_branch
  details.fetch("base").fetch("ref")
end

Private Instance Methods

details() click to toggle source
# File lib/github_merge_sign.rb, line 114
def details
  @_details ||= get_json("#{BASE_URL}/#{repo}/pulls/#{@pr_number}")
end
execute_command(*args) click to toggle source
# File lib/github_merge_sign.rb, line 108
def execute_command(*args)
  unless Kernel.system(*args)
    raise "Error: command '#{args.join(' ')}' exited #{$?.exitstatus}."
  end
end
get_json(url) click to toggle source
# File lib/github_merge_sign.rb, line 123
def get_json(url)
  response = http_get(url)
  if response.is_a?(Net::HTTPForbidden) && response.body =~ /API rate limit exceeded for/
    raise "Github rate-limit exceeded. To avoid this create a Github API token with public access and put it in GITHUB_API_TOKEN env var."
  end

  unless response.is_a?(Net::HTTPSuccess)
    raise "Failed to fetch PR details.\nGot #{response.code} fetching '#{url}':\n#{response.body}"
  end

  JSON.parse(response.body)
end
http_get(url) click to toggle source
# File lib/github_merge_sign.rb, line 136
def http_get(url)
  uri = URI.parse(url)
  Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
    request = Net::HTTP::Get.new(uri.request_uri)
    request['Authorization'] = "token #{ENV['GITHUB_API_TOKEN']}" if ENV["GITHUB_API_TOKEN"]
    return http.request(request)
  end
end
info(message) click to toggle source
# File lib/github_merge_sign.rb, line 145
def info(message)
  if $stdout.isatty
    puts "\e[36m#{message}\e[0m"
  else
    puts message
  end
end
read_repo_from_git() click to toggle source
# File lib/github_merge_sign.rb, line 91
def read_repo_from_git
  details = `git remote -v`
  unless $?.success?
    raise "Error reading git remotes: exit status #{$?.exitstatus}."
  end
  details.each_line do |line|
    line.strip!
    if line =~ /\Aorigin\t(\S+)\s+\(fetch\)\z/
      url = $1
      if url =~ /\A(?:https:\/\/|git@)github\.com[\/:](\S+?)(?:\.git)?\z/
        return $1
      end
    end
  end
  raise "origin is not a Github URL"
end
status() click to toggle source
# File lib/github_merge_sign.rb, line 118
def status
  url = "#{BASE_URL}/#{repo}/commits/#{head_commit_id}/status"
  @_status ||= get_json(url)
end