class PrMerger::Merger

Constants

APP_CONTEXT
SKIP_MERGE

Public Class Methods

new(access_token:) click to toggle source
# File lib/pr-merger.rb, line 13
def initialize(access_token:)
  @gh = Octokit::Client.new(access_token: access_token)
end

Public Instance Methods

run(base_repo:, base_branch:, merge_branch:, fork_repo:) click to toggle source
# File lib/pr-merger.rb, line 17
def run(base_repo:, base_branch:, merge_branch:, fork_repo:)
  @base_repo   = base_repo
  @base_branch = base_branch

  pull_requests = @gh.pull_requests(@base_repo, state: 'open').reverse

  cmd = TTY::Command.new
  active_branch_result = cmd.run! 'git rev-parse --abbrev-ref HEAD'

  if active_branch_result.success?
    previous_branch = active_branch_result.out.strip
  else
    previous_branch = cmd.run('cat .git/HEAD').strip
  end

  cmd.run 'git checkout', @base_branch

  if fork_repo
    cmd.run! 'git remote add upstream', "git@github.com:#{@base_repo}.git"
    cmd.run 'git fetch upstream', @base_branch
    cmd.run 'git reset --hard', "upstream/#{@base_branch}"
  else
    cmd.run 'git reset --hard', "origin/#{@base_branch}"
  end

  cmd.run 'git checkout -b', merge_branch

  merge_statuses = pull_requests.map do |pr|
    process_pr(pr, cmd)
  end

  cmd.run 'git checkout', previous_branch

  merge_statuses.all? { |status| status }
end

Private Instance Methods

error_status(pr, message) click to toggle source
# File lib/pr-merger.rb, line 99
def error_status(pr, message)
  send_status(pr, 'error', message)
end
failure_status(pr, message) click to toggle source
# File lib/pr-merger.rb, line 103
def failure_status(pr, message)
  send_status(pr, 'failure', message)
end
pending_status(pr, message) click to toggle source
# File lib/pr-merger.rb, line 95
def pending_status(pr, message)
  send_status(pr, 'pending', message)
end
process_pr(pr, cmd) click to toggle source

Process a given pull request

# File lib/pr-merger.rb, line 56
def process_pr(pr, cmd)
  head = pr[:head]
  repo = head[:repo]

  pending_status(pr, 'Merge in progress.')

  return true if skip_pr?(pr)

  begin
    cmd.run 'git fetch', repo[:ssh_url], head[:ref] if repo

    merge_status = cmd.run! 'git merge --no-ff --no-edit', head[:sha]

    if merge_status.success?
      success_status(pr, "Merge with '#{@base_branch}' was successful.")
    else
      cmd.run 'git merge --abort'

      message = "Failed to merge '#{head[:ref]} with #{@base_branch}."
      failure_status(pr, message)
    end
  rescue => e
    failure_status(pr, "Merge encountered an error: #{e.message}.")

    return false
  end

  true
end
send_status(pr, status, message) click to toggle source

Send Octokit status

# File lib/pr-merger.rb, line 112
def send_status(pr, status, message)
  @gh.create_status(
    @base_repo,
    pr[:head][:sha],
    status,
    context: APP_CONTEXT,
    description: message[0..140]
  )
end
skip_pr?(pr) click to toggle source

Skip a given pull request if it includes skip merge message

# File lib/pr-merger.rb, line 87
def skip_pr?(pr)
  return false unless pr[:title].include?(SKIP_MERGE)

  failure_status(pr, "Skipping #{pr[:head][:ref]}.")

  true
end
success_status(pr, message) click to toggle source
# File lib/pr-merger.rb, line 107
def success_status(pr, message)
  send_status(pr, 'success', message)
end