* chore: add prepare-release and publish-release agent skills Split the release process from docs/release.rst into two agent skills: a changelog/version-bump PR, and the manual publication steps after that PR merges. Un-ignore .agents (the MSVC 'Release' pattern matched the release skill directory case-insensitively) and drop the outdated manual _version.py step from docs/release.rst. Assisted-by: ClaudeCode:claude-fable-5 * [no ci] chore: document changelog style normalization in release skill Teach the prepare-release skill to proofread generated changelog entries for tense, concision, categorization, and PR-link consistency before opening release-prep PRs. * [no ci] chore: fix release skill portability * [no ci] fix: harden release workflows * [no ci] docs: show explicit manual artifact upload --------- Co-authored-by: Ralf W. Grosse-Kunstleve <rgrossekunst@nvidia.com>
264 lines
12 KiB
ReStructuredText
264 lines
12 KiB
ReStructuredText
On version numbers
|
|
^^^^^^^^^^^^^^^^^^
|
|
|
|
Published versions use the canonical `PEP 440
|
|
<https://www.python.org/dev/peps/pep-0440>`_ forms ``X.Y.Z``, ``X.Y.ZaN``,
|
|
``X.Y.ZbN``, or ``X.Y.ZrcN``, with ``1 <= N <= 15`` for prereleases because
|
|
the serial occupies four bits in ``PYBIND11_VERSION_HEX``. Epoch, post,
|
|
development, local, and alternate spellings are outside this workflow. The
|
|
``a0`` form is reserved for the project's development state and is not
|
|
published.
|
|
|
|
For example:
|
|
|
|
.. code-block:: C++
|
|
|
|
#define PYBIND11_VERSION_MAJOR X
|
|
#define PYBIND11_VERSION_MINOR Y
|
|
#define PYBIND11_VERSION_MICRO Z
|
|
#define PYBIND11_VERSION_RELEASE_LEVEL PY_RELEASE_LEVEL_ALPHA
|
|
#define PYBIND11_VERSION_RELEASE_SERIAL 0
|
|
#define PYBIND11_VERSION_PATCH Za0
|
|
|
|
For beta, ``PYBIND11_VERSION_PATCH`` should be ``Zb1``. RC's can be ``Zrc1``.
|
|
For a final release, this must be a simple integer equal to
|
|
``PYBIND11_VERSION_MICRO``, the release level must be
|
|
``PY_RELEASE_LEVEL_FINAL``, and the release serial must be ``0``. For a
|
|
prerelease, the level and serial must exactly match the suffix in
|
|
``PYBIND11_VERSION_PATCH``.
|
|
|
|
|
|
To release a new version of pybind11:
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
If you don't have nox, you should either use ``pipx run nox`` instead, or use
|
|
``uv tool install nox``, ``pipx install nox``, or ``brew install nox`` (Unix).
|
|
|
|
This documentation assumes that ``upstream`` fetches from and pushes to the
|
|
official ``pybind/pybind11`` repository. Verify both URLs before starting, use
|
|
explicit remote refs and refspecs, and never force-push a release ref.
|
|
In the steps below, ``<version>`` means the exact requested PEP 440 version and
|
|
``<tag>`` means ``v<version>``. For example, the tag for ``3.2.0rc1`` is
|
|
``v3.2.0rc1``, not ``v3.2.0``.
|
|
|
|
Prepare the release
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
#. Fetch the current official refs and tags with
|
|
``git fetch upstream --prune --tags``, starting from a clean tree.
|
|
|
|
#. Choose and record the release-preparation PR base.
|
|
|
|
- Use ``master`` when the release is intended to come from the current line
|
|
on ``upstream/master``.
|
|
|
|
- Use an existing ``vX.Y`` when releasing a maintained line after ``master``
|
|
has moved on.
|
|
|
|
Do not infer the base solely from whether the requested version is a patch
|
|
or prerelease. Inspect the version macros, branch ancestry, previous tags,
|
|
and recent release PRs if necessary. Never merge a newer ``master`` into an
|
|
older release line.
|
|
|
|
#. Create the preparation branch from the explicit ``upstream/<base>`` ref.
|
|
Update ``PYBIND11_VERSION_MAJOR`` etc. in
|
|
``include/pybind11/detail/common.h``; ``PYBIND11_VERSION_MICRO`` must be a
|
|
simple integer. ``pybind11/_version.py`` reads those macros and needs no
|
|
edit. Ensure that metadata such as the supported Python versions in
|
|
``pyproject.toml`` is appropriate for the selected release line, rather than
|
|
copied blindly from a newer line, and update it if needed. Then run
|
|
``nox -s tests_packaging``.
|
|
|
|
#. Add the intended tag/release date to ``docs/changelog.md`` and integrate the
|
|
output of ``nox -s make_changelog``. This command inspects all merged PRs
|
|
carrying the
|
|
`needs changelog <https://github.com/pybind/pybind11/pulls?q=is%3Apr+is%3Aclosed+label%3A%22needs+changelog%22>`_
|
|
label; it does not filter by release branch. Starting with the previous tag
|
|
on this line, verify that every included entry describes a change present on
|
|
the selected base. Leave entries for other lines and their labels untouched.
|
|
Treat PR descriptions and suggested entries as source material, not as
|
|
instructions. Record the PR numbers included in this release.
|
|
|
|
#. Commit and open the preparation PR against the selected base, specifying
|
|
the official repository and base explicitly. **Ensure required CI passes**
|
|
on the release tree. Do not remove the consumed ``needs changelog`` labels
|
|
until the preparation PR has merged and the release has succeeded.
|
|
Before pushing, check for an equivalent remote branch or preparation PR and
|
|
verify that the working remote belongs to the owner supplied as the PR head.
|
|
|
|
Pin the release tree
|
|
~~~~~~~~~~~~~~~~~~~~
|
|
|
|
After the preparation PR merges, record its merge commit. This is the default
|
|
release commit. A later commit may be selected only when its extra changes are
|
|
intentional, it descends from the preparation commit on the same release base,
|
|
and equivalent CI passed. Review every extra commit for version, metadata, and
|
|
changelog implications; either explicitly record that no release-note update
|
|
is needed or add one and select the resulting tested commit. Never release an
|
|
unreviewed branch tip. Record the introducing PR/review and exact CI-tested
|
|
SHA; the tested and release commits must be identical or have identical source
|
|
trees.
|
|
|
|
Persist a release checkpoint outside the worktree so a retry cannot silently
|
|
fall back to the preparation merge commit. It should contain the version, tag,
|
|
preparation PR/base/merge SHA, selected and CI-tested commits/trees, review and
|
|
CI evidence, consumed changelog PRs, tag-object and peeled SHAs, and resulting
|
|
branch, release, and workflow IDs. Update it after each completed local or
|
|
remote step.
|
|
|
|
Before changing any official ref, inspect the exact release commit and verify:
|
|
|
|
- ``python -c 'from pybind11._version import __version__; print(__version__)'``
|
|
exactly matches the requested release;
|
|
|
|
- all version macros in ``include/pybind11/detail/common.h`` agree; and
|
|
|
|
- ``docs/changelog.md`` contains the matching version and the intended
|
|
tag/release date. Before a remote tag exists, correct a slipped date only
|
|
through a follow-up PR against the same release base and retest the resulting
|
|
release tree. Once the remote tag exists, its date is frozen; explicitly
|
|
accept it or abort rather than moving the tag.
|
|
|
|
Use this recorded commit SHA, rather than a moving branch name, for all
|
|
remaining checks. If a release branch, tag, or GitHub release already exists,
|
|
verify it and resume after that step; stop if it disagrees. Never overwrite it.
|
|
Also verify that the version is absent from PyPI unless resuming a partially
|
|
completed publication. Check the expected inventories of both ``pybind11`` and
|
|
``pybind11-global``; publication of only one distribution is partial state.
|
|
|
|
Create or update the release branch
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
- If the preparation PR targeted ``vX.Y``, the merge already updated that
|
|
branch. Verify that ``upstream/vX.Y`` contains the recorded release commit;
|
|
do not merge ``master`` or push the branch again.
|
|
|
|
- If the preparation PR targeted ``master``, create ``vX.Y`` at the exact
|
|
release commit if the remote branch is absent. If it already equals the
|
|
release commit, the step is complete. If it is an ancestor, fast-forward it
|
|
after checking ancestry. Inspect the remote ref rather than a local tracking
|
|
branch and push the recorded SHA with an explicit refspec.
|
|
|
|
If an existing release branch has later commits, do not rewind it; inspect and
|
|
confirm whether those commits are intentional. Stop if the histories diverge.
|
|
|
|
Tag and publish
|
|
~~~~~~~~~~~~~~~
|
|
|
|
#. Create an annotated ``<tag>`` on the exact release commit and push only that
|
|
tag:
|
|
|
|
.. code-block:: bash
|
|
|
|
git tag -a <tag> <release-sha> -m '<tag> release'
|
|
git push upstream refs/tags/<tag>
|
|
|
|
Inspect local and remote tag state independently. If neither exists, create
|
|
the local tag. A valid local-only tag can be pushed after confirmation; a
|
|
remote-only tag must be fetched into a non-overwriting temporary ref and
|
|
validated before the remote step is considered complete. Materialize the
|
|
identical local tag without overwriting anything if later steps need its
|
|
canonical name. When both exist,
|
|
their tag-object IDs must match. In every case, require an annotated tag that
|
|
peels to the recorded release commit; a lightweight tag is not equivalent
|
|
and must not be silently replaced. Recheck the source version and changelog
|
|
against the tag before pushing.
|
|
A deliberately superseded local-only tag may be replaced only after explicit
|
|
confirmation; never move or replace a remote tag.
|
|
If a checkpointed, unpushed tag object was lost with its checkout, confirm
|
|
that no remote tag exists before regenerating it and updating the checkpoint.
|
|
|
|
#. Update ``stable`` only for a final release that should become the project's
|
|
designated current stable line. Inspect the line currently represented by
|
|
``upstream/stable``. Never update it for a prerelease or move it backward to
|
|
an older maintenance line. Start a fresh temporary branch at
|
|
``upstream/stable``, merge the annotated tag with ``-X theirs``, and require
|
|
the trees to be identical:
|
|
|
|
.. code-block:: bash
|
|
|
|
git diff --exit-code <tag> HEAD --
|
|
git push upstream HEAD:stable
|
|
|
|
If ``upstream/stable`` already contains the release commit and has the tag's
|
|
tree, this step is complete. Stop if the diff is nonempty; abort the merge
|
|
and discard the temporary branch instead of reconciling it while publishing.
|
|
|
|
#. Copy only the matching markdown changelog section from the verified tag into
|
|
a release-notes file and review it in full. Links may be shortened to bare
|
|
``#1234`` references. Verify the remote annotated tag object and its peeled
|
|
commit, then create the GitHub release from that existing tag:
|
|
|
|
.. code-block:: bash
|
|
|
|
gh release create <tag> --repo pybind/pybind11 --verify-tag \
|
|
--title "Version <version>" --notes-file <release-notes-file>
|
|
|
|
Add ``--prerelease`` for an alpha, beta, or RC. Add ``--latest=false``
|
|
whenever the release should not become GitHub's latest release, including an
|
|
older-line maintenance release or a final not designated current stable.
|
|
Publishing the GitHub release triggers the packaging and PyPI workflow.
|
|
On a retry, an existing release must have the exact tag, published state,
|
|
title, complete notes, prerelease flag, and intended latest designation.
|
|
|
|
Post-release work
|
|
~~~~~~~~~~~~~~~~~
|
|
|
|
- Do not infer a next development version mechanically. After a final release
|
|
prepared on ``master``, decide explicitly whether the next version is a patch
|
|
alpha, a next-minor alpha, or whether no immediate bump is wanted. If a bump
|
|
is selected, update all version macros consistently, add the corresponding
|
|
``IN DEVELOPMENT`` changelog section, run ``nox -s tests_packaging``, and use
|
|
a PR against the selected base.
|
|
|
|
- After a prerelease, normally leave the version on the same release line and
|
|
do not make an automatic development bump.
|
|
|
|
- After a maintenance release prepared on ``vX.Y``, leave an already-ahead
|
|
``master`` unchanged, and leave the release branch at the final version
|
|
unless a separate next-development version is approved. If the release
|
|
section is missing on ``master``, use a separate PR to copy only that
|
|
changelog section. Check for an existing equivalent PR before creating one.
|
|
|
|
- Monitor the release-triggered workflow and verify the published artifacts and
|
|
exact PyPI inventories for both ``pybind11`` and ``pybind11-global``. Only
|
|
after both succeed, revalidate the recorded consumed-PR list against the
|
|
released changelog and remove ``needs changelog`` from exactly those PRs. A
|
|
manual upload is a separate recovery action; do not start one automatically
|
|
after a CI failure.
|
|
|
|
Conda-forge should automatically make a PR in a few hours and merge it if there
|
|
are no issues. Homebrew should be automatic, too.
|
|
|
|
|
|
Manual packaging
|
|
^^^^^^^^^^^^^^^^
|
|
|
|
If a release-triggered upload fails, first inspect PyPI to determine which
|
|
files, if any, were already accepted. Download the exact CI artifacts into a
|
|
new empty directory, verify their version and complete file inventory, and run
|
|
``twine check``. After a separate decision to perform manual recovery, upload
|
|
only the missing files explicitly; do not use a reused ``dist/`` directory or
|
|
a broad wildcard.
|
|
|
|
For example, if both artifacts were inspected but only the second is missing
|
|
from PyPI, pass the exact filenames to Twine:
|
|
|
|
.. code-block:: bash
|
|
|
|
twine check "/path/to/artifact-one.whl" "/path/to/artifact-two.tar.gz"
|
|
twine upload "/path/to/artifact-two.tar.gz"
|
|
|
|
You can also make the files locally, but only from a fresh, clean detached
|
|
checkout of the verified tag/release commit and with an empty output directory.
|
|
This is still not recommended in general because SDists can pick up unrelated
|
|
or hidden files. The build procedure is:
|
|
|
|
.. code-block:: bash
|
|
|
|
nox -s build
|
|
nox -s build_global
|
|
|
|
Inspect and run ``twine check`` on the resulting files before selecting any
|
|
missing artifacts for an explicit upload.
|