mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-10-21 11:22:31 +00:00
docs: cover pre-releases and upgrade notes in the release-note skill (#9635)
* docs: cover pre-releases and upgrade notes in the release-note skill Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * docs: add style and length rules to the release-note skill Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * docs: drop machine-specific notes from the release-note skill Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * docs: base the first pre-release on the previous release branch point Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * docs: subtract the whole previous line when building a minor release note Signed-off-by: Dennis Zhuang <killme2008@gmail.com> --------- Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: greptimedb-release-note
|
||||
description: Generate a GreptimeDB release changelog with git cliff (correct range, subtract already-released patch PRs, rebuild contributors, add human-curated highlights), output to a file, and prepare the docs-repo blog PR. Use when asked to write/generate a GreptimeDB release note or changelog.
|
||||
description: Generate a GreptimeDB release changelog (minor, patch, or alpha/beta/rc pre-release) with git cliff (correct range, subtract already-released patch PRs, rebuild contributors, add human-curated highlights and upgrade notes), output to a file, and prepare the docs-repo blog PR. Use when asked to write/generate a GreptimeDB release note or changelog.
|
||||
---
|
||||
|
||||
# GreptimeDB Release Note / Changelog
|
||||
@@ -27,41 +27,54 @@ a token it still runs, but may be rate-limited / incomplete.
|
||||
|
||||
## 2. Determine the previous version (skip nightlies)
|
||||
|
||||
From `gh release list --repo GreptimeTeam/greptimedb`, ignore `*-nightly-*`, `-rc.*`, `-beta.*`,
|
||||
and build-suffixed tags; focus on formal `vX.Y.Z`.
|
||||
From `gh release list --repo GreptimeTeam/greptimedb`, ignore `*-nightly-*` and build-suffixed
|
||||
tags (e.g. `v1.3.0-alpha.1-3c0e2a8d5-20260928-1790639248`).
|
||||
- Patch `vX.Y.Z` (Z>0): previous = `vX.Y.(Z-1)`.
|
||||
- New minor `vX.Y.0`: previous = the latest `vX.(Y-1).*` (for `v1.0.0`, the biggest `0.x`).
|
||||
- Pre-release `vX.Y.Z-{alpha,beta,rc}.N`: previous = the latest pre-release tag of the same
|
||||
`vX.Y.Z` (e.g. `v1.3.0-beta.1` for `v1.3.0-beta.2`). If there is none (the first alpha),
|
||||
see "First pre-release" in §3: the previous minor's `.0` may not be published yet, and since
|
||||
v1.2 a `.0` tag sits on `release/vX.Y`, not on `main`.
|
||||
Double-check with the user.
|
||||
|
||||
## 3. Pick the range and generate
|
||||
|
||||
Key topology fact: a **minor tag** (e.g. `v1.0.0`) is an ancestor of `main`; **patch tags**
|
||||
(`v1.0.1`, `v1.0.2`) live on the `release/v1.0` branch and are **NOT** ancestors of `main`
|
||||
(they are cherry-picks with different SHAs). Verify with
|
||||
Key topology fact: **patch tags** (`v1.0.1`, `v1.0.2`) live on `release/vX.Y` and are **NOT**
|
||||
ancestors of `main` (they are cherry-picks with different SHAs). Up to v1.1 the `.0` tag was
|
||||
the point where `release/vX.Y` left `main`; from v1.2 the branch is cut before the `.0`, and
|
||||
pre-releases and the `.0` itself are tagged on the branch with backports. Verify with
|
||||
`git merge-base --is-ancestor <tag> <remote>/main`.
|
||||
|
||||
### New minor (`X.Y.0`, cut from main)
|
||||
Note the two different tags here: the `git cliff` **base is the previous minor `.0` tag**
|
||||
(e.g. `v1.0.0`, an ancestor of `main`) — **not** the "previous version" from §2 (the latest
|
||||
patch, e.g. `v1.0.2`), which is only used below to decide which patch PRs to subtract.
|
||||
### New minor (`X.Y.0`)
|
||||
The note compares against the whole previous line: everything new since `release/vX.(Y-1)`
|
||||
left `main`, minus what any published `vX.(Y-1).*` release (pre-releases, `.0`, patches)
|
||||
already shipped. The line's own pre-releases (`vX.Y.0-beta.*`) are not subtracted; the `.0`
|
||||
note covers the whole minor.
|
||||
|
||||
Base = the **previous minor tag** (e.g. `v1.0.0`); tip = the release commit (= `release/vX.Y`
|
||||
tip, usually `<remote>/main`):
|
||||
Base = the **previous minor tag** (e.g. `v1.1.0`); tip = the tagged `vX.Y.0` commit, resolved as
|
||||
in "Pre-release" below. Split the range at the point where `release/vX.Y` left `main`:
|
||||
```
|
||||
GITHUB_TOKEN=$(gh auth token) git cliff <prev-minor-tag>..<release-commit> --tag vX.Y.0 \
|
||||
-o /path/CHANGELOG-vX.Y.0.md
|
||||
bp=$(git merge-base <remote>/release/vX.Y <remote>/main)
|
||||
```
|
||||
`cliff.toml`'s `ignore_tags` folds in-range nightly tags into the single section.
|
||||
- `<prev-minor-tag>..$bp` is on `main`; generate it with `git cliff`:
|
||||
```
|
||||
GITHUB_TOKEN=$(gh auth token) git cliff <prev-minor-tag>..$bp --tag vX.Y.0 \
|
||||
-o /path/CHANGELOG-vX.Y.0.md
|
||||
```
|
||||
`cliff.toml`'s `ignore_tags` folds in-range nightly tags into the single section.
|
||||
- `$bp..<tip>` is the release branch: backports, squashed picks, and commits without a PR.
|
||||
`git cliff` fails on such commits (`Filter call 'sort' failed`), so build this part as a
|
||||
pre-release: collect the original PRs with the loop in "Pre-release" and add their bullets.
|
||||
List the commits that carry no PR with
|
||||
`git log --format='%h %s' $bp..<tip> | grep -vE '\(#[0-9]+\)$'`; drop mechanical ones, and
|
||||
for a manual port of a `main` PR (the body says `cherry picked from commit` a SHA that is not
|
||||
on `main`), find that PR by title and cite it.
|
||||
|
||||
**Then subtract PRs already shipped in the intermediate patch releases** (`vX.(Y-1).1`,
|
||||
`.2`, …) — their main-branch commits are inside the range and would duplicate, and the
|
||||
audience cares about what's new vs the latest patch. Collect the PR set from the patch release
|
||||
bodies and remove matching lines:
|
||||
```
|
||||
gh release view vX.(Y-1).Z --repo GreptimeTeam/greptimedb | grep -oE 'pull/[0-9]+'
|
||||
```
|
||||
Remove every changelog bullet whose `#NNNN` is in that combined set (a small Python script is
|
||||
the reliable way — match `pull/<n>)` on lines starting with `*`).
|
||||
**Then subtract PRs already shipped in published `vX.(Y-1).*` releases**: their `main` commits
|
||||
are inside the range. Build the set as for the first pre-release (below): the PR links in
|
||||
every published `vX.(Y-1).*` release body, plus the `(#N)` line endings in the history of the
|
||||
newest published `vX.(Y-1).*` tag. Remove every changelog bullet whose `#NNNN` is in that set (a
|
||||
small Python script is the reliable way — match `pull/<n>)` on lines starting with `*`).
|
||||
|
||||
### Patch (`X.Y.Z`, Z>0, cherry-picks on the release branch)
|
||||
The previous patch tag is an ancestor of the release branch, so a plain range works **when the
|
||||
@@ -99,11 +112,78 @@ user which they prefer**):
|
||||
If the branch history is otherwise messy, the same "identify the picked PRs and keep only those"
|
||||
rule applies.
|
||||
|
||||
### First pre-release (`X.Y.0-alpha.1`, cut from main)
|
||||
The base is the commit where the previous line's release branch left `main`. It exists when the
|
||||
alpha is cut, even if `vX.(Y-1).0` does not:
|
||||
```
|
||||
base=$(git merge-base <remote>/release/vX.(Y-1) <tag>)
|
||||
```
|
||||
If there is no `release/vX.(Y-1)`, use the newest published release tag that is an ancestor of
|
||||
the tag (`git merge-base --is-ancestor <candidate> <tag>`). Run `git cliff <base>..<tag>`, then
|
||||
subtract every PR that already shipped in a published `vX.(Y-1).*` release, pre-releases
|
||||
included. Take the union of the PR links in those release bodies and the `(#N)` line endings in
|
||||
the history of the newest published `vX.(Y-1).*` tag:
|
||||
```
|
||||
gh release view <published-vX.(Y-1)-tag> --repo GreptimeTeam/greptimedb | grep -oE 'pull/[0-9]+'
|
||||
git log <newest-published-vX.(Y-1)-tag> --format=%B | grep -oE '\(#[0-9]+\)$'
|
||||
```
|
||||
Either source alone misses PRs: a release body can omit a backport that its tag contains, and a
|
||||
backport commit can name only the backport PR.
|
||||
|
||||
### Pre-release (`X.Y.Z-{alpha,beta,rc}.N` after an earlier pre-release)
|
||||
**The tip is the tagged commit, not the release branch tip.** Resolve it from the remote and
|
||||
write this SHA in the docs PR body (§8) as what the note was checked against:
|
||||
```
|
||||
git ls-remote --tags <remote> 'vX.Y.Z-beta.N*'
|
||||
```
|
||||
Use the `refs/tags/vX.Y.Z-beta.N` line (its `^{}` line if the tag is annotated), not the
|
||||
build-suffixed tags it also lists. PRs that land on `release/vX.Y` after the tag
|
||||
(`git log --oneline <tag>..<remote>/release/vX.Y`) belong to the next release, even if they are
|
||||
already merged when you write the note.
|
||||
|
||||
The range `<prev-pre-release>..<tag>` usually mixes two kinds of commits: individual backports
|
||||
(`... [Backport release/vX.Y] (#NNNN)`) and squashed pick commits (`chore: backport selected
|
||||
changes for ... (#NNNN)`). In both, the original PRs are the `(#NN)` at the end of body lines;
|
||||
a backport commit can also carry prerequisite PRs. Collect the PR set, falling back to the
|
||||
subject for commits whose body lists none:
|
||||
```
|
||||
for c in $(git rev-list <prev-pre-release>..<tag>); do
|
||||
prs=$(git log -1 --format=%b "$c" | grep -oE '\(#[0-9]+\)$')
|
||||
[ -n "$prs" ] || prs=$(git log -1 --format=%s "$c" | grep -oE '\(#[0-9]+\)$')
|
||||
echo "$prs"
|
||||
done | tr -dc '0-9\n' | sort -un
|
||||
```
|
||||
List the bullets under the **original** PRs (title and author of the PR merged to `main`), not
|
||||
the backport PRs. Build them with patch (a) or (b) above; for (b), fetch
|
||||
`gh api repos/GreptimeTeam/greptimedb/pulls/<n> --jq '{number,title,author:.user.login,merged_at}'`,
|
||||
group by the `cliff.toml` parsers (first match wins), order by `merged_at`, and put `!` titles
|
||||
under Breaking changes (§6).
|
||||
|
||||
A squashed pick PR can also contain release-only commits that have no PR. List them:
|
||||
```
|
||||
gh api repos/GreptimeTeam/greptimedb/pulls/<squash-pr>/commits --paginate \
|
||||
--jq '.[].commit.message | split("\n")[0]' | grep -vE '\(#[0-9]+\)$'
|
||||
```
|
||||
Mechanical ones (version bump, toolchain or lint adaptations; example:
|
||||
`fix: adapt release OptionMap iteration to Rust 1.98`) get no bullet. A release-only
|
||||
commit that changes behavior gets a bullet in its group.
|
||||
|
||||
> Flaky-network note for (a): `git cliff` fetches the repo's full commit history from the GitHub
|
||||
> API for author enrichment (paginates back thousands of commits, even for a tiny range). On a
|
||||
> flaky connection it can time out mid-pagination; it caches pages, so just **re-run until it
|
||||
> completes** — each retry resumes from the cache. If it keeps failing, fall back to (b).
|
||||
|
||||
### Groups (all paths)
|
||||
`git cliff` drops PRs whose title isn't a conventional commit (`filter_unconventional`). List
|
||||
them and add the user-facing ones by hand:
|
||||
```
|
||||
git log --format=%s <range> | grep -E '\(#[0-9]+\)$' | grep -vE '^[a-z]+(\([^)]*\))?!?: '
|
||||
```
|
||||
Use only the groups in `cliff.toml`. `git cliff` prints a `### Build` section for `build:` commits
|
||||
because no parser matches them; move a user-facing `build(deps)` fix to Bug Fixes and the rest to
|
||||
Miscellaneous Tasks. Keep each bullet list free of blank lines; a blank line turns it into a
|
||||
loose list that renders with different spacing.
|
||||
|
||||
## 4. Rebuild contributor sections after subtracting
|
||||
|
||||
`cliff` computes `New Contributors` / `All Contributors` over the full range, so after removing
|
||||
@@ -114,6 +194,21 @@ lines, recompute from the **remaining** commit bullets:
|
||||
in-range PR shipped in a patch).
|
||||
Do this in the same script that subtracts the PRs.
|
||||
|
||||
When the bullets are built by hand (§3 (b), pre-releases), `git cliff` computes no New
|
||||
Contributors, and GitHub's generated notes don't either (they only see the backport PRs). An
|
||||
author is new if none of their merged PRs shipped before, i.e. none appears in the history of the
|
||||
base tag:
|
||||
```
|
||||
git log <base-tag> --format=%B | grep -oE '\(#[0-9]+\)$' | tr -dc '0-9\n' | sort -u > /tmp/shipped.txt
|
||||
gh search prs --repo GreptimeTeam/greptimedb --author <user> --merged --limit 1000 \
|
||||
--json number --jq '.[].number' | sort -u > /tmp/author.txt
|
||||
comm -12 /tmp/author.txt /tmp/shipped.txt # empty => new contributor
|
||||
```
|
||||
Run it for every non-bot author in the release, and cite their earliest-merged PR in this
|
||||
release. "Earliest merged PR is in range" is not the same test: a contributor whose first PR
|
||||
merged on `main` but was not picked into any release is still new when a later PR of theirs
|
||||
ships.
|
||||
|
||||
## 5. Verify
|
||||
|
||||
Cross-check the result against `git log <base>..<tip>`: no subtracted PR remains, and no
|
||||
@@ -122,17 +217,71 @@ line (ask the user; some prefer to keep it).
|
||||
|
||||
## 6. Human-curated sections (insert after the `Release date:` line)
|
||||
|
||||
**Style.** Follow the Google developer documentation style guide: sentence-case headings, active voice, present
|
||||
tense for behavior, American spelling, serial comma, code font for identifiers, bold only for UI
|
||||
names. Keep it short:
|
||||
- Intro: two sentences at most, what the release covers and its main theme.
|
||||
- Highlight: one paragraph of up to four sentences, plus an example if it earns one (see below).
|
||||
- Upgrade note or breaking change: one bullet, one or two sentences: what changes, who is
|
||||
affected, what to do.
|
||||
- Every sentence states a fact the reader acts on or checks. Delete sentences that restate the
|
||||
heading or the previous sentence, transitions, and summaries.
|
||||
- No marketing words (`significantly`, `powerful`, `seamless`, `robust`, `enhanced`), no vague
|
||||
verbs (`supports`, `handles`, `improves`; say what happens), no `several`/`many` when the number
|
||||
is known. Keep the source's hedges; don't add new ones.
|
||||
|
||||
Example:
|
||||
> Bad: This release significantly enhances write reliability by introducing a robust retry
|
||||
> mechanism that seamlessly handles transient failures.
|
||||
>
|
||||
> Good: In distributed clusters, the frontend retries a failed write up to three times with
|
||||
> exponential backoff. Set `max_retries = 0` to disable retries.
|
||||
|
||||
Mirror past release bodies (`gh release view v1.0.0 --repo GreptimeTeam/greptimedb`):
|
||||
- **Short intro**, terse, engineer-written tone (no marketing adjectives).
|
||||
- **`### 👍 Highlights`** — *few, deep* highlights, each with a **working example**
|
||||
(SQL / TOML config). To get examples right, **read the highlight PR and the related docs
|
||||
PR/page** in the docs repo (`GreptimeTeam/docs`, often checked out locally) — verify syntax
|
||||
in `docs/reference/sql/*.md`, `config/*.example.toml`, `config/config.md`. Do **not** mention
|
||||
implementation details, tiny features, or unfinished/experimental-but-not-ready features.
|
||||
Always have the user review and edit the highlights; iterate.
|
||||
- **Short intro** (see Style).
|
||||
- **`### 👍 Highlights`** — *few, deep* highlights, ranked by user impact. Fixes for silently
|
||||
wrong query results, lost or invisible data, and security issues rank with new features and
|
||||
can be highlights.
|
||||
- Write a fix as its symptom and the affected deployments or usage, not the PR title. Example:
|
||||
"with no traffic, `errors / requests > 0.05` was true, so the alert fired".
|
||||
- State whether a feature is on by default. If an example config uses the default values, say
|
||||
so.
|
||||
- Give an example (SQL / TOML config) only where it helps readers act. Don't pick highlights,
|
||||
or the example, by what is easy to demonstrate.
|
||||
- One highlight covers one change or one user-visible problem. Don't merge unrelated PRs into
|
||||
one entry because they touch the same component.
|
||||
- Sources: the PR body's sections on impact, compatibility, and behavior changes, plus the
|
||||
code and config it touches, not the title alone. Verify examples against the related docs
|
||||
PR/page in the docs repo (`GreptimeTeam/docs`, often checked out locally),
|
||||
`docs/reference/sql/*.md`, `config/*.example.toml`, `config/config.md`.
|
||||
Do **not** mention implementation details, tiny features, or unfinished/experimental-but-not-ready
|
||||
features. Always have the user review and edit the highlights; iterate.
|
||||
- **Dashboard** subsection — don't just bump the version. Read the dashboard PRs in the bundled
|
||||
release (`gh release view <ver> --repo GreptimeTeam/dashboard`; then the linked PRs) and
|
||||
describe the user-facing change.
|
||||
describe the user-facing change. Use the names the UI shows, not the PR titles; check them in
|
||||
the dashboard's locale strings at the bundled tag:
|
||||
```
|
||||
gh api 'repos/GreptimeTeam/dashboard/contents/src/locale/en-US/menu.ts?ref=<dashboard-tag>' \
|
||||
--jq .content | base64 -d
|
||||
```
|
||||
Screenshots need absolute `https://` URLs: the body is copied to the GitHub release, where
|
||||
relative and site-root (`/img/...`) paths don't resolve.
|
||||
- **`### Upgrade notes`** and **`### Breaking changes`** — check every PR in the release for:
|
||||
`!` titles; `Compatibility`, `Behavior change`, `Breaking` paragraphs in the body (often a
|
||||
plain `Compatibility:` line, not a heading); unchecked "API changes are backward compatible" /
|
||||
"Schema or data changes are backward compatible" boxes; new config options and their defaults;
|
||||
changed defaults; persisted format changes; rolling-upgrade order; new hard limits. These
|
||||
signals set reading order, not a filter: a limit can sit in plain prose with every box
|
||||
checked, so read the body of every user-facing `feat`/`fix` PR. Examples of what is easy to
|
||||
miss: a 512 MiB cap on decompressed request bodies (#9264); a default step for stepless
|
||||
subqueries (#9481).
|
||||
- **Upgrade notes**: steps operators take — upgrade order, index rebuilds, settings to review.
|
||||
- **Breaking changes**: behavior that used to work and now errors or returns different
|
||||
results. Put the `!` PR bullets here too, after these entries (see `v1.3.0-beta.1`).
|
||||
- **Benchmark results** (optional) — state the baseline version (often the previous formal
|
||||
release, not the previous beta), data size, cache state, and hardware. Don't attribute results
|
||||
to single PRs. Leave out results the benchmark marks as not for publication, and smoke-level
|
||||
runs.
|
||||
|
||||
## 7. Output
|
||||
|
||||
@@ -144,7 +293,8 @@ it**. The release runbook deletes it after the release + docs PR are done.
|
||||
The release note is also published as a blog post in **`GreptimeTeam/docs`**.
|
||||
- **Ask the user for their local `GreptimeTeam/docs` checkout path**; if they have none, offer
|
||||
to clone (`git clone git@github.com:GreptimeTeam/docs.git <path>`).
|
||||
- File: `blog/release-X-Y-Z.md` (version with dashes; see `blog/release-1-0-0.md`).
|
||||
- File: `blog/release-X-Y-Z.md` (version with dashes; see `blog/release-1-0-0.md`). Pre-releases
|
||||
use dashes throughout: `blog/release-1-3-0-beta-2.md`.
|
||||
- Content = docs frontmatter (below) followed by the GitHub release body, **keeping the
|
||||
`# vX.Y.Z` H1 directly under the frontmatter**. The blog frontmatter has no `title:`, so
|
||||
Docusaurus uses that H1 as the page title and sidebar label — omitting it makes the page
|
||||
@@ -156,11 +306,13 @@ The release note is also published as a blog post in **`GreptimeTeam/docs`**.
|
||||
description: GreptimeDB vX.Y.Z Changelog
|
||||
date: YYYY-MM-DD
|
||||
---
|
||||
|
||||
# vX.Y.Z
|
||||
|
||||
Release date: ...
|
||||
```
|
||||
The docs CI check `check-release-note-sync` requires the post minus its frontmatter to equal
|
||||
the GitHub release body byte for byte, so the H1 goes on the line right after the closing
|
||||
`---`, with no blank line.
|
||||
- The docs working tree may have unrelated WIP — **don't disturb it**: use a `git worktree` off
|
||||
`origin/main`:
|
||||
```
|
||||
@@ -169,8 +321,19 @@ The release note is also published as a blog post in **`GreptimeTeam/docs`**.
|
||||
```
|
||||
- **Read and follow the current docs repo PR template**
|
||||
(`.github/pull_request_template.md`) from the checkout used for the release.
|
||||
Fill in every required section and leave reviewer-owned checklist boxes unchecked;
|
||||
do not rely on hard-coded section names from an older template.
|
||||
Fill in every required section; do not rely on hard-coded section names from an older
|
||||
template. The checklist items are the author's to confirm: check the ones you did, and for one
|
||||
that doesn't apply, check it and say why. For pre-releases, state the tagged SHA (§3) under
|
||||
Verification.
|
||||
- **Build the site before opening the PR.** A new worktree has no `node_modules`. Install with
|
||||
the pnpm version pinned in `package.json` (`packageManager`), as CI does:
|
||||
```
|
||||
cd /tmp/docs-release-note
|
||||
pm=$(jq -r .packageManager package.json) # e.g. pnpm@8.6.0
|
||||
npx -y "$pm" install --frozen-lockfile
|
||||
npx -y "$pm" build
|
||||
```
|
||||
If the build fails, fix it or stop; don't mark the PR ready for review.
|
||||
- Commit with sign-off, push, open a **draft** PR, then remove the worktree:
|
||||
```
|
||||
git -C /tmp/docs-release-note add blog/release-X-Y-Z.md
|
||||
|
||||
Reference in New Issue
Block a user