Last active
November 1, 2022 19:37
-
-
Save eculver/a690bf0ead818d98b9c4fddb84d715f5 to your computer and use it in GitHub Desktop.
Consul OSS→Enterprise Merge Shell Helpers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function ci-for-oss-ent-merge-branch { | |
local branch | |
local base | |
branch="${1:-}" | |
if [ -z "${branch}" ]; then | |
echo "branch name required" | |
echo "usage: ci-for-oss-ent-merge-branch <branch-name>" | |
return 1 | |
fi | |
# There are two forms of branch names currently, one for when the merge is | |
# clean and the other when there were conflicts: | |
# oss/merge-$BRANCH-$SHA | |
# and | |
# oss/$BRANCH-$SHA | |
# | |
# Eventually, we should make these consistent but for now, let's just handle | |
# them both. The only difference is which field to use after cutting by '-'. | |
if grep -q '^oss/merge' <<< "$branch"; then | |
cut_flags=( "-f2" ) | |
else | |
cut_flags=( "-f1" ) | |
fi | |
base=$(echo "${branch}" | cut -d '/' -f2- | cut -d '-' ${cut_flags[@]}) | |
if [ -z "${base}" ]; then | |
echo "could not parse base from ${branch}, bailing" | |
return 1 | |
fi | |
open "https://app.circleci.com/pipelines/github/hashicorp/consul-enterprise?branch=${branch}" | |
} | |
function ci-for-all-oss-ent-merge-prs { | |
local branch | |
local pull_numbers | |
branch="${1:=main}" | |
if [ -z "${BOB_GITHUB_TOKEN}" ]; then | |
echo "must set \$BOB_GITHUB_TOKEN" | |
return 1 | |
fi | |
echo "Fetching all open oss-merge PRs for ${branch} branch" | |
pull_numbers=$(_get_oss_ent_merge_pull_numbers "${branch}" || echo "failed") | |
num_pulls=$(echo "${pull_numbers}" | wc -l | awk '{print $1}') | |
echo -n "Found ${num_pulls} oss-merge PRs for ${branch}, continue? (y/n) " | |
read -q | |
echo | |
if [[ ! $REPLY =~ ^[Yy]$ ]]; then | |
return | |
fi | |
while read -r num; do | |
echo "Fetching PR info for #${num}" | |
merge_branch=$(curl -s \ | |
-H "Authorization: token ${BOB_GITHUB_TOKEN}" \ | |
-H "Accept: application/vnd.github+json" \ | |
https://api.github.com/repos/hashicorp/consul-enterprise/pulls/${num} | jq -r '.head.ref') | |
if [ -z ${merge_branch} ]; then | |
echo "Could not find merge branch for PR #${num}, bailing" | |
return 1 | |
fi | |
ci-for-oss-ent-merge-branch "${merge_branch}" | |
done <<< "${pull_numbers}" | |
} | |
function delete-oss-merge-branches { | |
echo "Delete branches with ^oss/ pattern? (y/n) " | |
read -q | |
if [[ ! $REPLY =~ ^[Yy]$ ]]; then | |
return | |
fi | |
for b in $(git branch | awk '{$1=$1;print}' | egrep '^oss/'); do git branch -D "$b"; done | |
} | |
function rebase-oss-ent-merge-branch { | |
local branch | |
local pull_number | |
local base | |
branch="${1:-}" | |
pull_number="${2:-}" | |
if [ -z "${branch}" ]; then | |
echo "branch name required" | |
echo "usage: rebase-oss-ent-merge-branch <branch-name> <pull-number>" | |
return 1 | |
fi | |
if [ -z "${pull_number}" ]; then | |
echo "pull_number name required" | |
echo "usage: rebase-oss-ent-merge-branch <branch-name> <pull-number>" | |
return 1 | |
fi | |
# There are two forms of branch names currently, one for when the merge is | |
# clean and the other when there were conflicts: | |
# oss/merge-$BRANCH-$SHA | |
# and | |
# oss/$BRANCH-$SHA | |
# | |
# Eventually, we should make these consistent but for now, let's just handle | |
# them both. The only difference is which field to use after cutting by '-'. | |
if grep -q '^oss/merge' <<< "$branch"; then | |
cut_flags=( "-f2" ) | |
else | |
cut_flags=( "-f1" ) | |
fi | |
base=$(echo "${branch}" | cut -d '/' -f2- | cut -d '-' ${cut_flags[@]}) | |
if [ -z "${base}" ]; then | |
echo "could not parse base from ${branch}, bailing" | |
return 1 | |
fi | |
echo -n "fixing up ${branch} for ${base}, continue? (y/n) " | |
read -q | |
echo | |
if [[ ! $REPLY =~ ^[Yy]$ ]]; then | |
return | |
fi | |
git co "${base}" | |
git pull origin "${base}" | |
git co "${branch}" | |
if ! git merge "${base}"; then | |
echo | |
echo "Looks like there were conflicts, stopping here." | |
echo "When done resolving conflicts, run" | |
echo | |
echo " git push origin ${branch}" | |
echo | |
return 1 | |
fi | |
git push origin "${branch}" | |
if git --no-pager diff "origin/${base}"; then | |
echo | |
echo -n "There are no changes between this branch and ${base}, would you like to close this PR (${pull_number})? (y/n)" | |
read -q | |
echo | |
if [[ $REPLY =~ ^[Yy]$ ]]; then | |
_close_ent_pr_with_comment "${pull_number}" "Closing as this was included in a subsequent merge." | |
# ensure the the PR branch gets cleaned up | |
git push origin --delete "${branch}" | |
fi | |
fi | |
} | |
function rebase-all-oss-ent-merge-prs { | |
local branch | |
local pull_numbers | |
branch="${1:=main}" | |
if [ -z "${BOB_GITHUB_TOKEN}" ]; then | |
echo "must set \$BOB_GITHUB_TOKEN" | |
return 1 | |
fi | |
echo "Fetching all open oss-merge PRs for ${branch} branch" | |
pull_numbers=$(_get_oss_ent_merge_pull_numbers "${branch}" || echo "failed") | |
num_pulls=$(echo "${pull_numbers}" | wc -l | awk '{print $1}') | |
echo -n "Found ${num_pulls} oss-merge PRs for ${branch}, continue? (y/n) " | |
read -q | |
echo | |
if [[ ! $REPLY =~ ^[Yy]$ ]]; then | |
return | |
fi | |
while read -r num; do | |
echo "Fetching PR info for #${num}" | |
merge_branch=$(curl -s \ | |
-H "Authorization: token ${BOB_GITHUB_TOKEN}" \ | |
-H "Accept: application/vnd.github+json" \ | |
https://api.github.com/repos/hashicorp/consul-enterprise/pulls/${num} | jq -r '.head.ref') | |
if [ -z ${merge_branch} ]; then | |
echo "Could not find merge branch for PR #${num}, bailing" | |
return 1 | |
fi | |
rebase-oss-ent-merge-branch "${merge_branch}" "${num}" | |
done <<< "${pull_numbers}" | |
} | |
function _get_oss_ent_merge_pull_numbers { | |
local branch | |
branch="${1:=main}" | |
if [ -z "${BOB_GITHUB_TOKEN}" ]; then | |
echo "must set \$BOB_GITHUB_TOKEN" | |
return 1 | |
fi | |
curl -s \ | |
-H "Authorization: token ${BOB_GITHUB_TOKEN}" \ | |
-H 'Accept: application/vnd.github.text-match+json' \ | |
'https://api.github.com/search/issues?q=repo%3Ahashicorp/consul-enterprise+is%3Apr+label%3Aoss-merge+is%3Aopen+%5B'${branch}'%5D' | jq -r '.items[].number' | sort -n | |
} | |
function _close_ent_pr_with_comment { | |
local pull_number | |
local comment | |
local response | |
local exit_code | |
pull_number="${1:-}" | |
comment="${2:-}" | |
if [ -z "${pull_number}" ]; then | |
echo "pull_number required" | |
echo "usage: _close_ent_pr_with_comment <pull-number> <comment>" | |
return 1 | |
fi | |
if [ -z "${comment}" ]; then | |
echo "comment required" | |
echo "usage: _close_ent_pr_with_comment <pull-number> <comment>" | |
return 1 | |
fi | |
if [ -z "${BOB_GITHUB_TOKEN}" ]; then | |
echo "must set \$BOB_GITHUB_TOKEN" | |
return 1 | |
fi | |
# create the comment, note: this does not do any escaping of the comment string | |
response=$(curl -s \ | |
-H "Authorization: token ${BOB_GITHUB_TOKEN}" \ | |
-H 'Accept: application/vnd.github.full+json' \ | |
-X POST \ | |
-d '{"body": "'${comment}'"}' \ | |
'https://api.github.com/repos/hashicorp/consul-enterprise/issues/'${pull_number}'/comments') | |
exit_code=$? | |
if [ $exit_code -ne 0 ]; then | |
echo "API request to create a comment returned non-zero exit. Got: ${exit_code}." | |
echo | |
echo "Response:" | |
echo "${response}" | |
echo | |
exit 1 | |
fi | |
# close the pull request | |
response=$(curl -s \ | |
-H "Authorization: token ${BOB_GITHUB_TOKEN}" \ | |
-H 'Accept: application/vnd.github.full+json' \ | |
-X POST \ | |
-d '{"state": "closed"}' \ | |
'https://api.github.com/repos/hashicorp/consul-enterprise/pulls/'${pull_number}) | |
exit_code=$? | |
if [ $exit_code -ne 0 ]; then | |
echo "API request to close the pull request returned non-zero exit. Got: ${exit_code}." | |
echo | |
echo "Response:" | |
echo "${response}" | |
echo | |
exit 1 | |
fi | |
} |
And then there's the delete-oss-merge-branches
which removes all the branches that were created locally on the way. It only deletes them locally so it's safe to run.
I also baked confirmations in there to keep my attention on it since it can be a bit brain-dead after a few gos.
I added two new helpers just now:
ci-for-oss-ent-merge-branch
- opens CircleCI for a merge branchci-for-all-oss-ent-merge-prs
- opens CircleCI build UI for all open PRs
These are handy when you just need to kick builds.
I added support to detect whether the PR has a diff against the base and if not, to prompt the user for closing of the PR. If the user accepts the prompt, a comment with "Closing as this was included in a subsequent merge." is added, the PR is closed and the PR branch is deleted.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Note: In here, I'm using my
BOB_GITHUB_TOKEN
for the GitHub API interactions; I would suggest setting up your own if you don't already have this one.Typical workflow goes like:
rebase-oss-ent-merge-branch ${PASTE_BRANCH_NAME}
-- This will kick you out to deal with merge conflicts if they need resolving
-- Or it will just continue and push the rebased branch back to origin
Then, once the queue is unblocked, you can use the
rebase-all-oss-ent-merge-prs
helper to roll through all the other PRs that should now be no-ops since the first PR at the head should have included it's change. You can just close these, but I like to create a merge commit anyways for posterity. Up to you. Either way, you should ensure they are actually no-ops first by rebasing all of them.This makes dealing with a big queue much more manageable since you only have to unblock the head for each branch before mindlessly rolling through all of them at once and waiting for them to resolve themselves.