33export LC_ALL=C
44set -eo pipefail
55
6+ # Setup base branch and Bitcoin/Elements remote names.
67BASE_ORIG=merged-master
78BASE=" ${BASE_ORIG} "
89BITCOIN_UPSTREAM_REMOTE=bitcoin
9- BITCOIN_UPSTREAM=" ${BITCOIN_UPSTREAM_REMOTE} /master"
10- # ELEMENTS_UPSTREAM_REMOTE=upstream
11- # ELEMENTS_UPSTREAM="${ELEMENTS_UPSTREAM_REMOTE}/master"
10+ export BITCOIN_UPSTREAM=" ${BITCOIN_UPSTREAM_REMOTE} /master"
11+ ELEMENTS_UPSTREAM_REMOTE=upstream
12+ export ELEMENTS_UPSTREAM=" ${ELEMENTS_UPSTREAM_REMOTE} /master"
1213
14+ # START USER CONFIG:
15+ # Set your target upstream here
16+ TARGET_UPSTREAM=$BITCOIN_UPSTREAM
17+ TARGET_NAME=" Bitcoin"
18+ PR_PREFIX=" bitcoin/bitcoin"
19+ # TARGET_UPSTREAM=$ELEMENTS_UPSTREAM
20+ # TARGET_NAME="Elements"
21+ # PR_PREFIX="ElementsProject/elements"
22+
23+ # Set your git worktree location here. This is where the merges will be done, and where you should checkout the merged-master branch.
24+ WORKTREE=" /home/byron/code/elements-worktree"
25+
26+ # Set your parallellism during build/test. You probably want as many cores as possible.
27+ # Parallel functional tests can somewhat exceed your core count, depends on the build machine CPU/RAM.
28+ PARALLEL_BUILD=23 # passed to make -j
29+ PARALLEL_TEST=46 # passed to test_runner.py --jobs
30+ PARALLEL_FUZZ=12 # passed to test_runner.py -j when fuzzing
31+
32+ # Setup a ccache dir if necessary.
33+ # export CCACHE_DIR="/tmp/ccache"
34+ # export CCACHE_MAXSIZE="20G"
35+
36+ # Set and export a WEBHOOK environment variable to a Discord webhook URL outside of this script to get notifications of progress and failures.
37+
38+ # We don't currently fuzz during merging. Check fuzzing and CI after a merge run.
1339# Replace this with the location where we should put the fuzz test corpus
14- BITCOIN_QA_ASSETS=" ${HOME} /.tmp/bitcoin/qa-assets"
15- FUZZ_CORPUS=" ${BITCOIN_QA_ASSETS} /fuzz_seed_corpus/"
16- mkdir -p " $( dirname " ${BITCOIN_QA_ASSETS} " ) "
17-
18- # BEWARE: On some systems /tmp/ gets periodically cleaned, which may cause
19- # random files from this directory to disappear based on timestamp, and
20- # make git very confused
21- WORKTREE=" ${HOME} /.tmp/elements-merge-worktree"
22- mkdir -p " ${HOME} /.tmp"
23-
24- # These should be tuned to your machine; below values are for an 8-core
25- # 16-thread macbook pro
26- PARALLEL_BUILD=4 # passed to make -j
27- PARALLEL_TEST=12 # passed to test_runner.py --jobs
28- PARALLEL_FUZZ=8 # passed to test_runner.py -j when fuzzing
40+ # BITCOIN_QA_ASSETS="${HOME}/code/bitcoin/qa-assets"
41+ # FUZZ_CORPUS="${BITCOIN_QA_ASSETS}/fuzz_seed_corpus/"
2942
43+ # END USER CONFIG
44+
45+ # Script
3046SKIP_MERGE=0
3147DO_BUILD=1
3248KEEP_GOING=1
49+ DO_TEST=1
50+ DO_FUZZ=0
51+ NUM=15
52+ COUNT=0
3353
3454if [[ " $1 " == " setup" ]]; then
3555 echo " Setting up..."
3656 echo
37- git config remote.upstream.url > /dev/null || remote add upstream " https://github.com/ElementsProject/elements.git"
57+ git config remote.upstream.url > /dev/null || git remote add upstream " https://github.com/ElementsProject/elements.git"
3858 git config remote.bitcoin.url > /dev/null || git remote add bitcoin " https://github.com/bitcoin/bitcoin.git"
3959 if git worktree list --porcelain | grep --silent prunable; then
4060 echo " You have stale git worktrees, please either fix them or run 'git worktree prune'."
@@ -45,14 +65,7 @@ if [[ "$1" == "setup" ]]; then
4565 echo " Fetching all remotes..."
4666 echo
4767 git fetch --all
48- echo
49- # echo "Cloning fuzz test corpus..."
50- # echo
51- # if [[ ! -d "${BITCOIN_QA_ASSETS}" ]]; then
52- # cd "$(dirname ${BITCOIN_QA_ASSETS})" && git clone https://github.com/bitcoin-core/qa-assets.git
53- # fi
54- # echo
55- echo " Done! Remember to also check out merged-master, and push it back up when finished."
68+ echo " Done! Remember to also checkout merged-master at ${WORKTREE} "
5669 exit 0
5770elif [[ " $1 " == " continue" ]]; then
5871 SKIP_MERGE=1
@@ -62,17 +75,35 @@ elif [[ "$1" == "list-only" ]]; then
6275 DO_BUILD=0
6376elif [[ " $1 " == " step" ]]; then
6477 KEEP_GOING=0
78+ elif [[ " $1 " == " merge-only" ]]; then
79+ SKIP_MERGE=0
80+ KEEP_GOING=0
81+ DO_BUILD=0
82+ DO_TEST=0
6583elif [[ " $1 " == " step-continue" ]]; then
6684 SKIP_MERGE=1
6785 KEEP_GOING=0
86+ elif [[ " $1 " == " step-test" ]]; then
87+ SKIP_MERGE=1
88+ KEEP_GOING=0
89+ DO_BUILD=0
90+ elif [[ " $1 " == " step-fuzz" ]]; then
91+ SKIP_MERGE=1
92+ KEEP_GOING=0
93+ DO_BUILD=0
94+ DO_TEST=0
95+ elif [[ " $1 " == " analyze" ]]; then
96+ DO_BUILD=0
97+ DO_TEST=0
6898else
69- echo " Usage: $0 <setup|list-only|go|continue|step|step-continue>"
99+ echo " Usage: $0 <setup|list-only|go|continue|step|step-continue|analyze >"
70100 echo " setup will configure your repository for the first run of this script"
71101 echo " list-only will simply list all the PRs yet to be done"
72102 echo " go will try to merge every PR, building/testing each"
73103 echo " continue assumes the first git-merge has already happened, and starts with building"
74104 echo " step will try to merge/build/test a single PR"
75105 echo " step-continue assumes the first git-merge has already happened, and will try to build/test a single PR"
106+ echo " analyze will analyze the next $NUM PRs and count conflicts NB: DO NOT CTRL+C THIS PROCESS"
76107 echo
77108 echo " Prior to use, please create a git worktree for the elements repo at:"
78109 echo " $WORKTREE "
@@ -101,54 +132,95 @@ if [[ "$SKIP_MERGE" == "1" ]]; then
101132fi
102133
103134# # Get full list of merges
104- # for elements
105- # COMMITS=$(git -C "$WORKTREE" log "$ELEMENTS_UPSTREAM" --not $BASE --merges --first-parent --pretty='format:%ct %cI %h Elements %s')
106- # for bitcoin
107- COMMITS=$( git -C " $WORKTREE " log " $BITCOIN_UPSTREAM " --not $BASE --merges --first-parent --pretty=' format:%ct %cI %h Bitcoin %s' )
135+ COMMITS=$( git -C " $WORKTREE " log " $TARGET_UPSTREAM " --not $BASE --merges --first-parent --pretty=" format:%ct %cI %h $TARGET_NAME %s" )
108136
109- cd " $WORKTREE "
137+ cd " $WORKTREE " || exit 1
110138
111139VERBOSE=1
112140
141+ echo start > merge.log
142+
113143quietly () {
114144 if [[ " $VERBOSE " == " 1" ]]; then
115- " $@ "
145+ date | tee --append merge.log
146+ time " $@ " 2>&1 | tee --append merge.log
116147 else
117148 chronic " $@ "
118149 fi
119150}
120151
152+ notify () {
153+ local MESSAGE=" $1 "
154+ local JSON=" {\" content\" : \" $MESSAGE \" }"
155+ if [ -n " $WEBHOOK " ]; then
156+ curl -d " $JSON " -H " Content-Type: application/json" " $WEBHOOK "
157+ else
158+ echo " $MESSAGE "
159+ fi
160+ if [[ " $2 " == " 1" ]]; then
161+ exit 1
162+ fi
163+ }
164+
121165# # Sort by unix timestamp and iterate over them
122- # echo "$ELT_COMMITS" "$BTC_COMMITS" | sort -n -k1 | while read line
123166echo " $COMMITS " | tac | while read -r line
124167do
125- echo
126- echo " =-=-=-=-=-=-=-=-=-=-="
127- echo
128-
129- echo -e " $line "
130168 # # Extract data and output what we're doing
131- DATE=$( echo " $line " | cut -d ' ' -f 2)
132169 HASH=$( echo " $line " | cut -d ' ' -f 3)
133170 CHAIN=$( echo " $line " | cut -d ' ' -f 4)
134- PR_ID=$( echo " $line " | cut -d ' ' -f 6 | tr -d :)
135- PR_ID_ALT=$( echo " $line " | cut -d ' ' -f 8 | tr -d :)
171+ PR_ID=$( echo " $line " | grep -o -P " #\d+" )
136172
137- if [[ " $PR_ID " == " pull" ]]; then
138- PR_ID=" ${PR_ID_ALT} "
139- fi
140- echo -e " $CHAIN PR \e[37m$PR_ID \e[33m$HASH \e[0m on \e[32m$DATE \e[0m "
173+ GIT_HEAD=$( git rev-parse HEAD)
141174
142175 # # Do it
143176 if [[ " $1 " == " list-only" ]]; then
177+ echo -e " $line "
144178 continue
145179 fi
180+ if [[ " $1 " == " analyze" ]]; then
181+ COUNT=$(( COUNT + 1 ))
182+ # todo: count conflicts in critical files
183+ # CRITICAL_FILES=("src/wallet/spend.h", "src/wallet/spend.cpp")
184+ MERGE_FILE=" /tmp/$HASH .merge"
185+ DIFF_FILE=" /tmp/$HASH .diff"
186+ git -C " $WORKTREE " merge " $HASH " --no-ff -m " Merge $HASH into merged_master ($CHAIN PR $PR_PREFIX$PR_ID )" > " $MERGE_FILE " || true
187+ git -C " $WORKTREE " diff > " $DIFF_FILE "
188+ git -C " $WORKTREE " reset --hard " $GIT_HEAD " > /dev/null
189+ # FILES=$(grep "CONFLICT" "$MERGE_FILE")
190+ NUM_FILES=$( grep -c " CONFLICT" " $MERGE_FILE " )
191+ NUM_CONFLICTS=$( grep -c " <<<<<<<" " $DIFF_FILE " )
192+ echo " $COUNT . Merge up to $PR_ID ($HASH ) has $NUM_CONFLICTS conflicts in $NUM_FILES files."
193+ if [[ " $COUNT " == " $NUM " ]]; then
194+ exit 0
195+ else
196+ continue
197+ fi
198+ fi
199+ notify " starting merge of $PR_ID "
200+
201+ # check for stoppers and halt if found
202+ # a stopper is normally the PR after the version has been changed
203+ # ie. the branch point we want to stop at for this version
204+ STOPPERS=(
205+ " #30716" # bitcoin v28
206+ " #32041" # bitcoin v29
207+ )
208+ for STOPPER in " ${STOPPERS[@]} "
209+ do
210+ if [[ " $PR_ID " == * " $STOPPER " * ]]; then
211+ echo " Found $STOPPER in $PR_ID ! Exiting."
212+ notify " hit stopper, exiting"
213+ exit 1
214+ else
215+ echo " Didn't find $STOPPER in $PR_ID . Continuing."
216+ fi
217+ done
146218
147219 if [[ " $SKIP_MERGE " == " 1" ]]; then
148220 echo -e " Continuing build of \e[37m$PR_ID \e[0m at $( date) "
149221 else
150222 echo -e " Start merge/build of \e[37m$PR_ID \e[0m at $( date) "
151- git -C " $WORKTREE " merge " $HASH " --no-ff -m " Merge $HASH into merged_master ($CHAIN PR $PR_ID )"
223+ git -C " $WORKTREE " merge " $HASH " --no-ff -m " Merge $HASH into merged_master ($CHAIN PR $PR_PREFIX$ PR_ID )" || notify " fail merge " 1
152224 fi
153225
154226 if [[ " $DO_BUILD " == " 1" ]]; then
163235 # The following is an expansion of `make check` that skips the libsecp
164236 # tests and also the benchmarks (though it does build them!)
165237 echo " Building"
166- quietly make -j" $PARALLEL_BUILD " -k
167- # quietly make -j1 check
168- echo " Linting"
169- quietly ./ci/lint/06_script.sh
238+ quietly make -j" $PARALLEL_BUILD " -k || notify " fail build" 1
239+ # todo: fix linting step
240+ # echo "Linting"
241+ # quietly ./ci/lint/06_script.sh || notify "fail lint"
242+ fi
243+
244+ if [[ " $DO_TEST " == " 1" ]]; then
170245 echo " Testing"
171- quietly ./src/qt/test/test_elements-qt
172- quietly ./src/test/test_bitcoin
173- quietly ./src/bench/bench_bitcoin
174- quietly ./test/util/bitcoin-util-test.py
175- quietly ./test/util/rpcauth-test.py
176- quietly make -C src/univalue/ check
246+ quietly ./src/qt/test/test_elements-qt || notify " fail test qt" 1
247+ quietly ./src/test/test_bitcoin || notify " fail test bitcoin" 1
248+ quietly ./src/bench/bench_bitcoin || notify " fail test bench" 1
249+ quietly ./test/util/test_runner.py || notify " fail test util" 1
250+ quietly ./test/util/rpcauth-test.py || notify " fail test rpc" 1
177251 echo " Functional testing"
178- quietly ./test/functional/test_runner.py --jobs=" $PARALLEL_TEST "
252+ quietly ./test/functional/test_runner.py --jobs=" $PARALLEL_TEST " || notify " fail test runner" 1
253+ fi
254+
255+ if [[ " $DO_FUZZ " == " 1" ]]; then
179256 echo " Cleaning for fuzz"
180257 quietly make distclean || true
181258 quietly git -C " $WORKTREE " clean -xf
182259 echo " Building for fuzz"
183260 quietly ./autogen.sh
184261 # TODO turn on `,integer` after this rebase
185- quietly ./configure --with-incompatible-bdb -- enable-fuzz --with-sanitizers=address,fuzzer,undefined CC=clang CXX=clang++
262+ quietly ./configure --enable-fuzz --with-sanitizers=address,fuzzer,undefined CC=" ccache clang" CXX=" ccache clang++"
186263 quietly make -j" $PARALLEL_BUILD " -k
187264 echo " Fuzzing"
188- quietly ./test/fuzz/test_runner.py -j" $PARALLEL_FUZZ " " ${FUZZ_CORPUS} "
265+ quietly ./test/fuzz/test_runner.py -j" $PARALLEL_FUZZ " " ${FUZZ_CORPUS} " || notify " fail fuzz " 1
189266 fi
190267
191268 if [[ " $KEEP_GOING " == " 0" ]]; then
192- exit 1
269+ notify " $PR_ID done, exiting"
270+ exit 0
271+ else
272+ echo " $PR_ID done, continuing"
193273 fi
194274
195- # bummer1.sh
196275 SKIP_MERGE=0
276+ echo " end" >> merge.log
197277done
0 commit comments