Skip to content

Instantly share code, notes, and snippets.

@davorbonaci
Last active March 28, 2019 00:25
Show Gist options
  • Save davorbonaci/16f1c588e0d2140e5be3af1c5f756a55 to your computer and use it in GitHub Desktop.
Save davorbonaci/16f1c588e0d2140e5be3af1c5f756a55 to your computer and use it in GitHub Desktop.
#!/bin/bash
# This is a utility script to merge pull requests into Apache Beam
# repositories. It supports both repositories, source and website.
#
# Usage:
# merge_pr.sh [repository] [branch] pull_request_number
#
# Where:
# repository: { beam | beam-site }
# Default: beam.
# branch: { master | asf-site | any_other_branch }
# Default: master or asf-site, depending on the repository.
# ******************************************************************************
# This function gets called to clean up temporary files and
# revert back to the original state.
function clean_up
{
# Print final status message.
echo "$1"
# Optionally, clean up temporary state.
read -r -p "Do you want to clean up temporary files? [y/N] " response
if [[ $response =~ ^([yY][eE][sS]|[yY])$ ]] ; then
git branch -D "finish-pr-${PR_NUM}" >/dev/null 2>/dev/null || true
rm -fr "${ROOT_DIR}"
else
echo "Temporary workspace left at: ${ROOT_DIR}"
fi
# Return back to the original directory.
popd >/dev/null 2>/dev/null
exit 0
}
TARGET_REMOTE=apache
SOURCE_REMOTE=github
# Argument parsing.
if [[ $# -eq 1 ]] ; then
# One argument for pull request number; default others.
REPOSITORY=beam
TARGET_BRANCH=master
PR_NUM=$1
elif [[ $# -eq 2 ]] ; then
# Two arguments; attempt to figure out whether the user specified repository
# or branch.
if [[ "$1" = "beam" ]] ; then
REPOSITORY=$1
TARGET_BRANCH=master
PR_NUM=$2
elif [[ "$1" = "beam-site" ]] ; then
REPOSITORY=$1
TARGET_BRANCH=asf-site
PR_NUM=$2
else
REPOSITORY=beam
TARGET_BRANCH=$1
PR_NUM=$2
fi
elif [[ $# -eq 3 ]] ; then
# All arguments provided.
REPOSITORY=$1
TARGET_BRANCH=$2
PR_NUM=$3
else
# Unexpected number of flags. Print usage information.
echo "Usage: merge_pr.sh [repository] [branch] pull_request_number"
echo ""
echo " repository: { beam | beam-site }"
echo " Default: beam."
echo " branch: { master | asf-site | any_other_branch }"
echo " Default: master or asf-site, depending on the repository."
exit 1
fi
# Echo display what we are about to do.
echo "Attempting to merge pull request #${PR_NUM} to the branch " \
"\"${TARGET_BRANCH}\" in the repository \"${REPOSITORY}\"."
# Create temporary directory for this work.
pushd . >/dev/null 2>/dev/null
ROOT_DIR=$(mktemp -d)
cd "$ROOT_DIR"
# Clone the repository and configure it.
git clone --single-branch -b "${TARGET_BRANCH}" \
"https://github.com/apache/${REPOSITORY}.git" \
|| clean_up "ERROR: Failed to clone GitHub repository."
cd "${REPOSITORY}"
git remote add ${TARGET_REMOTE} \
"https://git-wip-us.apache.org/repos/asf/${REPOSITORY}.git"
git remote rename origin ${SOURCE_REMOTE}
git config --local --add remote.${SOURCE_REMOTE}.fetch \
"+refs/pull/*/head:refs/remotes/${SOURCE_REMOTE}/pr/*"
# Fetch everything to make sure we have the last state of the branch.
git fetch ${SOURCE_REMOTE} "pull/${PR_NUM}/head" \
|| clean_up "ERROR: Failed to fetch source pull request."
git fetch ${TARGET_REMOTE} "${TARGET_BRANCH}" \
|| clean_up "ERROR: Failed to fetch target branch."
# Checkout the pull request for merging
git checkout -b "finish-pr-${PR_NUM}" "${SOURCE_REMOTE}/pr/${PR_NUM}" \
|| clean_up "ERROR: Failed to checkout pull request number #${PR_NUM}."
# Infinite loop to iterate on the pull request.
while :
do
# Rebase the pull request on the target branch.
git rebase -i "${TARGET_REMOTE}/${TARGET_BRANCH}" \
|| clean_up "ERROR: Failed to rebase on top of " \
"${TARGET_REMOTE}/${TARGET_BRANCH}."
# If the rebase included an interactive command (eg., edit) open a shell to
# complete it
while [ -d ".git/rebase-merge" -o -d ".git/rebase-apply" ]; do
echo "Interactive rebase in progress. Starting a shell shell to finish."
echo "To resume, complete the rebase and run \"exit\"."
$SHELL -i
if [ -d ".git/rebase-merge" -o -d ".git/rebase-apply" ]; then
read -r -p "Rebase still in progress. Do you want to abort the rebase" \
response
if [[ $response =~ ^([yY][eE][sS]|[yY])$ ]]; then
git rebase --abort;
fi
fi
done
# Open 'gitk' to review the state of this branch.
gitk
# Ask the user whether we should proceed.
read -r -p "Are you happy with how this branch looks? [y/N] " response
if [[ $response =~ ^([yY][eE][sS]|[yY])$ ]] ; then
break
fi
done
# If website repository, offer to regenerate the site.
if [[ "${REPOSITORY}" = "beam-site" ]] ; then
read -r -p "Do you want to regenerate the website? [y/N] " \
response
if [[ $response =~ ^([yY][eE][sS]|[yY])$ ]] ; then
bundle exec jekyll build \
|| clean_up "ERROR: Building the website failed. Make sure your " \
"environment is correctly set up for building the website."
git add --all -- content
git commit -m "Regenerate website" \
|| clean_up "ERROR: Couldn't commit the website."
fi
fi
# At this point, we are happy how the pull request branch looks like.
# Merge it on the target branch.
git checkout "${TARGET_REMOTE}/${TARGET_BRANCH}" \
|| clean_up "ERROR: Failed to checkout target branch " \
"\"${TARGET_REMOTE}/${TARGET_BRANCH}\"."
git merge --no-ff -m $"This closes #${PR_NUM}" "finish-pr-${PR_NUM}" \
|| clean_up "ERROR: Failed to merge local branch " \
"\"finish-pr-${PR_NUM}\" to the target branch."
# Open 'gitk' to review the state of this branch.
gitk
# Run 'mvn clean verify', as one more final validation.
if [[ "${REPOSITORY}" = "beam" ]] ; then
read -r -p "Do you want to skip running 'mvn clean verify' locally? [y/N] " \
response
if ! [[ $response =~ ^([yY][eE][sS]|[yY])$ ]] ; then
mvn clean verify \
|| clean_up "ERROR: Maven test failed. Please fix it before pushing."
fi
fi
# Ask the user whether we should proceed.
echo "CAUTION -- This is the point of no return! CAUTION!"
read -r -p "Do you want to push this branch? [y/N] " response
if [[ $response =~ ^([yY][eE][sS]|[yY])$ ]] ; then
git push ${TARGET_REMOTE} "HEAD:${TARGET_BRANCH}" \
|| clean_up "ERROR: Failed to push to \"${TARGET_REMOTE}\" remote.\n" \
"Run \"git push ${TARGET_REMOTE} HEAD:${TARGET_BRANCH}\" to retry."
fi
clean_up "SUCCESS."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment