Last active
September 2, 2024 20:19
-
-
Save tangxiaocheng/997bd9f9dc0222a3dc59c99d5637c476 to your computer and use it in GitHub Desktop.
quick create or merge pr
This file contains 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
#!/bin/bash | |
############################################################################## | |
## | |
## File: quick_pr.sh | |
## Author: Randy | |
## Date: 2021-09-30 | |
## Description: Create and merge a PR in a agile way. Especially for testing ci/cd pipeline. | |
## Usage example: ./quick_pr.sh --runner 'github' --repo_path '/path/to/repo' --base_branch 'main' | |
## | |
## Required input parameters: | |
## runner: not uesd yet (for testing only) | |
## | |
## Optional input parameters: | |
## base_branch: pr base branch | |
## new_base_branch: if set, checkout base_branch and create a new branch with this name for PR | |
## commit_msg: support tags: todo, diff (e.g. --commit_msg 'diff: fix bug') | |
## jira: jira ticket number, if set, the branch name will be 'randy/${jira}/${current_time}' | |
## | |
## Optional boolean parameters: | |
## do_not_merge: if set or set to true, the PR will not be merged. Default is false. | |
## do_not_open: if set or set to true, it will not open the pr link. Default is false. | |
## | |
## Note: | |
## 1, please install gh cli before running this script | |
## 2, please set up the ssh key for the remote repository before running this script | |
## 3, the reason I can use gh cli to create and merge PR is because I have set up GH_TOKEN in my environment, such as in ~/.bashrc | |
############################################################################## | |
# pre-check for essential parameters | |
while [ $# -gt 0 ]; do | |
if [[ $1 == *"--"* ]]; then | |
v="${1/--/}" | |
if [[ -n "$2" ]] && [[ ! "$2" =~ ^-- ]]; then | |
declare -r "$v"="$2" | |
else | |
declare -r "$v"="true" | |
fi | |
fi | |
shift | |
done | |
required_parameters=("runner") | |
for parameter in "${required_parameters[@]}"; do | |
if [[ -n "${parameter}" ]]; then | |
parameter_value=$(eval echo \$"$parameter") | |
if [ -z "$parameter_value" ]; then | |
echo "please provides required input parameter with format: --$parameter 'value'" | |
exit 1 | |
fi | |
fi | |
done | |
echo "" | |
echo "--------------------------------------------------------------------------------------" | |
echo "Required input parameters:" | |
for parameter in "${required_parameters[@]}"; do | |
if [[ -n "${parameter}" ]]; then | |
parameter_value=$(eval echo \$"$parameter") | |
echo "${parameter} = ${parameter_value}" | |
fi | |
done | |
echo "--------------------------------------------------------------------------------------" | |
echo "" | |
optional_parameters=( "base_branch" "new_base_branch" "commit_msg" "jira" ) | |
echo "" | |
echo "--------------------------------------------------------------------------------------" | |
echo "Optional parameters:" | |
for parameter in "${optional_parameters[@]}"; do | |
if [[ -n "${parameter}" ]]; then | |
parameter_value=$(eval echo \$"$parameter") | |
if [[ -z "${parameter_value}" ]]; then | |
echo "${parameter} = UNSET" | |
else | |
echo "${parameter} = ${parameter_value}" | |
fi | |
fi | |
done | |
echo "--------------------------------------------------------------------------------------" | |
echo "" | |
optional_boolean_parameters=( "do_not_merge" "do_not_open") | |
echo "" | |
echo "--------------------------------------------------------------------------------------" | |
echo "Optional boolean parameters:" | |
for parameter in "${optional_boolean_parameters[@]}"; do | |
if [[ -n "${parameter}" ]]; then | |
parameter_value=$(eval echo \$"$parameter") | |
if [[ -z "${parameter_value}" ]]; then | |
echo "${parameter} = true" | |
else | |
echo "${parameter} = ${parameter_value}" | |
fi | |
fi | |
done | |
echo "--------------------------------------------------------------------------------------" | |
echo "" | |
# shellcheck disable=SC2154 | |
if [ ! -d "$repo_path" ]; then | |
echo "Invalid directory repo_path: $repo_path" | |
exit 1 | |
else | |
cd "$repo_path" || exit | |
fi | |
# check if the repo_path is a git repository | |
if [ ! -d ".git" ]; then | |
echo "This path repo_path: $repo_path is not a git repository " | |
exit 1 | |
else | |
echo "This path repo_path: $repo_path is a git repository" | |
fi | |
current_time=$(date +"%m_%d-%H.%M.%S") | |
if [[ -n "${jira}" ]]; then | |
echo "string is not empty" | |
pr_source_branch="randy/${jira}/${current_time}" | |
else | |
pr_source_branch="randy/${current_time}" | |
fi | |
# shellcheck disable=SC2154 | |
final_commit_msg="${commit_msg}" | |
if [[ -z "${final_commit_msg}" ]]; then | |
final_commit_msg="Empty input commit message on branch: ${pr_source_branch}" | |
fi | |
commit_msg_tags=( "todo" "diff" "" ) | |
tag_count=0 | |
for tag in "${commit_msg_tags[@]}"; do | |
if [[ -n "${tag}" ]]; then | |
tag_with_semicolon="${tag}:" | |
if grep -qiE "(^| )${tag_with_semicolon}" <<<"$final_commit_msg"; then | |
echo "found tag: '${tag_with_semicolon}' in commit message" | |
final_commit_msg=$(sed "s/${tag_with_semicolon}//g" <<<"$final_commit_msg") | |
declare -r "has_$tag"="true" | |
((tag_count++)) | |
else | |
echo "not found tag: '${tag_with_semicolon}' in commit message" | |
fi | |
fi | |
done | |
if [[ $tag_count -gt 1 ]]; then | |
echo "Invalid amount ($tag_count) of commit mesaage tags detected for commit message: ${commit_msg}" | |
exit 1 | |
fi | |
for tag in "${commit_msg_tags[@]}"; do | |
if [[ -n "${tag}" ]]; then | |
has_tag=$(eval echo \$"has_$tag") | |
echo "has_$tag: $has_tag" | |
fi | |
done | |
local_branch=$(git rev-parse --abbrev-ref HEAD) | |
if [[ -n "${base_branch}" ]]; then | |
if [ "$local_branch" != "${base_branch}" ]; then | |
git checkout "${base_branch}" | |
echo "Current branch is not the same as base_branch: ${base_branch}" | |
else | |
echo "Current branch is the same as base_branch: ${base_branch}" | |
fi | |
else | |
base_branch="$local_branch" | |
fi | |
git stash | |
git pull --rebase | |
final_base_branch="${base_branch}" | |
if [[ -n "${new_base_branch}" ]]; then | |
if [[ "${new_base_branch}" == "${final_base_branch}" ]]; then | |
echo "new_base_branch is the same as base_branch: ${base_branch}, do nothing" | |
else | |
echo "new_base_branch is different from base_branch: ${base_branch}" | |
git checkout -b "${new_base_branch}" | |
git push --set-upstream origin "${new_base_branch}" | |
final_base_branch="${new_base_branch}" | |
fi | |
fi | |
if git stash list | grep -q "stash"; then | |
if ! git stash apply; then | |
echo "git stash apply encountered an error. " | |
exit 1 | |
else | |
echo "git stash apply ran successfully" | |
fi | |
fi | |
git checkout -b "${pr_source_branch}" | |
# Check for unstaged changes (git diff) and staged but not committed changes (git status) | |
if git diff --exit-code && git diff --cached --exit-code; then | |
echo "no changes detected, let's make some changes for triggering ci via pr" | |
ci_trigger_file="ci_trigger_file.txt" | |
touch "$ci_trigger_file" | |
echo "$pr_source_branch" >> "$ci_trigger_file" | |
git add "$ci_trigger_file" | |
git commit -m "make code change for trigger ci with: $final_commit_msg" | |
else | |
git add . | |
git commit -a -m "${final_commit_msg}" | |
fi | |
output=$(git push --set-upstream origin "${pr_source_branch}" 2>&1) | |
exit_status=$? | |
if [ $exit_status -eq 0 ]; then | |
url=$(echo "$output" | grep -o 'https://github.com/[a-zA-Z0-9./_-]*') | |
if [ -n "$url" ]; then | |
echo "Branch url: $url" | |
# open "$url" | |
else | |
echo "Push successful, but no pull request URL found in the output." | |
fi | |
else | |
echo "Error pushing to GitHub:" | |
echo "$output" | |
exit 1 | |
fi | |
pr_link=$(gh pr create --base "$final_base_branch" --title "$final_commit_msg" --body "Merge PR: $final_commit_msg") | |
if [[ "${do_not_open}" != "true" ]]; then | |
open "$pr_link" | |
fi | |
sleep 3 | |
echo "pr: ${pr_link}" | |
if [[ "${do_not_merge}" != "true" ]]; then | |
gh pr merge "${pr_link}" -r -d --admin | |
git checkout "$final_base_branch" | |
git pull --rebase | |
else | |
echo "do_not_merge is set to true, so the PR will not be merged" | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment