Skip to content

Commit 7fccc80

Browse files
committed
new script to merge in upstream changes
1 parent 635b25a commit 7fccc80

File tree

1 file changed

+230
-0
lines changed

1 file changed

+230
-0
lines changed

merge_upstream.sh

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
#!/bin/bash
2+
3+
# The script to merge in upstream changes.
4+
# Provide an argument of the release major-minor, in the format of `vX.Y`. For example:
5+
# `v1.123`. Detection will run looking for upstream release branch of that version.
6+
#
7+
# Three scenarios are handled by this script:
8+
# 1. New minor version(updating from `v1.123` to `v1.124`)
9+
# 2. Existing minor version with an upstream patch increment(updating from `v1.123.0` to
10+
# `v1.123.1`)
11+
# 3. Existing patch version with a local Famedly level patch increment(updating from
12+
# `v1.123.1_1` to `v1.123.1_2`)
13+
#
14+
# If the provided release version does not exist on this repo, it will be created.
15+
# If it already exists on this repo, even remotely, it will be found and then checked
16+
# out.
17+
# After resolving this branch, the appropriate upstream changes will be merged in if
18+
# they exist. If a merge conflict arises from merging in upstream, the script will exit.
19+
# Once any conflicts are resolved, `git add` and `git merge --continue` need to be ran.
20+
# Then, run this script again with the same argument to proceed with the next step. If
21+
# you have not correctly commited the changes from a conflict, they will be lost.
22+
# When there are no upstream changes and only local changes, the Famedly
23+
# patch level is incremented. No tags are generated in this script.
24+
# After the merge is completed, the CHANGES.md file is updated to include any changes
25+
# exclusive to Famedly's repo. These changes will be placed at the bottom of the current
26+
# most entry. Successive Famedly additions will stay in order, appended after.
27+
set -e
28+
29+
release_name=$1
30+
31+
if [ -z "${release_name}" ]; then
32+
echo "Usage: $0 <release_name in vX.Y format>"
33+
exit 1
34+
fi
35+
36+
if [ "${release_name}" = "-h" ]; then
37+
echo "Usage: $0 <release_name in vX.Y format>"
38+
exit 0
39+
fi
40+
41+
# The minor number of the version, so of the release_name above which looks like `vX.Y`,
42+
# we are peeling off the `Y` for further use
43+
expected_minor=${release_name##*.}
44+
45+
# What our branch name will be. Upstream uses the format of `release-vX.Y` so to avoid
46+
# confusion we will pre-pend 'famedly' to that and use a `/` to namespace it. Like this:
47+
# `famedly-release/vX.Y`. We do want patch levels and hotfixes to live on the same
48+
# branch as the release major.minor
49+
release_branch_name="famedly-release/${release_name}"
50+
51+
# The upstream repo's version appropriate branch name
52+
upstream_release_branch_name="release-${release_name}"
53+
54+
echo -e "\e[32m>>>> fetching origin branches\e[0m"
55+
# If the release_name that was passed already exists as a tag, this will fatal error.
56+
# Make sure it does not legitimately exist, or that you really mean to replace it before
57+
# running `git tag -d <release_name>` to remove that one single tag. XXX: this may be wrong now, double check
58+
git fetch --tags origin
59+
60+
echo -e "\e[32m>>>> fetching upstream branches\e[0m"
61+
git fetch --tags upstream
62+
63+
echo -e "\e[32m>>>> checkout master branch\e[0m"
64+
git checkout master
65+
echo -e "\e[32m>>>> resetting master branch\e[0m"
66+
git reset --hard
67+
68+
echo -e "\e[32m>>>> find/checkout release branch\e[0m"
69+
# Temporarily disable error catching, as it will cause the script to halt
70+
# Normally this is not needed, but apparently our chosen branch name for famedly
71+
# releases causes fatal errors if it does not already exist. I believe it is caused by
72+
# the '/' in the branch name and how that can be a confusing reference for git to try
73+
# and sort out.
74+
# Specifically `famedly-release/vX.Y` comes across as 'fatal' instead of just
75+
# 'non-existent'. I choose to blame the '/'
76+
set +e
77+
78+
# Expect this a '0' for exists and a '1' for not-exists. Fatal is a '128'. We apply a
79+
# NOT so the condition only runs if there was an error
80+
if ! git switch "$release_branch_name"; then
81+
echo "branch did not exist, creating"
82+
# -c creates the branch at our current checkout. Which should be a clean checkout of
83+
# the master branch from a moment ago
84+
git switch -c "$release_branch_name"
85+
fi
86+
# Have to re-enable error catching in case something else breaks
87+
set -e
88+
89+
# Fetch existing tags on this branch. If this was a new branch from main, ours will be the
90+
# last tag added. If this was an existing branch we are appending to, ours will still be
91+
# the last tag added. Note that this may differ from what we are releasing if this the
92+
# initial version the branch contains. For example, if we are merging in `v1.124`, this
93+
# will be `v1.123.0_1` or similar
94+
last_tag_on_this_branch=$(git describe --tags --abbrev=0 --match v*.*.*_*)
95+
96+
# Our patch level version, everything after the `_`
97+
famedly_patch_level=${last_tag_on_this_branch#*_}
98+
99+
# The major, minor and patch level, so `vX.Y.Z`
100+
long_ver=${last_tag_on_this_branch%_*}
101+
102+
# Just the patch level integer, so `Z`
103+
patch_level=${long_ver##*.}
104+
105+
# The `vX.Y`.
106+
major_minor=${long_ver%.*}
107+
108+
# and just the `Y`
109+
minor_ver=${major_minor##*.}
110+
111+
112+
# First determine if the upstream branch has incremented it's patch level. These are in
113+
# the format of `vX.Y.Z`
114+
upstreams_last_tag=$(git ls-remote --tags upstream "$release_name"* | grep -o 'refs/tags/v[0-9]*\.[0-9]*\.[0-9]*' | sort -rV | head -n1 | cut --delimiter='/' --fields=3)
115+
# This should just be the `Z`
116+
upstream_patch_level=${upstreams_last_tag##*.}
117+
# The `vX.Y`
118+
upstream_major_minor=${upstreams_last_tag%.*}
119+
# And just the `Y`
120+
upstream_minor_ver=${upstream_major_minor##.*}
121+
122+
# if upstream patch level has increased, we bump ours too
123+
if [[ "$expected_minor" != "$minor_ver" ]]; then
124+
# Must have been a version bump which means this is a new minor version release. In
125+
# this case, it's likely that it is the same as what upstream's is. This resets the
126+
# Famedly patch level.
127+
expected_new_version_tag="${upstreams_last_tag}_1"
128+
echo "Likely new minor version $expected_new_version_tag"
129+
130+
elif [[ "$upstream_patch_level" -gt "$patch_level" ]]; then
131+
# No minor version bump but there was a patch level version bump, increment our
132+
# expected patch level and reset the Famedly patch level.
133+
expected_new_version_tag="${upstreams_last_tag}_1"
134+
echo "Likely new patch version $expected_new_version_tag"
135+
136+
else
137+
# No minor and no patch level increase, this is probably just a famedly patch level
138+
# increase.
139+
increment_patch_level=$(( famedly_patch_level + 1))
140+
expected_new_version_tag="${upstreams_last_tag}_$increment_patch_level"
141+
echo "Likely new famedly patch version $expected_new_version_tag"
142+
143+
fi
144+
145+
echo -e "\e[32m>>>> merging upstream into release branch\e[0m"
146+
# We let this error if there was a merge conflict that needs to be resolved. It should
147+
# be an idempotent process of just rerunning the script to continue on. After a
148+
# `git merge --continue`, of course
149+
set +e
150+
if ! git merge "upstream/release-${release_name}" -m "Famedly ${release_name}"; then
151+
echo "An error was detected. Fix the conflicts, 'git add' the changes, run 'git merge --continue' and re-run this script with the same arguments"
152+
exit 1
153+
fi
154+
set -e
155+
156+
# Retrieve the git log entries themselves and format them to fit well into the
157+
# markdown format. If there were none, we can skip this whole next section. This will
158+
# only pull the changes between the lastest tag found on this branch and master when the
159+
# tag is in our format and discards merges
160+
git_log_output=$(git log $(git describe --tags --abbrev=0 --match v*.*.*_*)..master --pretty=format:'- %s %C(bold blue)(%an)%Creset\' --no-merges --no-decorate)
161+
if [[ ! -z "$git_log_output" ]]; then
162+
echo -e "\n"
163+
echo "Note: Entries to be added to the CHANGES.md file are detected. I can do this automatically for you."
164+
echo -e "\e[31m WARNING: if you need to run this script multiple times, only do this ONCE!\e[0m"
165+
read -n 1 -p "Process Famedly change log entries, press 'y' or Enter to skip: " input_key
166+
echo -e "\n"
167+
if [[ "$input_key" == "y" ]]; then
168+
echo -e "\e[32m>>>> Adding Famedly changes to CHANGES.md\e[0m"
169+
echo -e "\e[32m>>>> ONLY RUN THIS ONE TIME\e[0m"
170+
# Find out how many lines down in CHANGES.md that the previous '# Synapse' entry
171+
# was. This gives us the line number needed to run the insertion.
172+
changelog_line_number=$(grep -n "# Synapse" CHANGES.md | awk 'NR==2 {print $1}' FS=":")
173+
174+
# Shell expansion is stupid when it comes to escaping things. This is a hack to get
175+
# the new line character into a string that sed will actually interpret that does
176+
# not interfere with already appended '\' from the git log output. Used at the end
177+
# to give us the extra line inside the markdown.
178+
NL=$'\n'
179+
# Construct the sed command. This is a little messy. sed requires that new lines are
180+
# a '\' and not a normal ansi '\n'(and it will insert that for us). We need a basic
181+
# header for the entry and an additional new line at the end(the markdown itself
182+
# does not care but this makes it easier to find when opening the file directly).
183+
sed_command="${changelog_line_number} i\### Famedly additions for ${expected_new_version_tag}\n\n"
184+
sed_command+="${git_log_output}${NL}"
185+
186+
# That should do it. The changes from our repo that occurred since the last upstream
187+
# merge(specifically that do not include said merge) should be inserted just above
188+
# that previous upstream merge(and just underneath the merge we are in the middle
189+
# of).
190+
sed -i "${sed_command}" CHANGES.md
191+
192+
# Amend the last commit(which should be the merge commit itself) to incorporate
193+
# changes from the changelog just produced
194+
echo -e "\e[32m>>>> Amending last commit with Famedly changelog entries\e[0m"
195+
git add CHANGES.md
196+
git commit --amend --no-edit --allow-empty
197+
fi
198+
fi
199+
200+
read -n 1 -p "Press 't' to run linting and tests, or any other key to skip: " input_key
201+
echo -e "\n"
202+
if [[ $input_key == "t" ]]; then
203+
echo -e "\e[32m>>>> running lint and tests...\e[0m"
204+
# Make sure there are no weirdities around poetry. Until the deprecation migration
205+
# occurs, expect orange things to read here.
206+
poetry check
207+
poetry install --extras all --no-interaction
208+
poetry run ./scripts-dev/lint.sh
209+
210+
logical_cores=$([ $(uname) = 'Darwin' ] &&
211+
sysctl -n hw.logicalcpu_max ||
212+
nproc)
213+
214+
poetry run trial -j"${logical_cores}" tests
215+
echo -e "\e[32m>>>> Testing successful!\e[0m"
216+
fi
217+
218+
read -n 1 -p "Press 'p' to push branch to Github, or any other key to skip: " input_key
219+
echo -e "\n"
220+
if [[ $input_key == "p" ]]; then
221+
echo -e "\e[32m>>>> pushing release branch\e[0m"
222+
git push --force -u origin "${release_branch_name}"
223+
echo "Visit online `https://github.com/famedly/synapse/pull/new/${release_branch_name}`"
224+
echo "to open a pull request for this release."
225+
else
226+
echo "Not pushing branch. Run 'git push --force -u origin ${release_branch_name}' when ready"
227+
fi
228+
229+
echo -e "\e[32m>>>> Finished!\e[0m"
230+
echo -e "\e[32m>> Recommend using $expected_new_version_tag as new release_name when running make_release.sh"

0 commit comments

Comments
 (0)