blob: ecfd0466a11dc433c957b1b3e9716a1fa7c6fbaa [file]
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.