Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 14 additions & 24 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,37 +47,27 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Get the changelog underline
id: changelog_underline
# towncrier writes the rendered notes to stdout (informational
# chatter goes to stderr), so this is the curated release body for
# this version, not github-tag-action's commit-derived changelog.
- name: Generate the GitHub release notes
env:
RELEASE: ${{ steps.calver.outputs.release }}
run: |
underline="$(echo "$RELEASE" | tr -c '\n' '-')"
echo "underline=${underline}" >> "$GITHUB_OUTPUT"

- name: Update changelog
id: update_changelog
uses: jacobtomlinson/gha-find-replace@v3
with:
find: "Next\n----"
replace: "Next\n----\n\n${{ steps.calver.outputs.release }}\n${{ steps.changelog_underline.outputs.underline\
\ }}\n"
include: CHANGELOG.rst
regex: false
run: uv run --extra=release towncrier build --draft --version "$RELEASE" >
release-notes.md

- name: Check Update changelog was modified
# Assemble the same fragments into CHANGELOG.rst under a new
# ``$RELEASE`` section and delete the consumed fragment files.
- name: Update the changelog
env:
MODIFIED_FILES: ${{ steps.update_changelog.outputs.modifiedFiles }}
run: |
if [ "$MODIFIED_FILES" = "0" ]; then
echo "Error: No files were modified when updating changelog"
exit 1
fi
RELEASE: ${{ steps.calver.outputs.release }}
run: uv run --extra=release towncrier build --yes --version "$RELEASE"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Towncrier missing from release extra

High Severity

The release workflow runs towncrier via uv run --extra=release, but towncrier is only declared under optional-dependencies.dev, not optional-dependencies.release. The release job environment therefore will not install towncrier, so changelog and release-notes steps should fail before tagging or publishing.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a573f07. Configure here.


- uses: stefanzweifel/git-auto-commit-action@v7
id: commit
with:
commit_message: Bump CHANGELOG
file_pattern: CHANGELOG.rst
file_pattern: CHANGELOG.rst newsfragments
# Error if there are no changes.
skip_dirty_check: true

Expand All @@ -96,7 +86,7 @@ jobs:
tag: ${{ steps.tag_version.outputs.new_tag }}
makeLatest: true
name: Release ${{ steps.tag_version.outputs.new_tag }}
body: ${{ steps.tag_version.outputs.changelog }}
bodyFile: release-notes.md

- name: Build a binary wheel and a source tarball
env:
Expand Down
3 changes: 1 addition & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
Changelog
=========

Next
----
.. towncrier release notes start

2026.02.25.1
------------
Expand Down
9 changes: 9 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,17 @@
"sphinx.ext.napoleon",
"sphinx_substitution_extensions",
"sphinxcontrib.spelling",
"sphinxcontrib.towncrier.ext",
]

# Render the unreleased ``newsfragments/`` entries into
# ``docs/source/unreleased.rst`` so the Sphinx spelling, doc-build and
# link-checking gates cover the prose before it is assembled into
# CHANGELOG.rst at release time.
towncrier_draft_autoversion_mode = "draft"
towncrier_draft_include_empty = True
towncrier_draft_working_directory = f"{_pyproject_file.parent}"

templates_path = ["_templates"]
source_suffix = ".rst"
master_doc = "index"
Expand Down
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,5 @@ Reference
exceptions
contributing
release-process
unreleased
changelog
8 changes: 8 additions & 0 deletions docs/source/unreleased.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Unreleased changes
==================

Changes that have landed on the main branch but are not yet part of a
tagged release. These entries are assembled into the
:doc:`changelog` when the next release is published.

.. towncrier-draft-entries::
14 changes: 14 additions & 0 deletions docs/towncrier_template.rst.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

{% for section_name, section in sections.items() %}
{% if section %}
{% for category, entries in section.items() %}
{% for text, _ in entries.items() %}
- {{ text }}

{% endfor %}
{% endfor %}
{% else %}
No significant changes.

{% endif %}
{% endfor %}
Empty file added newsfragments/.gitkeep
Empty file.
33 changes: 33 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,17 @@ optional-dependencies.dev = [
"sphinx-pyproject==0.3.0",
"sphinx-substitution-extensions==2026.1.12",
"sphinxcontrib-spelling==8.0.2",
# ``sphinxcontrib-towncrier`` renders unreleased news fragments
# into docs/source/unreleased.rst during Sphinx builds.
"sphinxcontrib-towncrier==0.5.0a0",
"strict-kwargs==2026.5.19.post3",
"sybil==9.3.0",
# Listed explicitly (despite being transitive via vws-python-mock) so that
# [tool.uv.sources] can redirect to the CPU-only PyTorch index.
# See: https://vws-python.github.io/vws-python-mock/installation.html#faster-installation
"torch>=2.5.1",
"torchvision>=0.20.1",
"towncrier==25.8.0",
"ty==0.0.37",
"types-requests==2.33.0.20260518",
"vulture==2.16",
Expand Down Expand Up @@ -284,6 +288,8 @@ ignore = [
"*.enc",
".pre-commit-config.yaml",
"CHANGELOG.rst",
"newsfragments",
"newsfragments/**",
"CODE_OF_CONDUCT.rst",
"CONTRIBUTING.rst",
"LICENSE",
Expand Down Expand Up @@ -349,6 +355,30 @@ report.exclude_also = [
]
report.show_missing = true

[tool.towncrier]
# The changelog and the per-release GitHub release notes are both built
# from news fragments under ``newsfragments/``. The release workflow
# runs ``towncrier build`` to assemble them; contributors add one
# fragment file per user-facing change.
directory = "newsfragments"
filename = "CHANGELOG.rst"
# Custom template so an assembled version reproduces the historical
# style exactly: a bare ``<version>`` heading (no project name, no
# date) followed by a flat bullet list with no per-type sub-headings.
template = "docs/towncrier_template.rst.jinja"
title_format = "{version}"
# ``title_format`` underline first, then any nested headings. A bare
# version such as ``2026.05.18`` underlined with ``-`` matches every
# pre-towncrier entry in CHANGELOG.rst.
underlines = [ "-", "~", "^" ]
issue_format = "#{issue}"
type = [
# A single, unnamed fragment type keeps the assembled output as one
# flat bullet list, matching the historical changelog (which never
# grouped entries under "Features"/"Bugfixes"/... sub-headings).
{ directory = "change", name = "", showcontent = true },
]

[tool.pydocstringformatter]
write = true
split-summary-body = false
Expand Down Expand Up @@ -413,6 +443,9 @@ ignore_names = [
"spelling_word_list_filename",
"templates_path",
"warning_is_error",
"towncrier_draft_autoversion_mode",
"towncrier_draft_include_empty",
"towncrier_draft_working_directory",
]
# Duplicate some of .gitignore
exclude = [ ".venv" ]
Expand Down
Loading