1+ #! /bin/bash
2+ set +x
3+
4+ # To hotfix:
5+ # 1. Make a new hotfix branch from release-al2
6+ # 2. Run this script from this hotfix branch. It will deploy the changes to stg and create a PR for release-al2.
7+ # 3. Verify the changes on stg.
8+ # 4. Merge the PR into release-al2. This deploys the changes to production.
9+ # 5. Make a PR and merge release-al2 back to develop to sync the changes with develop.
10+
11+ # CLI flags:
12+ # --recut If you have made changes after cutting, you can add this flag to use the newest changes for hotfix release.
13+ # --nosquash The script squashes the changes in the hotfix branch into a single commit by default. This flag prevents the squashing if you want to keep the all the commits in the hotfix branch.
14+
15+ # pre-requisites: install github CLI
16+ # - github documentation: https://github.com/cli/cli#installation
17+ # - github is remote 'origin'
18+ # - PRs use test section LAST with heading "## Tests"
19+ # - ALL build and release PRs start with "build: "
20+
21+ hotfix_branch=$( git branch --show-current)
22+
23+ if ! command -v gh > /dev/null 2>&1 ; then
24+ echo -e " \033[31mInstall gh first\033[0m"
25+ exit 1
26+ fi
27+
28+ if ! gh auth status > /dev/null 2>&1 ; then
29+ echo -e " \033[31mYou need to login: gh auth login\033[0m"
30+ exit 1
31+ fi
32+
33+ has_local_changes=$( git status --porcelain --untracked-files=no --ignored=no)
34+ if [[ ${has_local_changes} ]]; then
35+ set +x
36+ echo -e " \033[31m==========\033[0m"
37+ echo -e " \033[31mABORT: You have local modifications. Please stash or commit changes and run again.\033[0m"
38+ echo -e " \033[31m==========\033[0m"
39+ exit 1
40+ fi
41+
42+ # From current hotfix/<description> branch
43+ echo -e " \033[34mFetching latest tags and pulling latest changes\033[0m"
44+ git fetch --all --tags
45+ git pull
46+
47+ echo -e " \033[34mResetting to the latest commit on the hotfix branch\033[0m"
48+ git reset --hard ${hotfix_branch}
49+
50+ # Get the next patch version
51+ current_version=$( node -p " require('./package.json').version" )
52+ release_version=" v$( node -p '
53+ const [major, minor, patch] = require("./package.json").version.split(".");
54+ `${major}.${minor}.${parseInt(patch) + 1}`
55+ ' ) "
56+ release_branch=release_${release_version}
57+
58+ echo -e " \033[34mNext patch version: ${release_version} \033[0m"
59+
60+ may_force_push=
61+ if [[ " $1 " == " --recut" || " $2 " == " --recut" ]]; then
62+ echo -e " \033[34mRecutting: Deleting local and remote tag and release branch\033[0m"
63+ # Delete the local and remote tag for this release version if it exists.
64+ git tag -d ${release_version}
65+ git push --delete origin ${release_version}
66+ # Delete the local release branch for this release version if it exists.
67+ git branch -D ${release_branch}
68+ may_force_push=-f
69+ fi
70+
71+ echo -e " \033[34mCreate a temporary branch to commit version bump changes\033[0m"
72+ short_hash=$( git rev-parse --short HEAD)
73+ temp_release_branch=temp_${short_hash}
74+ # All subsequent actions are done on the temporary branch to prevent the hotfix branch from being polluted/modified.
75+ git checkout -b ${temp_release_branch}
76+
77+ # Squash the commit history into single commit titled the branch name, unless --nosquash is provided
78+ if [[ " $1 " != " --nosquash" && " $2 " != " --nosquash" ]]; then
79+ echo -e " \033[34mSquashing commit history into single commit titled the branch name\033[0m"
80+ git reset --soft release-al2
81+ git commit -a -n -m " fix: changes from ${hotfix_branch} "
82+ fi
83+
84+ echo -e " \033[34mBumping version to next patch version\033[0m"
85+ # Update the version in the root directory
86+ npm --no-git-tag-version version ${release_version}
87+ # Update the version in frontend directory
88+ npm --prefix frontend --no-git-tag-version version ${release_version}
89+
90+ git commit -a -n -m " chore: bump version to ${release_version} "
91+ git tag ${release_version}
92+
93+ echo -e " \033[34mCreating release branch to merge into release-al2\033[0m"
94+ git checkout -b ${release_branch}
95+
96+ echo -e " \033[34mDeleting temporary branch\033[0m"
97+ git branch -D ${temp_release_branch}
98+
99+ echo -e " \033[34mCreating the release branch to remote\033[0m"
100+ git push origin ${may_force_push} HEAD:${release_branch}
101+ echo -e " \033[34mPushing to stg for verification\033[0m"
102+ git push -f origin HEAD:stg
103+ git push origin ${release_version}
104+
105+ echo -e " \033[34mCreating PR for release ${release_version} into release-al2\033[0m"
106+ # # extract changelog to inject into the PR
107+ pr_body_file=.pr_body_${release_version}
108+ pr_body_file_grouped=.pr_body_${release_version} _grouped
109+
110+ awk " /#### \[${release_version} \]/{flag=1;next}/####/{flag=0}flag" CHANGELOG.md | sed -E ' /^([^-]|[[:space:]]*$)/d' > ${pr_body_file}
111+
112+ # Extract the new changes
113+ echo " ## New" > ${pr_body_file_grouped}
114+ echo " " >> ${pr_body_file_grouped}
115+ grep -v -E -- ' - [a-z]+\(deps(-dev)?\)' ${pr_body_file} >> ${pr_body_file_grouped}
116+
117+ # Extract production dependencies
118+ echo " " >> ${pr_body_file_grouped}
119+ echo " ## Dependencies" >> ${pr_body_file_grouped}
120+ echo " " >> ${pr_body_file_grouped}
121+ grep -E -- ' - [a-z]+\(deps\)' ${pr_body_file} >> ${pr_body_file_grouped}
122+
123+ # Extract dev-dependencies
124+ echo " " >> ${pr_body_file_grouped}
125+ echo " ## Dev-Dependencies" >> ${pr_body_file_grouped}
126+ echo " " >> ${pr_body_file_grouped}
127+ grep -E -- ' - [a-z]+\(deps-dev\)' ${pr_body_file} >> ${pr_body_file_grouped}
128+
129+ # Extract test procedures for feature PRs
130+ echo " " >> ${pr_body_file_grouped}
131+ echo " ## Tests" >> ${pr_body_file_grouped}
132+ echo " " >> ${pr_body_file_grouped}
133+ grep -v -E -- ' - [a-z]+\(deps(-dev)?\)' ${pr_body_file} | grep -v -E -- ' - build: ' | while read line_item; do
134+ pr_id=$( echo ${line_item} | grep -o -E ' \[`#\d+`\]' | grep -o -E ' \d+' )
135+ tests=$( gh pr view ${pr_id} | awk ' f;/^#+ Tests?/{f=1}' | sed -E " s/\[[Xx]\]/[ ]/" | sed -E " s/^(##+) /\1## /" )
136+ if [[ ${tests} =~ [^[:space:]] ]]; then
137+ echo ${line_item} | sed " s/^- /### /" >> ${pr_body_file_grouped}
138+ echo " ${tests} " >> ${pr_body_file_grouped}
139+ echo " " >> ${pr_body_file_grouped}
140+ fi
141+ done
142+
143+ # Creating PR to merge into release-al2
144+ gh pr create \
145+ -H " ${release_branch} " \
146+ -B " release-al2" \
147+ -t " build: release ${release_version} " \
148+ -F " ${pr_body_file_grouped} " \
149+ || gh pr edit ${release_branch} \
150+ -B " release-al2" \
151+ -t " build: release ${release_version} " \
152+ -F " ${pr_body_file_grouped} "
153+
154+ # Perform cleanup of temporary files and local release branch
155+ echo -e " \033[34mCleaning up temporary files and local release branch\033[0m"
156+ rm ${pr_body_file}
157+ rm ${pr_body_file_grouped}
158+ git checkout ${hotfix_branch}
159+ git branch -D ${release_branch}
160+
161+ echo -e " \033[34mHotfix preparation complete\033[0m"
0 commit comments