release
Prepare a psd-tools release: update changelog, open release PR. Use when the user wants to cut a new version.
Informations de source
- Dépôt
- psd-tools/psd-tools
- Dernière activité de la source
- 25 septembre 2026 à 02:45
- Langue détectée de SKILL.md
- anglais
- Étoiles
- 1 482
- Forks
- 226
Options d'installation
Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.
Vérifiez les fichiers source
Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.
Affichage de SKILL.md
SKILL.md
Instructions source · Aperçu en lecture seule- name
- release
- description
- Prepare a psd-tools release: update changelog, open release PR. Use when the user wants to cut a new version.
## Step 0 — Determine target version
**Provided version**: $ARGUMENTS
### If a version was provided (above is non-empty)
Validate it as a PEP 440 string (e.g. `1.2.3`, `1.2.3a1`, `1.2.3rc1`, `1.2.3.post1`).
Note: PEP 440 versions must **not** start with `v` — that prefix belongs on the git tag, not
the version string.
Stop and ask the user to correct it if invalid. Store it as **VERSION** for all subsequent steps.
### If no version was provided (above is empty)
Analyze the commits listed in Step 1 to recommend the correct next version.
The last tag shown in Step 1 uses a `v` prefix (e.g. `v1.14.3`); strip it when computing
the next version so the result is a bare PEP 440 string (e.g. `1.14.4`, not `v1.14.4`).
Apply the project's rule, which is not plain semver:
- **Minor bump** (`X.Y+1.0`) — any new public feature or API addition, **or** any change
that breaks a public API or documented behaviour. Pre-2.0 a minor bump may break; what
the project asks of such a change is not a major bump but that the entry says so
explicitly, which the **Changelog** section of `CLAUDE.md` already requires.
- **Patch bump** (`X.Y.Z+1`) — bug fixes, security patches, chores, docs or refactoring
only, with nothing backwards-incompatible.
Never infer a **major bump** from the presence of breaking changes. `2.0.0` is a
deliberate decision by the maintainer about the project's direction; propose it only if
they have already said they want one.
Show your reasoning and proposed version to the user, then ask them to confirm or override it.
Store the confirmed version as **VERSION** for all subsequent steps.
## Step 1 — Review commits since the last release
Fetch tags:
!`git fetch --tags -q || echo "Warning: failed to fetch tags — check network/auth and consider retrying."`
Last tag: !`git describe --tags --abbrev=0 2>/dev/null || echo "(none)"`
Today's date: !`date +%Y-%m-%d`
Now list the commits since the last release by running one of these commands:
- If "Last tag" above is `(none)`: run `git log --oneline`
- Otherwise: run `git log <LAST_TAG>..HEAD --oneline` (substituting the actual tag)
You must run this command and review the output before proceeding to Step 0 or Step 2.
## Step 1b — Prose sweep
Rationale docstrings and comments accumulate across a release: each fix PR appends a
paragraph explaining what it measured, and none compresses what is already there. Trim
them now, while the release is the unit of review — nothing enforces this mechanically
(ruff's docstring rules are off, `ruff format` does not reflow comments or docstrings,
and no line-length rule is selected), so this step is the only thing that catches it.
Measure rather than eyeball — the worst ratios sit on the smallest helpers, which read
as fine in a diff:
```bash
uv run python tools/prose_density.py
```
Anything above roughly 3x prose-to-code deserves a look. Keep what cost real
measurement, and what stops a future reader undoing a fix: why a bound is shaped the
way it is, why a guard cannot be tighter, what an experiment ruled out. Cut history the
changelog already holds, corpus statistics the tests already assert, and the same
explanation repeated at three layers.
Trimming is a separate commit from the release commit, and touches comments and
docstrings only. If any line of code moves, stop and treat it as a code change.
It is still a commit, so it needs the release branch to exist: create it as Step 4
describes, rather than landing this on `main`. It is the same branch, created once —
Step 4 does not create a second one.
## Step 1c — Reconcile the release milestone
Release scope is also tracked in a milestone, titled bare without the `v` prefix
(`1.20.0`, not `v1.20.0`). It is maintained by hand during triage, so it and the changelog
can disagree about the same release. Settle that before Step 2 drafts the changelog, since
the answer changes what gets written.
The two drift in both directions, and one query cannot see both.
**The milestone over-promises** — it holds an item whose fix never landed:
```bash
gh issue list --milestone "VERSION" --state all --limit 100 \
--json number,title,state,closedAt
```
An empty result is fine; a release cut outside the milestone scheme is not an error.
**The milestone under-reports** — an item was fixed but is milestoned for a *later*
release, so it stands closed against a release it did not ship in. It is not in
**VERSION**, so the query above cannot show it. List what closed since the last tag
instead, substituting that tag's commit timestamp, which
`git log -1 --format=%cI <LAST_TAG>` prints in full:
```bash
gh issue list --state closed --limit 200 \
--search "closed:>=YYYY-MM-DDTHH:MM:SS+HH:MM" \
--json number,title,closedAt,milestone
```
Pass the whole timestamp, not just the date. A release tagged partway through a day
leaves issues closed before it on the same date, and those belong to the release that
just shipped; a date-only bound sweeps them into this one.
Of those, only the ones carrying a different version are drift. An item with **no**
milestone is not — the milestone records planned scope, not everything that shipped — so
give its count in one line and leave it alone unless the user asks.
Put both lists to the user and ask; do not change anything first. Whether an open item
blocks the release is the call a human is here to make.
- **Open items in VERSION**: move to the next milestone, or hold the release for them.
Closing a milestone does not close its issues, so one left open stands attached to a
release it did not ship in.
- **Closed items milestoned later**: move into **VERSION**, or leave them.
Once the user has decided, apply it with `gh issue edit <N> --milestone "<title>"`.
Do not close the milestone here. `auto-tag.yml` closes it after the merge (see Step 6).
## Step 2 — Draft changelog entry
Read `docs/changelog.rst` to understand the current format, then draft a new entry for **VERSION**
using this RST format:
```
VERSION (YYYY-MM-DD)
--------------------
- [category] Description (#PR)
```
**Important**: The `-` underline must be at least as long as the title line (RST requirement).
Count the exact characters in `VERSION (YYYY-MM-DD)` and use that many dashes.
The categories, and the rule for picking between them, are documented in the
**Changelog** section of the repo-root `CLAUDE.md`, which loads as project
instructions — use that list. It is deliberately not restated here: the second copy
is what drifted (#791).
Group related changes. Omit purely internal churn that users won't care about. Reference PR numbers where available.
Show the draft to the user and ask for approval or edits before continuing.
## Step 3 — Update docs/changelog.rst
Prepend the approved changelog entry directly after the `Changelog` header block and the
following blank line, leaving a blank line between the header and the new entry.
## Step 4 — Create release branch and commit
The branch name is the only thing `auto-tag` reads the version from, and it matches
`^release/(vX.Y.Z...)$` anchored at both ends, so the name has to be exactly
`release/vVERSION`. A prefixed variant is not a cosmetic difference: the extract step
yields an empty version, the `tag` job is skipped on `version != ''`, and the run
still reports success — so nothing is tagged and nothing says so.
Create it once, by whichever of these two routes applies — Step 1b's trimming commit
needs the same branch, so it may already exist.
If the project's instructions ask for a worktree, create the worktree and rename its
branch from inside it. `EnterWorktree` names the branch after the worktree rather than
the name it was given, prefixing `worktree-` and replacing slashes: asking for
`release/v1.20.0` produced `worktree-release+v1.20.0`.
```bash
git branch -m release/vVERSION
```
Otherwise create the branch directly:
```bash
git checkout -b release/vVERSION
```
Either way, confirm it before going on — `git branch --show-current` has to print
`release/vVERSION` exactly.
Then update `src/psd_tools/version.py` using the Edit tool — replace the existing
`__version__` line with:
```python
__version__ = "VERSION"
```
`VERSION` is the bare PEP 440 string **without** the `v` prefix (e.g. `1.15.0`, not `v1.15.0`).
Then stage both changed files and commit:
```bash
git add docs/changelog.rst src/psd_tools/version.py
git commit -m "docs: release vVERSION"
git push -u origin release/vVERSION
```
Replace `VERSION` with the actual version string (e.g. `1.15.0`).
## Step 5 — Open a pull request
Run `gh pr create` with `--title "Release vVERSION"` and a `--body` containing:
- A `## Release vVERSION` heading
- A `### Changelog` section with the approved entry from Step 2 pasted in
- A `### Release checklist` section with these items:
- `[ ] Changelog entry reviewed and accurate`
- `[ ] Version follows PEP 440`
- `[ ] Prose sweep done (Step 1b), or explicitly skipped`
- `[ ] Milestone reconciled both ways (Step 1c)`
- A closing note: "After this PR is merged, the `auto-tag` workflow will tag the merge commit
as `vVERSION` and the `release` workflow will build wheels and publish to PyPI automatically."
Replace `VERSION` with the actual version string throughout.
## Step 6 — Done
Print the PR URL. Remind the user:
> After the PR is approved and merged, the `auto-tag` GitHub Actions workflow tags the merge
> commit as `vVERSION` automatically. That tag push triggers the `release` workflow to build
> wheels for all platforms and publish to PyPI. No manual tagging or publishing is needed.
> The same workflow closes the `VERSION` milestone if one is open; that step is bookkeeping
> and is allowed to fail without affecting the release.
Replace `VERSION` with the actual version string.
Voir sur GitHub