@@ -138,6 +138,113 @@ Every new CLI option **must** be added in **all** of these locations:
138138- Each protocol (redis/memcached) has its own class hierarchy
139139- Connection state machine is in ` shard_connection.cpp `
140140
141+ ## Releasing
142+
143+ ### Release-note style
144+
145+ GitHub release notes are curated, not auto-generated. Follow the pattern established
146+ from 2.2.1 onward:
147+
148+ ``` markdown
149+ # What's Changed
150+
151+ ## New Features
152+ - ** Short title** : One-sentence description ending with the PR number (#123 ).
153+
154+ ## Bug Fixes
155+ - ** Short title** : Description with ` inline code ` for flag/identifier names (#124 ).
156+
157+ ## Packaging & Distribution
158+ - ...
159+
160+ ## Developer Tooling & CI Improvements
161+ - ...
162+
163+ ## Maintenance
164+ - ...
165+
166+ ## New Contributors
167+ - @handle made their first contribution (#125 )
168+
169+ ** Full Changelog** : https://github.com/redis/memtier_benchmark/compare/ <prev >...<this >
170+ ```
171+
172+ Conventions:
173+ - ` # What's Changed ` heading, then ` ## ` sections (only the sections that have
174+ content — drop empty ones).
175+ - Section order, when present: New Features → Bug Fixes → Packaging & Distribution
176+ → Developer Tooling & CI Improvements → Build & Configuration Improvements →
177+ Maintenance → AI → New Contributors.
178+ - Bullet form: `- ** Bold lead phrase** : Sentence-cased description ending with a
179+ period, with the PR number in parens before the period (#NNN).`
180+ - Use inline backticks for CLI flags, file names, and identifiers.
181+ - Close with the ` **Full Changelog** ` compare link.
182+ - Do * not* paste the GitHub auto-generated ` * by @user in <PR-link> ` style — it
183+ was used up to 2.2.0 but has been replaced by the curated form.
184+
185+ ### Patch releases (cherry-picks to a release branch)
186+
187+ Patch releases live on a release branch (e.g. ` 2.3 ` for the 2.3.x line). The flow:
188+
189+ 1 . ** List the candidates** : ` git log --oneline origin/master ^origin/<release-branch> ` .
190+ For each commit, decide whether it belongs in a patch release. Skip duplicates
191+ that were applied independently to the release branch (compare diffs, not just
192+ subjects — see ` 0cb8e9a ` vs ` d92b163 ` in the 2.3.1 history).
193+ 2 . ** Branch from the release branch** : ` git switch -c release.<X.Y.Z> origin/<release-branch> ` .
194+ 3 . ** Cherry-pick in chronological order** so the release-branch history reads
195+ forward. Resolve conflicts as they arise.
196+ 4 . ** Verify CI workflows fire on the release branch.** Workflow files
197+ (` asan.yml ` , ` ci.yml ` , ` code-style.yml ` , ` tsan.yml ` , ` ubsan.yml ` ,
198+ ` release-rpm.yml ` ) historically restricted ` pull_request: branches: ` to
199+ ` [master, main] ` only. A PR targeting the release branch will run ** zero**
200+ checks until the branch list includes the release branch — extend each
201+ workflow's ` branches: ` filter as part of the release PR if it is missing.
202+ 5 . ** Run ` utils/prepare_release.sh <X.Y.Z> ` ** as the last commit. See gotchas
203+ below.
204+ 6 . ** Open a single PR base ` <release-branch> ` ← ` release.<X.Y.Z> ` .** Don't split
205+ the cherry-picks and the version bump into separate PRs for a patch release;
206+ the atomic shape leaves the release branch self-consistent if the PR is
207+ delayed.
208+ 7 . ** After merge, tag and publish a Release** on the merged release-branch HEAD.
209+ The ` release: published ` event fires ` dockers.yml ` , ` release-rpm.yml ` , and
210+ ` release.yml ` (APT) automatically.
211+ 8 . ** Mirror any review-driven follow-ups back to master** as a separate PR.
212+ Cursor Bugbot frequently catches latent issues on release-prep PRs (e.g. the
213+ ` get_total_latency() ` narrowing flagged on the 2.3.1 backport); do not let
214+ those fixes live only on the release branch.
215+
216+ ### ` utils/prepare_release.sh ` gotchas
217+
218+ ` utils/prepare_release.sh <version> ` bumps ` configure.ac ` , runs
219+ ` autoreconf -ivf && ./configure && make && make rebuild-man ` , and commits
220+ ` configure.ac + memtier_benchmark.1 ` as a single ` Release <version> ` commit.
221+ Watch out for:
222+
223+ - ** Stale ` version.h ` corrupts the man page.** The Makefile rule
224+ ` version.h: $(SHELL) $(srcdir)/version.sh ` has ** no dependencies** , so once
225+ ` version.h ` exists ` make ` won't re-run ` version.sh ` . The script then bakes an
226+ outdated git SHA into ` memtier_benchmark.1 ` via ` help2man ` . ** Always
227+ ` rm -f version.h ` before running the script** so the SHA matches HEAD.
228+ - ** No branch sanity check.** The script doesn't verify the current branch.
229+ Confirm ` git branch --show-current ` matches the intended release branch
230+ before running.
231+ - ** No version-string validation.** A typo like ` 2.3.1. ` writes a broken
232+ ` AC_INIT ` line and the failure only surfaces on the next build. Sanity-check
233+ with ` grep AC_INIT configure.ac ` after the bump commit lands.
234+ - ** No tag is created.** The script commits but doesn't tag. After merge you
235+ still need ` git tag <X.Y.Z> ` on the merged release-branch tip, push the tag,
236+ and publish a GitHub Release for the workflows to fire.
237+ - ** Untracked-file check is dirty-state-only.** The script blocks on tracked
238+ modifications but ignores untracked files. Editor backups like
239+ ` configure.ac~ ` won't trigger the guard.
240+
241+ ### Release-note generation
242+
243+ Use ` gh release create <tag> --generate-notes --draft ` as a starting point,
244+ then rewrite the body in the curated style above before publishing. Don't
245+ publish the auto-generated draft as-is — it doesn't match the project's
246+ release-note conventions.
247+
141248## Debugging
142249
143250### Debug Build
0 commit comments