diff options
| author | hathach <[email protected]> | 2026-06-30 16:07:21 +0700 |
|---|---|---|
| committer | hathach <[email protected]> | 2026-06-30 16:07:21 +0700 |
| commit | 47ce85fc1916ea08bee3c70c261fd8674604f169 (patch) | |
| tree | 1822de246f30fad267343a58831d71e4413fcb96 | |
| parent | 20200b35202e5dd0a1a5e8be1db081dc57f6ae8e (diff) | |
make-release skill: tighten to commands + essential judgment
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
| -rw-r--r-- | .claude/skills/make-release/SKILL.md | 75 |
1 files changed, 31 insertions, 44 deletions
diff --git a/.claude/skills/make-release/SKILL.md b/.claude/skills/make-release/SKILL.md index 058d7245d..0ef5e8431 100644 --- a/.claude/skills/make-release/SKILL.md +++ b/.claude/skills/make-release/SKILL.md @@ -1,84 +1,71 @@ --- name: make-release -description: Use when cutting a new TinyUSB release — bumping the version, running tools/make_release.py, writing the per-release docs/changelog/ entry from the PRs merged since the last tag, and validating before the maintainer commits and tags. Covers the changelog PR-boundary reconciliation and the regenerated-file gotchas. +description: Use when cutting a new TinyUSB release — version bump, regenerated files, the per-release changelog, and validation before the maintainer commits and tags. --- # Cut a TinyUSB Release -**Do NOT commit or tag** during prep — leave every change unstaged for maintainer review; the maintainer finalizes (commit, tag, release) in step 4. Work in a worktree. Agree the target version `X.Y.Z` with the maintainer before starting (it is their call, not derivable from the diff). +**Don't commit or tag during prep** — leave changes unstaged for the maintainer (step 4). Work in a worktree. Agree the version `X.Y.Z` with the maintainer first (their call, not derivable from the diff). -## 1. Bump version + regenerate files +## 1. Bump + regenerate ```bash -# edit tools/make_release.py: version = 'X.Y.Z' -python3 tools/make_release.py # MUST run from repo root (uses relative paths) +# set version = 'X.Y.Z' in tools/make_release.py, then FROM REPO ROOT: +python3 tools/make_release.py ``` +Refreshes `tusb_option.h`, `repository.yml`, `library.json`, `sonar-project.properties`, and (via gen_doc/gen_presets) `docs/reference/{boards,dependencies}.rst` + preset JSONs (presets/docs change only if boards/deps changed). -Refreshes `src/tusb_option.h`, `repository.yml`, `library.json`, `sonar-project.properties`, and (via `gen_doc`/`gen_presets`) `docs/reference/{boards,dependencies}.rst`, `hw/bsp/BoardPresets.json`, per-example `CMakePresets.json`. The presets/docs only change if boards/deps changed since the last release. - -Gotchas: -- `gen_doc` imports `pandas` + `tabulate` at line 3 and crashes before writing if they're missing (not in `docs/requirements.txt`) → `pip install pandas tabulate`. See the **build-doc** skill. -- `boards.rst` is written with no trailing newline → pre-commit's `end-of-file-fixer` adds it; run pre-commit on the regenerated files (step 3). +Gotchas: `gen_doc` needs `pandas`+`tabulate` (not in requirements) → `pip install pandas tabulate`; `boards.rst` lands with no trailing newline → let pre-commit fix it (step 3). ## 2. Changelog — `docs/changelog/` (the hard part) -Each release is its own file: create `docs/changelog/X.Y.Z.rst` and add it as the **first** entry in the `docs/changelog/index.rst` toctree (newest first). Determine the PR set by **git commit reachability, not by merge date** — a date query (`gh ... --search merged:>=DATE`) both includes the *previous* release's own changelog PR (its `mergedAt` shares the tag day) and is awkward at the boundary; `--merges | grep "Merge pull request"` silently drops squash-merged PRs. +New file `docs/changelog/X.Y.Z.rst`, listed **first** in `docs/changelog/index.rst`. Get the PR set by **commit reachability, not merge date** (a date query wrongly pulls in the prior release's changelog PR at the boundary): ```bash -PREV=0.20.0 # last release tag -git merge-base --is-ancestor $PREV HEAD && echo OK # else range math invalid — stop +PREV=0.20.0 +git merge-base --is-ancestor $PREV HEAD && echo OK # else stop: range invalid git log --first-parent $PREV..HEAD --pretty=%s > /tmp/fp.txt -{ sed -nE 's/^Merge pull request #([0-9]+).*/\1/p' /tmp/fp.txt - sed -nE 's/.*\(#([0-9]+)\)$/\1/p' /tmp/fp.txt ; } | sort -un > /tmp/prs.txt -# guard: any mainline commit with NO PR ref (direct push / rebase-merge). Must be empty; investigate if not: -grep -vE '^Merge pull request #[0-9]+|\(#[0-9]+\)$' /tmp/fp.txt -# titles + labels to categorize (parallelize — 200+ sequential gh calls take minutes): +{ sed -nE 's/^Merge pull request #([0-9]+).*/\1/p' /tmp/fp.txt # merge-button + sed -nE 's/.*\(#([0-9]+)\)$/\1/p' /tmp/fp.txt ; } | sort -un > /tmp/prs.txt # squash +grep -vE '^Merge pull request #[0-9]+|\(#[0-9]+\)$' /tmp/fp.txt # guard: must be empty (direct pushes) xargs -P8 -I{} gh pr view {} --json number,title,labels \ - --jq '"#\(.number)\t\(.title)\t[\(.labels|map(.name)|join(","))]"' < /tmp/prs.txt + --jq '"#\(.number)\t\(.title)\t[\(.labels|map(.name)|join(","))]"' < /tmp/prs.txt # categorize ``` +`--first-parent` skips dev-merges; the two `sed`s catch merge-button + squash. A PR merged into a *feature branch* folds into its parent (won't appear alone) — reflect its final state in the parent's bullet. -Why this is correct: `$PREV..HEAD` excludes everything reachable from the last tag (nothing already shipped leaks in — including the prior release's own changelog PR, which the tag sits on); `--first-parent` skips `Merge branch … into <feature>` dev-merges that aren't PRs; the two `sed` patterns catch both merge-button (`Merge pull request #N` — the ` from <branch>` suffix is sometimes edited out) and squash (`… (#N)`) merges. The anchored patterns avoid grabbing stray `#numbers` from prose/issue refs. - -A PR merged into a *feature branch* (rather than master) is folded into its parent top-level PR and won't appear on its own — intended (review follow-ups aren't double-listed), but make the parent's changelog bullet reflect the final merged state. This is also why a date-based `gh` query returns *more* numbers than this set: those extras are the prior changelog PR plus these sub-PRs. - -Then **curate** into the existing sections, matching the prior release file's RST style exactly: -- The file's title is the version (`X.Y.Z` with `======` underline matching its length), then the italic date (the planned tag date — ask if unknown). Add the file to the top of `docs/changelog/index.rst`. -- Sections in order: **General** (New MCUs and Boards / Code Quality and Build / Documentation), **API Changes**, **Device Stack** (per class: Audio, CDC, HID, MIDI, MSC, MTP, Net, Video, …), **Host Stack**, **Controller Driver (DCD & HCD)** (per driver: DWC2, FSDEV, MUSB, RP2040, …), **Testing**, **Contributors**. Within a section, each driver/class group is a ``^^^``-underlined sub-heading (not a bullet). -- RST inline code (double backticks) for symbols. Group related PRs into one bullet — summarize, don't dump 200 lines. Driver/`Port *` PR labels help bucket DCD/HCD entries. -- **Contributors** section credits the release's PR authors (this is where contributor credit lives — there is no separate contributors page). List the unique non-bot author handles alphabetically: - +**Curate** into the prior file's exact RST style: +- Title = version (`======` underline), then italic date (ask if unknown). Add to top of `index.rst`. +- Section order: **General** (New MCUs and Boards / Code Quality and Build / Documentation) → **API Changes** → **Device Stack** (per class) → **Host Stack** → **Controller Driver (DCD & HCD)** (per driver) → **Testing** → **Contributors**. Each class/driver group is a ``^^^`` sub-heading, not a bullet. +- Double-backticks for symbols; group related PRs into one bullet (don't dump). `Port *`/driver labels help bucket DCD/HCD. +- **Contributors**: unique non-bot PR authors, alphabetical (the only contributor credit — no separate page): ```bash xargs -P8 -I{} gh pr view {} --json author --jq '.author.login' < /tmp/prs.txt \ - | grep -viE '\[bot\]$|^(copilot|claude|dependabot|github-actions)$' | sort -uf | sed 's/^/@/' | paste -sd, - | sed 's/,/, /g' # then drop any CI/service accounts + | grep -viE '\[bot\]$|^(copilot|claude|dependabot|github-actions)$' | sort -uf | sed 's/^/@/' | paste -sd, - | sed 's/,/, /g' # drop any CI/service accounts ``` -## 3. Validate (all unstaged) +## 3. Validate (leave unstaged) ```bash pre-commit run --files docs/changelog/X.Y.Z.rst docs/changelog/index.rst \ docs/reference/boards.rst docs/reference/dependencies.rst \ library.json repository.yml sonar-project.properties src/tusb_option.h tools/make_release.py -python3 tools/build_doc.py -c # docs build clean; new release page wired into the toctree (see build-doc skill) -( cd test/unit-test && ceedling test:all ) # expect all pass +python3 tools/build_doc.py -c # docs build clean (see build-doc skill) +( cd test/unit-test && ceedling test:all ) ( cd examples/device/cdc_msc && rm -rf build && mkdir build && cd build && \ cmake -DBOARD=stm32f407disco -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel .. && cmake --build . ) -git diff --stat -- ':!.idea' # review; `.idea/*` is IDE noise, exclude it +git diff --stat -- ':!.idea' # .idea/* is IDE noise ``` - -Confirm version is consistent across `tusb_option.h` / `library.json` / `repository.yml` / `sonar-project.properties`, then leave everything unstaged for the maintainer. +Confirm the version matches across `tusb_option.h` / `library.json` / `repository.yml` / `sonar-project.properties`. ## 4. Finalize (maintainer) -After reviewing the unstaged diff: - ```bash -git add -A -- ':!.idea' && git commit -m "Bump version to X.Y.Z" # match the reviewed diff; stages the new changelog file -git tag -a X.Y.Z -m "Release X.Y.Z" # tags are unprefixed (e.g. 0.20.0, not v0.20.0) +git add -A -- ':!.idea' && git commit -m "Bump version to X.Y.Z" +git tag -a X.Y.Z -m "Release X.Y.Z" # tags are unprefixed (0.20.0, not v0.20.0) git push origin <branch> X.Y.Z ``` +Then create the GitHub release from the tag. -Then create a GitHub release from the `X.Y.Z` tag. - -## 5. Code size vs the previous release (CI-automatic) +## 5. Code size (automatic) -On the GitHub **release** event, `build.yml`'s `code-metrics` job builds the matrix with `--target tinyusb_metrics`, combines per-board `metrics.json`, downloads the **previous** tag's `metrics.json` (`gh release download $PREV -p metrics.json`), runs `metrics.py compare`, and uploads both `metrics.json` and `metrics_compare_<curr>-<prev>.md` to the release. No manual step — **provided the previous release has a `metrics.json` asset**. After releasing, confirm those two assets appeared. +On the release event, CI's `code-metrics` job diffs against the previous tag's `metrics.json` and uploads `metrics.json` + a compare to the release — **only if the previous release has a `metrics.json` asset**. Confirm both appeared. |
