Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build: add a script to merge a PR according to our current workflow #868

Merged
merged 1 commit into from
Jan 20, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions scripts/merge-pr.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env bash

# We've disabled Github's merge button because it doesn't fast-forward properly.
# Instead, we can use this script to merge an approved PR.
#
# Requires: `git`, `gh`, 'jq'
#
# Usage: $0 [branch-or-pr-number]
#
# If the current branch is `main` then `branch-or-pr-number` is required.

set -e -o pipefail

function bail() {
msg="$1"
if [ -n "$msg" ]; then
# we do want to emit to stderr here
#shellcheck disable=SC2210
echo >2 "$msg"
fi
exit 1
}

current_branch="$(git branch --show-current)"
if [ -z "$1" ]; then
if [ "$current_branch" == "main" ]; then
bail "on main; must specify the PR number or branch name to merge"
else
branch="$current_branch"
fi
else
# if it's a number, assume it's a PR number and get the branch name. otherwise just a branch.
if [[ "$1" =~ ^[0-9]+$ ]]; then
branch="$(gh pr view "$1" --json headRefName | jq -r ".headRefName")"
else
branch="$1"
fi
fi

# ensure the branch is approved
status_json="$(gh pr view "$branch" --json reviewDecision,statusCheckRollup)"
review_decision="$(jq -r .reviewDecision <<< "$status_json")"
if [ "$review_decision" != "APPROVED" ]; then
bail "$branch has not yet been approved"
fi

# ensure all checks have completed successfully
non_success="$(jq -r '.statusCheckRollup[] | select(.__typename == "CheckRun" and ((.status == "COMPLETED" and (.conclusion == "SUCCESS" or .conclusion == "SKIPPED")) | not)) | .name' <<< "$status_json")"
if [ -n "$non_success" ]; then
bail "some CI checks are incomplete or unsuccessful:\n$non_success"
fi

# ensure that the branch is at the tip of `origin/main` for a linear history
git fetch
git checkout "$branch"
if ! git rebase origin/main; then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a prime use case for git merge-base ---is-ancestor. 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha, I was looking for something like that, but google wasn't helpful. I think this behavior, which does the rebase as a side-effect, is actually better though.

There are 3 cases we care about:

  1. The branch already originates from origin/main
  2. The branch originates from some older commit than origin/main but applies cleanly.
  3. The branch originates from some older commit than origin/main but does not apply cleanly.

git merge-base --is-ancestor will do a great job distinguishing case 1, but conflates cases 2 and 3. We do want to distinguish between those.

git rebase as written here conflates 1 and 2, but distinguishes case 3; I think that's the behavior we actually want.

git rebase --abort
bail "$branch did not cleanly rebase onto origin/main; do so manually and try again"
fi

# if rebase moved the tip then force-push to ensure github is tracking the new history
# this resets CI, but doesn't mess with the approvals. We can assume CI is OK, at this point.
istankovic marked this conversation as resolved.
Show resolved Hide resolved
if [ "$(git rev-parse "$branch")" != "$(git rev-parse "origin/$branch")" ]; then
git push -f
fi

# we can now actually merge this to main without breaking anything
git checkout main
git merge "$branch" --ff-only
git push
Loading