############################# Render GitHub Release notes ############################# ``notes`` turns one reviewed changelog section into a GitHub Release body. Run it from the tagged checkout so the description matches the source that was published. It reads the changelog and prints Markdown to standard output; it does not edit the changelog or create the GitHub Release. Every release-creator command explains its choices on standard error, one line per decision, while standard output and the files it writes carry only the data. For the example below, ``notes`` logs ``Release notes for 1.2.3: 1 entry in 1 category from docs/changelog.md``, and ``> release-notes.md`` still captures the body alone. A complete Markdown example =========================== Suppose ``docs/changelog.md`` contains these two releases: .. code-block:: markdown # Changelog ## 1.2.3 (2026-10-18) ### Fixed - Avoid stale cache reads ([#42](https://github.com/owner/project/pull/42)) by [@alice](https://github.com/alice). ## 1.2.2 (2026-09-20) ### Fixed - Reject invalid cache keys by [@alice](https://github.com/alice). Render only ``1.2.3``: .. code-block:: bash uvx --from release-creator release-creator notes \ --changelog docs/changelog.md \ --tag 1.2.3 \ --repository owner/project \ --previous 1.2.2 \ --artifact-url https://pypi.org/project/project/1.2.3/ \ > release-notes.md ``--tag`` selects one section. ``--repository`` builds GitHub links. ``--previous`` selects the start of the comparison; the :doc:`tags command ` can find it. ``--artifact-url`` adds the published package link. ``release-notes.md`` contains the complete GitHub description: .. code-block:: markdown ### Bug fixes - Avoid stale cache reads ([#42](https://github.com/owner/project/pull/42)) by @alice. [🐍 PyPI release](https://pypi.org/project/project/1.2.3/) [📜 Full Changelog](https://github.com/owner/project/compare/1.2.2...1.2.3) Review that file before passing it to ``gh release create --notes-file release-notes.md``. The ``by`` credit stays in the changelog; the GitHub body uses an ``@alice`` mention. Rules and failure cases ======================= The tagged changelog decides which changes appear, their order, their categories, and their authors. ``notes`` does not infer changes or people from commits, and it does not check credits or pull request links: the ``fragments`` linker writes them, and ``validate-news`` rejects fragments that carry their own. ``notes`` uses the first section for ``--tag``. It renames ``Fixed`` to ``Bug fixes`` and ``Added`` to ``Features`` while keeping each release bullet on one line; an unknown category keeps its own name as the heading. Markdown text outside the categories passes through as written, and ``notes`` keeps empty Markdown headings, so leave them out of the tagged changelog; the supplied Towncrier template does this. The reStructuredText reader omits empty categories and renders blocks outside a category or release item as plain paragraphs. If the source section already has a ``Full Changelog`` line, ``notes`` replaces it with one link built from ``--previous`` and ``--tag``. The source's category anchors remain stable. See :doc:`the format reference <../reference/formats>` for every category name. Omit ``--artifact-url`` if there is no external artifact. Use ``--artifact-label`` to name another ecosystem's package link. A project that publishes several packages repeats ``--artifact-url``, each followed by its ``--artifact-label``; the links share one line, separated by commas. Each link starts with an emoji picked from its host: 🐍 for PyPI, 🧩 for the JetBrains Marketplace, and 📦 for anything else. If ``--previous`` is absent, the final link points to the commits at this tag instead of a comparison. Shared changelog with separate packages ======================================= When packages have separate histories in one Markdown file, name the package section explicitly. For example, ``toml-fmt`` has ``## pyproject-fmt`` above ``### 2.30.4 (date)`` and ``#### Fixed``. Its Git tag is ``pyproject-fmt/2.30.4``: .. code-block:: bash uvx --from release-creator release-creator notes \ --changelog CHANGELOG.md --package pyproject-fmt \ --tag pyproject-fmt/2.30.4 --repository tox-dev/toml-fmt \ --previous pyproject-fmt/2.30.3 > release-notes.md The package selector prevents a matching version in another package's history from entering the output. It does not change the repository used for GitHub links. New contributors ================ During release preparation, run ``contributors`` after ``towncrier build`` and before committing the changelog: .. code-block:: bash uvx --from release-creator release-creator contributors \ --changelog docs/changelog.md --version 1.2.3 --repository owner/project The command reads the pull requests linked in the ``1.2.3`` section and asks GitHub for each one's opener and merge time. A pull request opener is new when no merged pull request of theirs in the repository predates this one; when the release holds several of their pull requests, only the earliest counts. Co-authors never count, and bots and AI assistant accounts (``claude``, ``codex``, ``CommandCodeBot``, ``Copilot``) are skipped. The command needs ``gh`` with read access to the repository and edits only Towncrier changelogs; Knope projects do not use it. For a shared Markdown changelog, pass the package name, as in ``--package pyproject-fmt``. The command then edits the ``### 1.2.3`` section under ``## pyproject-fmt``, found as ``notes --package`` finds it, and the subsection gets a ``#### New Contributors`` heading and a ``release-pyproject-fmt-1-2-3-new-contributors`` anchor, matching that package's categories. When someone is new, the command appends a subsection at the end of the version section, with the same anchor and heading markup as the categories. In Markdown: .. code-block:: markdown ### New Contributors - [@alice](https://github.com/alice) made their first contribution in [#42](https://github.com/owner/project/pull/42) In reStructuredText, the heading uses the categories' underline character: .. code-block:: rst .. _release-1-2-3-new-contributors: New Contributors ---------------- - :user:`alice` made their first contribution in :pr:`42` Entries follow merge order. With no new contributor, the changelog stays unchanged. ``notes`` renders the subsection last in the release body, before the artifact and Full Changelog links. Pull requests that are not merged are skipped. Run the command once per release: a second run appends another subsection. The command logs each decision on standard error and leaves standard output empty. For the example above it logs ``New Contributors: @alice (#42)``, or a reason such as ``New Contributors: none for 1.2.3 (no first-time author among its pull requests)``. reStructuredText changelogs =========================== Use a ``.rst`` changelog path to select the reStructuredText reader: .. code-block:: bash uvx --from release-creator release-creator notes \ --changelog docs/changelog.rst --tag 1.2.3 \ --repository owner/project > release-notes.md ``:issue:`` links target issues; ``:pr:`` and ``:pull:`` target pull requests. ``:user:`` becomes an ``@handle`` mention. Nested lists stay nested. ``.. code-block::`` directives become fenced Markdown blocks inside their release items. Top-level ``.. warning::`` directives become separate warning blocks after the preceding item.