Release a new djoser version
Prepare and verify a release end-to-end. The user's git push origin X.Y.Z
is the only action that triggers anything external: the tag push starts the
Release workflow (validate → test matrix → build → PyPI → GitHub release).
Never push the tag yourself. Never run uv publish or gh release create
manually — CI does that.
Work through the stages in order. Do not skip a verification because it
"probably passes"; the whole point of this skill is that the release is
checked before the tag exists.
1. Establish scope
git fetch origin and make sure the local master matches
origin/master with a clean working tree.
- Find the last release:
LAST_TAG=$(git describe --tags --abbrev=0 origin/master).
- Review everything since then:
git log --oneline $LAST_TAG..origin/master
and the full git diff $LAST_TAG..origin/master. Read the diff, not just
the commit subjects — changelog entries must describe user-facing behavior,
and commit messages sometimes undersell or oversell what changed.
- Summarize the user-facing changes for the user and agree on the new
version number:
- patch = bug fixes, minor = backwards-compatible features,
major = breaking changes
- a PEP 440 pre-release suffix (
a1, b1, rc1) publishes to PyPI like
any other version, but pip/uv never resolve to it without --pre or an
exact pin; suggest one first when the diff is large or risky. Write the
changelog entry under the final X.Y.Z heading — CI falls back to it for
the pre-release, and the changelog carries no pre-release sections.
- If there is nothing user-facing to release, say so and stop.
2. Prepare the release branch
git checkout -b release/X.Y.Z origin/master
- Set the version in
pyproject.toml ([project] version), then run
uv lock — the lockfile records the project's own version and the CI
lock-file check fails if it is stale.
- Update
CHANGELOG.rst:
- new section at the top, matching the existing format exactly:
overline/underline dashes,
`X.Y.Z`_ (YYYY-MM-DD) heading, *
bullets with issue/PR links
- a
.. _X.Y.Z: https://github.com/sunscrapers/djoser/compare/PREV...X.Y.Z
link definition at the bottom
- every notable change found in stage 1 must be reflected; nothing in the
entry may be missing from the diff
- the CI extracts this section verbatim as the GitHub release notes, and
fails a stable release if the section is missing
docs/source/changelog.rst includes this file, so the entry is also
published on Read the Docs — there is no second place to edit, but the
entry must be valid RST (escape **kwargs -style markup) or the
docs build warns and renders it wrong
- Show the changelog entry to the user and get approval before continuing.
3. Verify locally
Run all of these; all must pass:
make test — full suite
make run-hooks — pre-commit checks (needs .venv/bin on PATH)
make build — translations compile and the package builds
uv lock --check — lockfile consistent with pyproject.toml
make docs — the docs build, including the rendered changelog entry;
it must not add new warnings
Report the results honestly; a failure here aborts the release until fixed.
4. Land the bump on master
- Commit
pyproject.toml, uv.lock and CHANGELOG.rst as
Bump version to X.Y.Z.
- Push the branch and open a PR against
master; wait for the user to
merge it (the repo squash-merges).
- After the merge:
git fetch origin and confirm origin/master now
carries the bump (uv version --short on that commit equals X.Y.Z).
5. Tag and hand over
- Tag the merged commit:
git tag X.Y.Z origin/master — tags are bare
X.Y.Z, no v prefix, matching every historical djoser tag (stable tags
must point at a commit on master — CI enforces this).
- Do NOT push the tag. Tell the user to run:
git push origin X.Y.Z
- Point them at the Actions tab to watch the
Release workflow. If it
fails, the fix path is: correct the problem, git push --delete origin X.Y.Z, delete the local tag, and re-run this skill's relevant stages.
1---2name: release3description: Release a new djoser version - review the diff since the last release, write the changelog entry, bump the version, verify everything locally, and prepare the tag for the user to push4---56# Release a new djoser version78Prepare and verify a release end-to-end. The user's `git push origin X.Y.Z`9is the only action that triggers anything external: the tag push starts the10`Release` workflow (validate → test matrix → build → PyPI → GitHub release).11Never push the tag yourself. Never run `uv publish` or `gh release create`12manually — CI does that.1314Work through the stages in order. Do not skip a verification because it15"probably passes"; the whole point of this skill is that the release is16checked before the tag exists.1718## 1. Establish scope19201. `git fetch origin` and make sure the local `master` matches21 `origin/master` with a clean working tree.222. Find the last release: `LAST_TAG=$(git describe --tags --abbrev=0 origin/master)`.233. Review everything since then: `git log --oneline $LAST_TAG..origin/master`24 and the full `git diff $LAST_TAG..origin/master`. Read the diff, not just25 the commit subjects — changelog entries must describe user-facing behavior,26 and commit messages sometimes undersell or oversell what changed.274. Summarize the user-facing changes for the user and agree on the new28 version number:29 - patch = bug fixes, minor = backwards-compatible features,30 major = breaking changes31 - a PEP 440 pre-release suffix (`a1`, `b1`, `rc1`) publishes to PyPI like32 any other version, but pip/uv never resolve to it without `--pre` or an33 exact pin; suggest one first when the diff is large or risky. Write the34 changelog entry under the final `X.Y.Z` heading — CI falls back to it for35 the pre-release, and the changelog carries no pre-release sections.365. If there is nothing user-facing to release, say so and stop.3738## 2. Prepare the release branch39401. `git checkout -b release/X.Y.Z origin/master`412. Set the version in `pyproject.toml` (`[project] version`), then run42 `uv lock` — the lockfile records the project's own version and the CI43 lock-file check fails if it is stale.443. Update `CHANGELOG.rst`:45 - new section at the top, matching the existing format exactly:46 overline/underline dashes, `` `X.Y.Z`_ (YYYY-MM-DD) `` heading, `*`47 bullets with issue/PR links48 - a `.. _X.Y.Z: https://github.com/sunscrapers/djoser/compare/PREV...X.Y.Z`49 link definition at the bottom50 - every notable change found in stage 1 must be reflected; nothing in the51 entry may be missing from the diff52 - the CI extracts this section verbatim as the GitHub release notes, and53 fails a stable release if the section is missing54 - `docs/source/changelog.rst` includes this file, so the entry is also55 published on Read the Docs — there is no second place to edit, but the56 entry must be valid RST (escape `` ``**kwargs`` ``-style markup) or the57 docs build warns and renders it wrong584. Show the changelog entry to the user and get approval before continuing.5960## 3. Verify locally6162Run all of these; all must pass:63641. `make test` — full suite652. `make run-hooks` — pre-commit checks (needs `.venv/bin` on `PATH`)663. `make build` — translations compile and the package builds674. `uv lock --check` — lockfile consistent with `pyproject.toml`685. `make docs` — the docs build, including the rendered changelog entry;69 it must not add new warnings7071Report the results honestly; a failure here aborts the release until fixed.7273## 4. Land the bump on master74751. Commit `pyproject.toml`, `uv.lock` and `CHANGELOG.rst` as76 `Bump version to X.Y.Z`.772. Push the branch and open a PR against `master`; wait for the user to78 merge it (the repo squash-merges).793. After the merge: `git fetch origin` and confirm `origin/master` now80 carries the bump (`uv version --short` on that commit equals `X.Y.Z`).8182## 5. Tag and hand over83841. Tag the merged commit: `git tag X.Y.Z origin/master` — tags are bare85 `X.Y.Z`, no `v` prefix, matching every historical djoser tag (stable tags86 must point at a commit on `master` — CI enforces this).872. Do NOT push the tag. Tell the user to run:88 ```bash89 git push origin X.Y.Z90 ```913. Point them at the Actions tab to watch the `Release` workflow. If it92 fails, the fix path is: correct the problem, `git push --delete origin93 X.Y.Z`, delete the local tag, and re-run this skill's relevant stages.