TOWNCRIER(7)
π NAME π
okay bestie, welcome to towncrier(7) β yes, section 7, the manual-page of it all. sit down, hydrate, we're doing this. π
towncrier is a utility to produce useful, summarized news files (also known as changelogs) for your project. slay.
so the vibe is this: instead of reading the whole Git history, or having one single file which developers all write to and produce merge conflicts (cursed, honestly, absolutely giving chaos), towncrier reads βnews fragmentsβ β little files that contain information useful to end users. tiny. specific. iconic. β¨
used by Twisted <https://github.com/twisted/twisted>, pytest <https://github.com/pytest-dev/pytest/>, pip <https://github.com/pypa/pip/>, BuildBot <https://github.com/buildbot/buildbot>, and attrs <https://github.com/python-attrs/attrs>, among others. the friend group is stacked, fr fr.
and here's the tea: while the command line tool towncrier requires Python to run, as long as you donβt use any Python-specific affordances (like auto-detection of the project version), it is usable with any project type on any platform. language-agnostic king behavior. π
π¦ PHILOSOPHY π¦
towncrier delivers the news which is convenient to those that hear it, not those that write it. read that again. internalize it.
basically: the βdeveloper logβ might contain complex information about the original issue, how it was fixed, who authored the fix, and who reviewed the fix. a lot, right? so towncrier duplicates what has changed out of there and into a βnews fragmentβ β a small file containing just enough information to be useful to end users. then towncrier produces a digest of the changes which is valuable to those who may wish to use the software. π
these fragments are also commonly called βtopfilesβ or βnewsfilesβ, so if you see those words floating around, that's the same thing, no cap.
towncrier works best in a development system where all merges involve closing an issue. periodt.
π¬ DOCUMENTATION π¬
π Narrative π
(this is the heading bestie, the actual content lives in the sections right below it β keep scrolling π«Ά)
π Tutorial π
this tutorial assumes you have a Python project with a reStructuredText (rst) or Markdown (md) news file (also known as changelog) that you wish to use towncrier on, to generate its news file. it will cover setting up your project with a basic configuration, which you can then feel free to customize <customization/index.html>. yes, you get to make it yours. main character customization screen energy. π
install from PyPI:
python3 -m pip install towncrier
βοΈ Configuration βοΈ
towncrier keeps its config in the PEP-518 <https://www.python.org/dev/peps/pep-0518/> pyproject.toml or a towncrier.toml file. and listen β if the latter exists, it takes precedence. noted.
the most basic configuration is just telling towncrier where to look for news fragments and what file to generate:
[tool.towncrier]
directory = "changes"
# Where you want your news files to come out, `NEWS.rst` is the default.
# This can be .rst or .md, towncrier's default template works with both.
# filename = "NEWS.rst"
which will look into β./changesβ for news fragments and write them into β./NEWS.rstβ. simple. elegant. we love.
if youβre working on a Python project, you can also specify a package:
[tool.towncrier]
# The name of your Python package
package = "myproject"
# The path to your Python package.
# If your package lives in 'src/myproject/', it must be 'src',
# but if you don't keep your code in a 'src' dir, remove the
# config option
package_dir = "src"
by default, towncrier will look for news fragments inside your Python package, in a directory named newsfragments. so with this example project, it will look in src/myproject/newsfragments/ for them.
create this folder:
$ mkdir src/myproject/newsfragments/
# This makes sure that Git will never delete the empty folder
$ echo '!.gitignore' > src/myproject/newsfragments/.gitignore
(the .gitignore trick is lowkey genius, Git would otherwise yeet your empty lil folder into the void π)
π Detecting Version π
towncrier needs to know what version your project is when generating news files. these are the ways you can provide it, in order of precedence (with the first taking precedence over the second, and so on):
- manually pass
--version=<myversionhere>when interacting with towncrier. - set a value for the version option in your configuration file.
- for Python projects with a
packagekey in the configuration file:- install the package to use its metadata version information
- add a
__version__in the top level package that is either a string literal, a tuple, or an Incremental <https://github.com/twisted/incremental> version
as an example, you can manually specify the version when calling towncrier on the command line with the --version flag:
$ towncrier build --version=1.2.3post4
π Setting Date π
towncrier will also include the current date (in YYYY-MM-DD format) when generating news files. you can change this with the --date flag:
$ towncrier build --date=2018-01-01
π Creating News Fragments π
towncrier news fragments are categorised according to their βtypeβ. There are five default types, but you can configure them freely (see Configuration <configuration.html> for details).
the five default types are:
- feature: Signifying a new feature.
- bugfix: Signifying a bug fix.
- doc: Signifying a documentation improvement.
- removal: Signifying a deprecation or removal of public API.
- misc: An issue has been closed, but it is not of interest to users.
when you create a news fragment, the filename consists of the issue/ticket ID (or some other unique identifier) as well as the βtypeβ. towncrier does not care about the fragmentβs suffix. it's chill like that.
you can create those fragments either by hand, or using the towncrier create command. letβs create some example news fragments to demonstrate:
$ echo 'Fixed a thing!' > src/myproject/newsfragments/1234.bugfix
$ towncrier create --content 'Can also be ``rst`` as well!' 3456.doc.rst
# You can associate multiple issue numbers with a news fragment by giving them the same contents.
$ towncrier create --content 'Can also be ``rst`` as well!' 7890.doc.rst
$ echo 'The final part is ignored, so set it to whatever you want.' > src/myproject/newsfragments/8765.removal.txt
$ echo 'misc is special, and does not put the contents of the file in the newsfile.' > src/myproject/newsfragments/1.misc
$ towncrier create --edit 2.misc.rst # starts an editor
$ towncrier create -c "Orphan fragments have no issue ID." +random.bugfix.rst
for orphan news fragments (those that donβt need to be linked to any issue ID or other identifier), start the file name with +. the content will still be included in the release notes, at the end of the category corresponding to the file extension:
$ echo 'Fixed an unreported thing!' > src/myproject/newsfragments/+anything.bugfix
we can then see our news fragments compiled by running towncrier in draft mode:
$ towncrier build --draft --name myproject --version 1.0.2 --date 2015-12-27
you should get an output similar to this:
Loading template...
Finding news fragments...
Rendering news fragments...
Draft only -- nothing has been written.
What is seen below is what would be written.
myproject 1.0.2 (2015-12-27)
============================
Bugfixes
--------
- Fixed a thing! (#1234)
- Orphan fragments have no issue ID.
Improved Documentation
----------------------
- Can also be ``rst`` as well! (#3456, #7890)
Deprecations and Removals
-------------------------
- The final part is ignored, so set it to whatever you want. (#8765)
Misc
----
- #1, #2
Note: if you configure a Markdown file (for example, filename = "CHANGES.md") in your configuration file, the titles will be output in Markdown format instead.
Note: all files (news fragments, the news file, the configuration file, and templates) are encoded and are expected to be encoded as UTF-8. no wiggle room on this one bestie, it's UTF-8 or nothing π
π Producing News Files In Production π
to produce the news file for real, run:
$ towncrier
this command will remove the news files (with git rm) and append the built news to the filename specified in pyproject.toml, and then stage the news file changes (with git add). it leaves committing the changes up to the user. so it does the chores but not all the chores. respectful.
if you wish to have content at the top of the news file (for example, to say where you can find the issues), put your text above a rST comment that says:
.. towncrier release notes start
towncrier will then put the version notes after this comment, and leave your existing content that was above it where it is. untouched. preserved. π«Ά
Note: if you configure a Markdown file (for example,filename = "CHANGES.md") in your configuration file, the comment should be<!-- towncrier release notes start -->instead.
π Finale π
you should now have everything you need to get started with towncrier! please see Customizing <customization/index.html> for some common tasks, or Configuration <configuration.html> for the full configuration specification. go forth and changelog, babe. β
π§ How to Keep a Changelog in Markdown π§
Keep a Changelog <https://keepachangelog.com/> is a standardized way to format a news file in Markdown <https://en.wikipedia.org/wiki/Markdown>. it's giving organized. it's giving tidy.
this guide shows you how to configure towncrier for keeping a Markdown-based news file of a project without using any Python-specific features. everything used here can be used with any other language or platform. universally applicable, we love to see it. π
this guide makes the following assumptions:
- the project lives at <https://github.com/twisted/my-project/>.
- the news file name is
CHANGELOG.md. - you store the news fragments in the
changelog.ddirectory at the root of the project.
put the following into your pyproject.toml or towncrier.toml:
[tool.towncrier]
directory = "changelog.d"
filename = "CHANGELOG.md"
start_string = "<!-- towncrier release notes start -->\n"
underlines = ["", "", ""]
title_format = "## [{version}](https://github.com/twisted/my-project/tree/{version}) - {project_date}"
issue_format = "[#{issue}](https://github.com/twisted/my-project/issues/{issue})"
[[tool.towncrier.type]]
name = "Security"
[[tool.towncrier.type]]
name = "Removed"
[[tool.towncrier.type]]
name = "Deprecated"
[[tool.towncrier.type]]
name = "Added"
[[tool.towncrier.type]]
name = "Changed"
[[tool.towncrier.type]]
name = "Fixed"
next create the news fragment directory:
$ mkdir changelog.d
next, create the news file with an explanatory header:
$ cat >CHANGELOG.md <<EOF
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
This project uses [*towncrier*](https://towncrier.readthedocs.io/) and the changes for the upcoming release can be found in <https://github.com/twisted/my-project/tree/main/changelog.d/>.
<!-- towncrier release notes start -->
EOF
Note: The two empty lines at the end are on purpose. do not delete them. do not "clean them up". they are there for a reason and the reason knows what it did. π
okay bestie that's literally the whole setup, now you get to actually write stuff!! π start dropping news fragments like this:
towncrier create -c "Added a cool feature!" 1.added.md
towncrier create -c "Changed a behavior!" 2.changed.md
towncrier create -c "Deprecated a module!" 3.deprecated.md
towncrier create -c "Removed a square feature!" 4.removed.md
towncrier create -c "Fixed a bug!" 5.fixed.md
towncrier create -c "Fixed a security issue!" 6.security.md
towncrier create -c "Fixed a security issue!" 7.security.md
towncrier create -c "A fix without an issue number!" +something-unique.fixed.md
then after you run towncrier build --yes --version 1.0.0 (the Git error messages? ignore them, they're just being extra π) your CHANGELOG.md comes out looking like:
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
This project uses [*towncrier*](https://towncrier.readthedocs.io/) and the changes for the upcoming release can be found in <https://github.com/twisted/my-project/tree/main/changelog.d/>.
<!-- towncrier release notes start -->
## [1.0.0](https://github.com/twisted/my-project/tree/1.0.0) - 2022-09-28
### Security
- Fixed a security issue! ([#6](https://github.com/twisted/my-project/issues/6), [#7](https://github.com/twisted/my-project/issues/7))
### Removed
- Removed a square feature! ([#4](https://github.com/twisted/my-project/issues/4))
### Deprecated
- Deprecated a module! ([#3](https://github.com/twisted/my-project/issues/3))
### Added
- Added a cool feature! ([#1](https://github.com/twisted/my-project/issues/1))
### Changed
- Changed a behavior! ([#2](https://github.com/twisted/my-project/issues/2))
### Fixed
- Fixed a bug! ([#5](https://github.com/twisted/my-project/issues/5))
- A fix without an issue number!
pretty close, right?? so yeah, that's the whole guide, we're done here!! β¨
quick note bestie, don't skip these: - the sections get rendered in the order the fragment types are defined. order matters, periodt. - because towncrier doesn't have a concept of a "previous version" (yet), the version links are gonna point to the release tags and not to the compare link like in Keep a Changelog. - Keep a Changelog doesn't have the concept of a uncategorized change, so the template doesn't expect any.
Multiple Projects Share One Config (Monorepo) π
okay so multiple projects can each have their own independent release notes while sharing the same format β think packages inside a monorepo. here's how you set that up with towncrier, bestie.
minimal example incoming:
repo
βββ project_a
β βββ newsfragments
β β βββ 123.added
β βββ project_a
β β βββ __init__.py
β βββ NEWS.rst
βββ project_b
β βββ newsfragments
β β βββ 120.bugfix
β βββ project_b
β β βββ __init__.py
β βββ NEWS.rst
βββ towncrier.toml
and the towncrier.toml looks like this:
[tool.towncrier]
# It's important to keep these config fields empty
# because we have more than one package/name to manage.
package = ""
name = ""
now to add a fragment:
towncrier create --config towncrier.toml --dir project_a 124.added
this should create a file at project_a/newsfragments/124.added.
to build the news file for that same project:
towncrier build --config towncrier.toml --dir project_a --version 1.5
and heads up: you must explicitly pass --version, there is no other way to get the version number. the towncrier.toml can only contain one version number and the package field is of no use for the same reason.
Reference π
Command Line Reference π
the following options can be passed to all of the commands that are explained below:
--config FILE_PATHβ pass a custom config file at FILE_PATH. Default:towncrier.tomlorpyproject.tomlfile. if both files exist, the first will take precedence.--dir PATHβ the command gets executed relative to PATH. so for instance, with the default config, news fragments are checked and added inPATH/newsfragmentsand the news file is built inPATH/NEWS.rst. Default: current directory.
towncrier build ποΈ
builds the combined news file out of your news fragments. build is also assumed if you don't pass any command at all.
if there are no news fragments (and yes, that includes an empty fragments directory or a directory that doesn't even exist), a notice of "no significant changes" will be added to the news file.
by default, the processed news fragments get removed. for any fragments committed in your git repo, git rm will be used (which will also remove the fragments directory if it's now empty).
--draftβ only render news fragments to standard output. don't write to files, don't check versions. only renders the news fragments without the surrounding template.--name NAMEβ use NAME as the project name in the news file. can be configured.--version VERSIONβ use VERSION in the rendered news file. can be configured or guessed (that's the default). this option requires thebuildcommand to be explicitly passed.--date DATEβ the date in ISO format (https://xkcd.com/1179/) to use in the news file. Default: today's date.--yesβ do not ask for confirmations. useful for automated tasks.--keepβ don't delete news fragments after the build and don't ask for confirmation whether to delete or keep the fragments.
towncrier create βοΈ
creates a news fragment in the directory towncrier is configured to look for fragments in:
$ towncrier create 123.bugfix.rst
if you don't provide a file name, towncrier will prompt you for one.
towncrier create will enforce that the passed type (like bugfix) is valid.
if the fragments directory doesn't exist, it will be created. no stress.
if the filename already exists, towncrier create will add (and then increment) a number after the fragment type until it finds a filename that doesn't exist yet. so in the example above, it'll generate 123.bugfix.1.rst if 123.bugfix.rst already exists.
to create a news fragment that isn't tied to a specific issue (what towncrier calls an "orphan fragment"), start the fragment name with a +. if that's the entire fragment name, a random hash gets added for you:
$ towncrier create +.feature.rst
$ ls newsfragments/
+fcc4dc7b.feature.rst
--content,-c CONTENTβ a string to use for content. Default: an instructive placeholder.--edit/--no-editβ whether to start$EDITORto edit the news fragment right away. Default:$EDITORwill be started unless you also provided content.--section SECTIONβ the section to use for the news fragment. Default: the section with no path, or if all sections have a path then the first defined section.
towncrier check π
to check if a feature branch adds at least one news fragment, run:
$ towncrier check
the check is automatically skipped when the main news file is modified inside the branch, since that signals a release branch that's expected to not have news fragments.
by default, towncrier compares the current branch against origin/main (and falls back to origin/master with a warning if it exists, for now).
--compare-with REMOTE-BRANCHβ use REMOTE-BRANCH instead oforigin/main:
$ towncrier check --compare-with origin/trunk
--stagedβ include files that have been staged for commit when checking for news fragments:
$ towncrier check --staged
$ towncrier check --staged --compare-with origin/trunk
Configuration Reference βοΈ
towncrier has sooooo many knobs and switches you can use to customize it to your project's needs. the setup in the Tutorial doesn't touch on many of them, but this document will detail every single one of these options for you!!
and for how to perform common customization tasks, see Customizing towncrier.
[tool.towncrier] π§°
all configuration for towncrier sits inside towncrier.toml or pyproject.toml, under the tool.towncrier namespace. please see https://toml.io/ for how to write TOML.
a minimal config for a Python project looks like this:
# pyproject.toml
[project]
name = "myproject"
a minimal config for a non-Python project looks like this:
# towncrier.toml
[tool.towncrier]
name = "My Project"
Top level keys π
nameβ the name of your project. for Python projects that provide apackagekey, if left empty then the name will be automatically determined from thepackagekey. Defaults to the key[project.name]inpyproject.toml(if present), otherwise defaults to the empty string"".versionβ the version of your project. for Python projects that provide thepackagekey, if left empty then the version will be automatically determined from the installed package's version metadata or a__version__variable in the package's module. if not provided or able to be determined, the version must be passed explicitly by the command line argument--version.directoryβ the directory storing your news fragments. for Python projects that provide apackagekey, the default is anewsfragmentsdirectory within the package. otherwise the default is anewsfragmentsdirectory relative to either the directory passed as--diror (by default) the configuration file.filenameβ the filename of your news file."NEWS.rst"by default. its location is determined the same way as the location of the directory storing the news fragments.templateβ path to the template for generating the news file. if the path looks like<some.package>:<filename.ext>, it's interpreted as a template bundled with an installed Python package."towncrier:default.rst"by default unlessfilenameends with.md, in which case the default is"towncrier:default.md".start_stringβ the magic string that towncrier looks for when considering where the release notes should start.".. towncrier release notes start\n"by default unlessfilenameends with.md, in which case the default is"<!-- towncrier release notes start -->\n".title_formatβ a format string for the title of your project.
The explicit value of False disables the title entirely β no title, gone, bye π. Any other empty value means the template should render the title (the bundled templates go with <name> <version> (<date>)). And if you want your title to be built dynamically, use these keys in your string: {name}, {version}, and {project_date}.
"" by default.
When you're using reStructuredText, formatted titles get underlined using the underlines configuration. For titles, the first value from underlines is what creates the underline (and it gets inserted on the line following the title). But if the template has an .md suffix, we assume we're looking at markdown format and the title is applied as-is, i.e. full control over the title format is given to the user. The top header level is inferred from this β e.g. title_format = "(v{version})=\n### {version}" will render the title at level 3, categories at level 4, and so on. iykyk β¨
issue_formatβ a format string for rendering the issue/ticket number in newsfiles.
If it's None, issues are rendered as #<issue> when they're integers, or just <issue> otherwise. Use the {issue} key in your string to render the issue number β for example, Markdown projects may want "[{issue}]: https://<your bug tracker>/{issue}".
None by default.
underlinesβ the characters used for underlining headers.
Not used in the bundled Markdown template, just so u know.
["=", "-", "~"] by default.
wrapβ boolean value indicating whether to wrap news fragments to a line length of79.
false by default.
all_bulletsβ boolean value indicating whether the template uses bullets for each news fragment.
true by default.
single_fileβ boolean value indicating whether to write all news fragments to a single file.
If it's false, the filename should use the following keys to render the filenames dynamically: {name}, {version}, and {project_date}.
true by default.
orphan_prefixβ the prefix used for orphaned news fragments.
"+" by default.
create_eof_newlineβ makes sure the content of a news fragment file created withtowncrier createends with an empty line.
true by default.
create_add_extensionβ adds thefilenameoption extension to news fragment files created withtowncrier createif an extension wasn't explicitly provided.
true by default.
ignoreβ a case-insensitive list of filenames in the news fragments directory to ignore. Wildcard matching is supported via thefnmatchfunction, fromhttps://docs.python.org/3/library/fnmatch.html#fnmatch.fnmatchπ
None by default.
towncrier check will fail if there are any news fragment files with invalid filenames β except the ones on this list. towncrier build will likewise fail, but only if this list has been configured (so set it to an empty list if there are literally no files to ignore).
The following filenames are automatically ignored, case insensitive:
.gitignore.gitkeep.keepREADMEREADME.mdREADME.rst- the template file itself
issue_patternβ makes sure the issue name (the file name excluding the category and suffix) matches a certain regex pattern. Make sure to use escape characters properly (e.g."\d+"for digit-only file names). When it's empty (""), every issue name is considered valid β everyone's welcome π
"" by default.
β¨ Extra top level keys for Python projects β¨
packageβ the Python package name of your project.
It lets name and version be automatically determined from the Python package, and it changes the default directory to be a newsfragments directory within this package.
Defaults to the key [project.name] in pyproject.toml (if present), otherwise defaults to the empty string "".
package_dirβ the folder your package lives in.
"." by default, though some projects might need to use "src".
π¦ Sections π¦
towncrier supports splitting fragments into multiple sections, each with its own news of fragment types. Like separate little friend groups for your news.
Add an array of tables to your .toml configuration file named [[tool.towncrier.section]].
Each table within this array has the following mandatory keys:
nameβ the name of the section.pathβ the path to the directory containing the news fragments for this section, relative to the configureddirectory. Use""for the root directory.
For example:
[[tool.towncrier.section]]
name = "Main Platform"
path = ""
[[tool.towncrier.section]]
name = "Secondary"
path = "secondary"
π« Section Path Behaviour π«
The path behaviour is slightly different depending on whether directory is explicitly set.
If directory is not set, "newsfragments" is added to the end of each path. So with the sections above, the paths would be:
- Main Platform β
./newsfragments - Secondary β
./secondary/newsfragments
If directory is set, the section paths are appended to this path. So with directory = "changes" and the above sections, the paths would be:
- Main Platform β
./changes - Secondary β
./changes/secondary
πΈ Custom fragment types πΈ
towncrier has the following default fragment types: feature, bugfix, doc, removal, and misc.
You can use either of the two following methods to define custom types instead β and heads up, you will need to redefine any of the default types you want to use π
π Use TOML tables (alphabetical order) π
Add tables to your .toml configuration file named [tool.towncrier.fragment.<a custom fragment type>].
These may include the following optional keys:
nameβ the description of the fragment type, as it must be included in the news file.
Defaults to its fragment type, but capitalized.
showcontentβ a boolean value indicating whether the fragment contents should be included in the news file.
true by default.
Note: π«Ά Orphan fragments (those without an issue number) always have their content included. If a fragment was created, it means that information is important for end users.
checkβ a boolean value indicating whether the fragment should be considered by thetowncrier checkcommand.
true by default.
For example, if you want your custom fragment types to be ["feat", "fix", "chore",] and you want all of them to use the default configuration except "chore", you can do it as follows:
[tool.towncrier]
[tool.towncrier.fragment.feat]
[tool.towncrier.fragment.fix]
[tool.towncrier.fragment.chore]
name = "Other Tasks"
showcontent = false
[tool.towncrier.fragment.deps]
name = "Dependency Changes"
check = false
Warning: π Since TOML mappings aren't ordered, types defined using this method are always rendered alphabetically. No negotiating, bestie.
π₯ Use a TOML Array (defined order) π₯
Add an array of tables to your .toml configuration file named [[tool.towncrier.type]].
If you use this way to configure custom fragment types, ensure there is no tool.towncrier.fragment table. Fr fr, just the one.
Each table within this array has the following mandatory keys:
name(required) β the description of the fragment type, as it must be included in the news file.directoryβ the type / category of the fragment.
Defaults to name.lower().
showcontentβ a boolean value indicating whether the fragment contents should be included in the news file.
true by default.
Note: π«Ά Orphan fragments (those without an issue number) always have their content included. If a fragment was created, it means that information is important for end users.
checkβ a boolean value indicating whether the fragment should be considered by thetowncrier checkcommand.
true by default.
For example:
[tool.towncrier]
[[tool.towncrier.type]]
name = "Deprecations"
[[tool.towncrier.type]]
directory = "chore"
name = "Other Tasks"
showcontent = false
[[tool.towncrier.type]]
directory = "deps"
name = "Dependency Changes"
showcontent = true
check = false
π§ pre-commit π§
towncrier can also be used in your pre-commit configuration (.pre-commit-config.yaml, and the project itself lives at https://pre-commit.com/) to check and/or update your news fragments on commit or during CI processes. Main character automation energy.
No additional configuration is needed in your towncrier configuration β the hook will read from the appropriate configuration files in your project. Slay.
π Examples π
Usage with the default configuration
repos:
- repo: https://github.com/twisted/towncrier
rev: 23.11.0 # run 'pre-commit autoupdate' to update
hooks:
- id: towncrier-check
Usage with custom configuration and directories
News fragments are stored in changelog.d/ in the root of the repository and we want to keep the news fragments when running update:
repos:
- repo: https://github.com/twisted/towncrier
rev: 23.11.0 # run 'pre-commit autoupdate' to update
hooks:
- id: towncrier-update
files: $changelog\.d/
args: ['--keep']
π Customizing towncrier π
towncrier can be customized to suit your project's needs. These pages should describe common customization tasks, while if you want a reference, see Configuration <../configuration.html>.
β¨ Customizing the News File Output β¨
π Adding Content Above towncrier π
If you wish to have content at the top of the news file (for example, to say where you can find the issues), you can use a special rST comment to tell towncrier to only update after it. In your existing news file (e.g. NEWS.rst), add the following line above where you want towncrier to put content:
.. towncrier release notes start
In an existing news file, it'll look something like this:
This is the changelog of my project. You can find the
issue tracker at http://blah.
.. towncrier release notes start
myproject 1.0.2 (2018-01-01)
============================
Bugfixes
--------
- Fixed, etc...
towncrier will not alter content above the comment. That's the whole deal β above that line is your territory π
πΈ Markdown πΈ
If your news file is in Markdown (e.g. NEWS.md), use the following comment instead:
π οΈ DEVELOPMENT π οΈ
π RELEASE NOTES π
okay bestie, gather round, this is the receipts section π
every towncrier issue gets filed on GitHub at https://github.com/twisted/towncrier/issues, and every single ticket number you see down there corresponds to a closed GitHub issue. iykyk.
so like, when you see a cute little #394 sitting at the end of a bullet? that's shorthand for https://github.com/twisted/towncrier/issues/394. slay. no need to retype the whole URL forty times, we're not monsters π
also fun fact, the raw docs have a literal marker comment <!-- towncrier release notes start --> and that's where allll the following content lives. iconic little flag fr.
towncrier 25.8.0 (2025-08-30)
No significant changes since the previous release candidate. (the glow-up was already done in the RC, periodt β¨)
Features π
- Support was added for Mercurial SCM. yes, the other version control bestie. (
#394) - The
towncrier checkcommand now has a--stagedflag so it can inspect the files staged for commit when checking for a news fragment β which is so useful in a pre-commit hook. (#676) - When used with a
pyproject.tomlfile, if no explicit values are defined for[tool.towncrier.name|package], they now fallback to the value of[project.name]. (#687) - The
directoryconfiguration option is no longer required β it can now be generated based on the value from thenameconfiguration. (#691) - Added support for Python 3.13 and removed support for Python 3.8. (
#700)
Bugfixes π§
- Multi-line news fragments that end with a code block now get a newline inserted before the link to the issue is appended, so the formatting doesn't get destroyed. (
#614) - Markdown header level is correctly inferred from
title_format. (#695)
Improved Documentation π
- The documentation RST source files are now included in the sdist package. (
#672) - Refactored the default markdown template to make it easier to understand, extend, and customize. (
#706)
Deprecations and Removals π
- Support for Python 3.8 was removed. (
#700) - When no sections are present, the default Markdown template now renders the category headers as H2. In previous versions it was rendered as H3. (
#663)
Misc π«Ά
#667,#669,#679,#680,#682,#701,#702,#713
Towncrier 24.8.0 (2024-08-23)
No changes since the previous release candidate.
Features π
- Added
.gitkeepas an ignored filename. (#643) - The config
ignoreoption now supports wildcard matching viafnmatch. (#644) - Added a config for enforcing issue names using regex. (
#649)
Bugfixes π§
- The template file is now ignored based only on the file name. (
#638) - Control of the header formatting is once again completely up to the user when they are writing markdown files (this fixes a regression introduced in
#610). (#651) - Fixed an issue where
issue_templatefailed to recognize the issue name of files with a non-category suffix (.md). (#654) - Fixed a bug where orphan news fragments (e.g.
+abc1234.feature) would fail when anissue_patternis configured. Orphan news fragments are now exempt fromissue_patternchecks. (#655)
Deprecations and Removals π
- Moved the towncrier version definition from
src/towncrier/_version.pytopyproject.toml.
towncrier.__version__ was removed, after being deprecated in 23.6.0. (#640)
Misc π«Ά
#640,#657
Towncrier 24.7.1 (2024-07-31)
No significant changes since the previous release candidate.
Bugfixes π§
- When the template file is stored in the same directory as the news fragments, it is automatically ignored when checking for valid fragment file names. (
#632)
Misc π«Ά
#629,#630
Towncrier 24.7.0 (2024-07-31)
No changes since the previous release candidate.
Features π
towncrier buildnow handles removing news fragments which are not part of the git repository β like uncommitted or unstaged files, bestie, we see you. (#357)- Inferring the version of a Python package now tries to use the metadata of the installed package before importing the package explicitly (which only looks for
[package].__version__). (#432) - If no filename is given when doing
towncrier create, it now interactively asks for the issue number and fragment type (and then launches an interactive editor for the fragment content).
Now by default, when creating a fragment it will be appended with the filename option's extension (unless an extension is explicitly provided). For example, towncrier create 123.feature will create news/123.feature.rst. This can be changed in the configuration file by setting add_extension = false.
A new line is now added by default to the end of the fragment contents. This can be reverted in the configuration file by setting add_newline = false. (#482)
- The temporary file that
towncrier createcreates now uses the correct.rstor.mdextension, which may help your editor with syntax highlighting. (#594) - Running
towncrierwill now traverse back up directories looking for the configuration file. (#601) - The
towncrier createaction now uses the sections defined in your config (either interactively, or via the new--sectionoption). (#603) - News fragments are now sorted by issue number even if they have non-digit characters.
For example:
- some issue (gh-3, gh-10)
- another issue (gh-4)
- yet another issue (gh-11)
The sorting algorithm groups the issues first by non-text characters and then by number. (#608)
- The
title_formatconfiguration option now uses a markdown format for markdown templates. (#610) - Newsfragment categories can now be marked with
check = false, which makes them get ignored intowncrier check. (#617) towncrier checkwill now fail if any news fragments have invalid filenames.
Added a new configuration option called ignore that lets you specify a list of filenames that should be ignored. If this is set, towncrier build will also fail if any filenames are invalid β except for the ones in that list. (#622)
Bugfixes π§
- Added explicit encoding to
read_text. (#561) - The default Markdown template now renders a title containing the release version and date, even when the
nameconfiguration is left empty. (#587) - Orphan news fragments β fragments not associated with an issue β consisting of only digits (e.g.
+12345678.feature) now retain their leading marker character. (#588) - Orphan news fragments (fragments not associated with an issue) will now still show in categories that are marked to not show content, since they don't have an issue number to show anyway. (
#612)
Improved Documentation π
- Clarify version discovery behavior. (
#432,#602) - The tutorial now introduces the
filenameoption in the appropriate paragraph and mentions its default value. (#586) - Added docs to explain how
towncrier create +.feature.rst(orphan fragments) works. (#589)
Misc π«Ά
#491,#561,#562,#568,#569,#571,#574,#575,#582,#591,#596,#597,#625
towncrier 23.11.0 (2023-11-08)
No significant changes since the previous release candidate.
Bugfixes π§
buildnow treats a missing fragments directory the same as an empty one, consistent with the other operations. (#538)- Fragments with filenames like
fix-1.2.3.featureare now associated with the issuefix-1.2.3. In previous versions they were incorrectly associated to issue3. (#562) - Orphan newsfragments containing numeric values are no longer accidentally associated to issues. In previous versions the orphan marker was ignored and the newsfragment was associated to an issue having the last numerical value from the filename. (
#562)
Misc
#558,#559
π towncrier 23.10.0 (2023-10-24) π
No significant changes since the previous release candidate. (translation: the RC already ate, so this one's just the victory lap π )
β¨ Features β¨
- Python 3.12 is now officially supported. (
#541) - Initial support was added for monorepo-style setup. That's one project with multiple independent news files stored in separate sub-directories, all sharing the same towncrier config. (
#548) ποΈ - Two newlines are <italics>no longer always</italics> added between the current release notes and the previous content. The newlines are now defined only inside the template. (
#552)
Important! If you're using a custom template and want to keep the same whitespace between releases, you may have to modify your template. Do not skip this bestie, your diff will be a mess. π
π Bugfixes π
- Towncrier now vendors the
click-default-grouppackage that prevented installations on modern Pips. (#540)
π Improved Documentation π
- The markdown docs now use the default markdown template rather than a simpler custom one. (
#545) - Cleanup a duplicate backtick in the tutorial. (
#551) (one tiny ``` had a whole villain arc π)
π Deprecations and Removals π
- The support for Python 3.7 has been dropped. (
#521)
π¦ Misc π¦
#481,#520,#522,#523,#529,#536
π towncrier 23.6.0 (2023-06-06) π
This is the last release to support Python 3.7. π (saying bye to an icon fr)
β¨ Features β¨
- Make
towncrier createuse the fragment counter rather than failing on existing fragment names.
For example, if there is an existing fragment named 123.feature, then towncrier create 123.feature will now create a fragment named 123.feature.1. (#475)
- Provide a default Markdown template if the configured filename ends with
.md.
The Markdown template uses the same rendered format as the default reStructuredText template, but with a Markdown syntax. (#483)
- Towncrier no longer depends on setuptools & uses
importlib.resources(or its backport) instead. (#496) - Added pre-commit hooks for checking and updating news in projects using pre-commit. (
#498) πͺ - Calling
towncrier checkwithout an existing configuration, will just show only an error message.
In previous versions, a traceback was generated instead of the error message. (#501) (no more wall-of-traceback jump scare π«Ά)
π Bugfixes π
- Fix creating fragment in a section not adding random characters.
For example, towncrier create some_section/+.feature should end up as a fragment named something like news/some_section/+a4e22da1.feature. (#468)
- Fix the ReadTheDocs build for towncrier which was broken due to the python version in use being 3.8. Upgrade to 3.11. (
#509)
π Improved Documentation π
- Moved man page to correct section (
#470) - Update link to Quick Start in
configuration.htmlto point to Tutorial instead. (#504) - Add a note about the build command's
--versionrequiring the command to be explicitly passed. (#511) - Fix typos in the Pre-Commit docs. (
#512)
π¦ Misc π¦
#459,#462,#472,#485,#486,#487,#488,#495,#497,#507,#1117,#513
π towncrier 22.12.0 (2022-12-21) π
No changes since the previous release candidate. (she's the same girl, just with a final version number π )
π towncrier 22.12.0rc1 (2022-12-20) π
β¨ Features β¨
- Added
--keepoption to the build command that allows generating a newsfile, but keeps the newsfragments in place. This option can not be used together with--yes. (#129) - Python 3.11 is now officially supported. (
#427) - You can now create fragments that are not associated with issues. Start the name of the fragment with
+(e.g.+anything.feature). The content of these orphan news fragments will be included in the release notes, at the end of the category corresponding to the file extension.
To help quickly create a unique orphan news fragment, towncrier create +.feature will append a random string to the base name of the file, to avoid name collisions. (#428)
π Improved Documentation π
- Improved contribution documentation. (
#415) - Correct a typo in the readme that incorrectly documented custom fragments in a format that does not work. (
#424) - The documentation has been restructured and (hopefully) improved. (
#435) - Added a Markdown-based how-to guide. (
#436) - Defining custom fragments using a TOML array is not deprecated anymore. (
#438)
π Deprecations and Removals π
- Default branch for
towncrier checkis now"origin/main"instead of"origin/master". If"origin/main"does not exist, fallback to"origin/master"with a deprecation warning. (#400)
π¦ Misc π¦
#406,#408,#411,#412,#413,#414,#416,#418,#419,#421,#429,#430,#431,#434,#446,#447
π towncrier 22.8.0 (2022-08-29) π
No significant changes since the previous release candidate. β¨
π towncrier 22.8.0.rc1 (2022-08-28) π
β¨ Features β¨
- Make the
checksubcommand succeed for branches that change the news file
This should enable the check subcommand to be used as a CI lint step and not fail when a pull request only modifies the configured news file (i.e. when the news file is being assembled for the next release). (#337)
- Added support to tables in toml settings, which provides a more intuitive way to configure custom types. (
#369) - The
towncrier createcommand line now has a new-m TEXTargument that is used to define the content of the newly created fragment. (#374)
π Bugfixes π
- The extra newline between the title and rendered content when using
--draftis no longer inserted. (#105) - The detection of duplicate release notes was fixed and recording changes of same version is no longer triggered.
Support for having the release notes for each version in a separate file is working again. This is a regression introduced in VERSION 19.9.0rc1. (#391)
π Improved Documentation π
- Improve
CONTRIBUTING.rstand add PR template. (#342) - Move docs too the main branch and document custom fragment types. (
#367) - The CLI help messages were updated to contain more information. (
#384)
π Deprecations and Removals π
- Support for all Python versions older than 3.7 has been dropped. (
#378)
π¦ Misc π¦
#292,#330,#366,#376,#377,#380,#381,#382,#383,#393,#399,#402
π towncrier 21.9.0 (2022-02-04) π
β¨ Features β¨
towncrier βversionwas added to the command line interface to show the product version. (#339)- Support Toml v1 syntax with tomli on Python 3.6+ (
#354)
psst, quick housekeeping: every#NNNin this part is that issue's number over athttps://github.com/twisted/towncrier/issues/NNNβ so if you ever wanna go read the tea, just plug the digits in π
π Bugfixes
- it used to write the title out twice when
title_formatwas specified?? not anymore, that's fixed (#346) - universal newlines are now disabled when reading TOML (#359)
π Misc
- #332, #333, #334, #338
towncrier 21.3.0 (2021-04-02)
no significant changes since the previous release candidate π«Ά nothing to see here, bestie
towncrier 21.3.0.rc1 (2021-03-21)
β¨ Features
- the issue number inside file names gets stripped down now, so you don't end up with cursed issue links like
#007(#126) - you can now define the project version and name right in the configuration file π which means towncrier works seamlessly with non-Python projects too (#165)
- news fragment file name parsing got a glow-up: names like
123.feature.1.extwork now, which is so handy when you want a proper extension (rst,md, whatever) so your editor does its syntax highlighting thing (#173) - brand new
--editoption on thecreatesubcommand β it launches an editor so you can type the contents of the newsfragment in (#275) - CPython 3.8 and 3.9 joined the automated test matrix and are officially supported, periodt (#291)
- when it goes looking for the project, it first checks for an existing importable instance β which saves you when the version is only available in the installed version and not the source (#297)
- building with PEP 517 is supported now (#314)
π Bugfixes
- configuration errors found while running the command line now trigger a message to
stderrand no longer show a traceback (#84) - if the newsfragment files couldn't be discovered, that's a configuration error now β loud and clear (#85)
- invoking towncrier as
python -m towncrierworks (#163) - the
checksubcommand defaults to UTF-8 encoding whensys.stdout.encodingisNone. this happens, for example, with Python 2 on GitHub Actions, or when the output is piped (#175) - specifying
title_formatdisables the default top line creation so nothing gets duplicated (#180)
π Improved Documentation
- the README now mentions that you can name your config file
towncrier.toml(in addition topyproject.toml) (#172) start_linewas corrected tostart_stringin the readme to match the long standing implementation (#277)
towncrier 19.9.0 (2021-03-20)
no significant changes π
towncrier 19.9.0rc1 (2019-09-16)
β¨ Features
- new
createsubcommand, which you can use to quickly create a news fragment command in the location defined by config (#4) - subcommands are supported now, which means the functionality of the
towncrierexecutable is replaced by thebuildsubcommand:
$ towncrier build --draft
and a new check subcommand is exposed β an alternative to calling the towncrier.check module manually:
$ towncrier check
calling towncrier without a subcommand results in a call to the build subcommand, to keep backwards compatibility. that may be removed in a future release (#144)
- towncrier's templating now allows configuration of the version header. CUSTOM TEMPLATE USERS PLEASE NOTE: you will need to add the version header information to your template! (#147)
- towncrier now accepts the
--configargument to specify a custom configuration file (#157) - there's now the option for
all_bullets = falsein the configuration. settingall_bulletstofalsemeans news fragments have to include the bullet point if they should be rendered as enumerations β otherwise they render directly (which means fragments can include a header π). you have to set this option to avoid the (incorrect) automatic indentation of multiline fragments that don't include bullet points. thesingle-file-no-bullets.rsttemplate is a whole example of using these options (#158) - the
single_fileoption can now be added to the configuration file. when it's set totrue, thefilenamekey becomes formattable with thename,version, andproject_dateformat variables β¨ which lets subsequent versions be written out to new files instead of appended to an existing one (#161) - you can now specify Towncrier-bundled templates in your configuration file. the available templates are
default,hr-between-versions(as used in attrs), andsingle-file-no-bullets(#162)
π Bugfixes
- newsfragment filenames with multiple dots are accepted now, like
fix-1.2.3.bugfix(#142)
π Deprecations and Removals
- the
--pyprojectoption fortowncrier checkis now replaced with--config, for consistency with the other commands (#162)
towncrier 19.2.0 (2019-02-15)
β¨ Features
- multiple fragments per issue/type pair are supported now π which extends the naming pattern of the fragments to
issuenumber.type(.counter), wherecounteris an optional integer (#119) - Python 2.7 is supported now (#121)
python -m towncrier.checknow accepts an option to give the configuration file location (#123)towncrier.checknow reports git output when it encounters a git failure (#124)
towncrier 18.6.0 (2018-07-05)
β¨ Features
python -m towncrier.check, which checks a Git branch for the presence of added newsfiles, made for use in a CI system (#75)wrapis now an optional configuration option (it'sFalseby default) and it controls the line wrapping of news files. towncrier also won't try to normalise (wipe newlines from) the input anymore β it just strips the leading and ending whitespace (#80)- towncrier can now be invoked by
python -m towncrier(#115)
π Deprecations and Removals
- towncrier now supports Python 3.5+ as a script runtime. Python 2.7 will not function (#80)
towncrier 18.5.0 (2018-05-16)
β¨ Features
- Python 3.3 is no longer supported (#103)
- made
packageoptional π when the version is passed on the command line, andtitle_formatdoesn't use the package name, and it isn't used for the path to the news fragments β then no package name is needed, so we shouldn't enforce it (#111)
π Bugfixes
- when cleaning up old newsfragments, if a newsfragment is named
123.feature.rst, remove that file instead of trying to remove the non-existent123.feature(#99) - if there are two newsfragments with the same name (example:
123.bugfix.rstand123.bugfix.rst~), raise an error instead of silently picking one at random (#101)
towncrier 17.8.0 (2017-08-19)
β¨ Features
- new option
issue_format. for example, you can use it to make the issue text in the NEWS file be formatted as ReST links to the issue tracker (#52) --yesoption added so you can run non-interactively (#56)- you can now name newsfragments like
123.feature.rst, or123.feature.txt, or123.feature.whatever.you.want, and towncrier will ignore the extension (#62) - new option in
pyproject.toml:underlines = ["=", "-", "~"]to specify the ReST underline hierarchy in towncrier's generated text (#63) - instead of sorting sections/types alphabetically (e.g.
"bugfix"before"feature"because"b"<"f"), sections/types now appear in the output in the same order they have in your config file (#70)
π Bugfixes
- when rewrapping text, don't break words or at hyphens β they might be inside a URL (#68)
π Deprecations and Removals
towncrier.iniconfig file support has been removed in favour ofpyproject.tomlconfiguration (#71)
towncrier 17.4.0 (2017-04-15)
π Misc
- #46
towncrier 17.1.0
π Bugfixes
- fix
--datebeing ignored (#43)
towncrier 16.12.0
π Bugfixes
- towncrier will now import the local version of the package and not the global one (#38)
β¨ Features
- allow configuration of the template file, title text and "magic comment" (#35)
- towncrier now uses
pyproject.toml, as defined in PEP-518 (#40)
towncrier 16.1.0 (2016-03-25)
β¨ Features
- ported to Python 2.7 (#27)
- towncrier now supports non-numerical news fragment names (#32)
π Bugfixes
- towncrier would spew an unhelpful exception if it failed importing your project while autodiscovering β now it doesn't (#22)
incrementalis now added as a runtime dependency for towncrier (#25)
π Misc
- #33
π towncrier 16.0.0 (2016-01-06)
β¨ Features
- towncrier now automatically puts a date beside the version as it is generated, using today's date. π
For repeatable builds, use the
--dateswitch and provide a date. For no date, use--date=. (#11) - towncrier will now add the version logs after
.. towncrier release notes start, if it is in the file, allowing you to preserve text at the top of the file. (#15)
π Improved Documentation
- The README now mentions how to manually provide the version number, for non-Py3 compatible projects. (#19)
π towncrier 15.1.0
β¨ Features
- towncrier now supports reading
__version__attributes that are tuples of numbers (e.g.(15, 4, 0)). (#3) - towncrier now has support for testing via Tox and each commit is now ran on Travis CI. (#6)
π Bugfixes
- towncrier now defaults to the current working directory for the
package_dirsettings variable. (#2)
π towncrier 15.0.0
β¨ Features
- Basic functionality has been implemented. This includes configuring towncrier to find your project, having a set of preconfigured news fragment categories, and assembling a newsfile from them. (#1)
π«Ά Contributing to Towncrier
Want to contribute to this project? Great! We'd love to hear from you! π
As a developer and user, you probably have some questions about our project and how to contribute. In this article, we try to answer these and give you some recommendations.
π¦ Ways to communicate and contribute
There are several options to contribute to this project:
- Open a new topic on our GitHub Discussions <https://github.com/twisted/towncrier/discussions> page.
Tell us about your ideas or ask questions there. Discuss with us the next Towncrier release.
- Help or comment on our GitHub issues <https://github.com/twisted/towncrier/issues> tracker.
There are certainly many issues where you can help with your expertise. Or you would like to share your ideas with us.
- Open a new issue using GitHub issues <https://github.com/twisted/towncrier/issues> tracker.
If you found a bug or have a new cool feature, describe your findings. Try to be as descriptive as possible to help us understand your issue.
- Check out the Libera #twisted IRC channel or Twisted Gitter <https://gitter.im/twisted/twisted>.
If you prefer to discuss some topics personally, you may find the IRC or Gitter channels interesting. They are bridged.
- Modify the code.
If you would love to see the new feature in the next release, this is probably the best way. π
πΈ Modifying the code
The source code is managed using Git and is hosted on GitHub:
https://github.com/twisted/towncrier
git@github.com:twisted/towncrier.git
We recommend the following workflow:
- Fork our project <https://github.com/twisted/towncrier/fork> on GitHub.
- Clone your forked Git repository (replace
GITHUB_USERwith your account name on GitHub):
$ git clone git@github.com:GITHUB_USER/towncrier.git
- Prepare a pull request:
a. Create a new branch with:
$ git checkout -b <BRANCH_NAME>
b. Write your test cases and run the complete test suite, see the section Running the test suite for details.
c. Document any user-facing changes in one of the /docs/ files. Use one sentence per line <https://rhodesmill.org/brandon/2012/one-sentence-per-line/>.
d. Create a news fragment in src/towncrier/newsfragments/ describing the changes and containing information that is of interest to end-users. Use one sentence per line <https://rhodesmill.org/brandon/2012/one-sentence-per-line/> here, too. You can use the towncrier CLI to create them; for example towncrier create 1234.bugfix
Use one of the following types:
featurefor new featuresbugfixfor bugfixesdocfor improvements to documentationremovalfor deprecations and removalsmiscfor everything else that is linked but not shown in ourNEWS.rstfile. Use this for pull requests that don't affect end-users and leave them empty.
e. Create a pull request <https://github.com/twisted/towncrier/pulls>. Describe in the pull request what you did and why. If you have open questions, ask. (optional) Allow team members to edit the code on your PR.
- Wait for feedback. If you receive any comments, address these.
- After your pull request is merged, delete your branch.
π§ Running the test suite
We use the twisted.trial <https://github.com/twisted/trac-wiki-archive/blob/trunk/TwistedTrial.mediawiki> module and nox <https://nox.thea.codes/> to run tests against all supported Python versions and operating systems.
The following list contains some ways how to run the test suite:
- To install this project into a virtualenv along with the dependencies necessary to run the tests and build the documentation:
$ pip install -e .[dev]
- To run the tests, use
triallike so:
$ trial towncrier
- To investigate and debug errors, use the
trialcommand like this:
$ trial -b towncrier
This will invoke a PDB session. If you press c it will continue running the test suite until it runs into an error.
- To run all tests against all supported versions, install
noxand use:
$ nox
You may want to add the --no-error-on-missing-interpreters option to avoid errors when a specific Python interpreter version couldn't be found.
- To get a complete list of the available targets, run:
$ nox -l
- To run only a specific test only, use the
towncrier.test.FILE.CLASS.METHODsyntax, for example:
$ nox -e tests -- towncrier.test.test_project.InvocationTests.test_version
- To run some quality checks before you create the pull request, we recommend using this call:
$ nox -e pre_commit check_newsfragment
- Or enable
pre-commitas a git hook:
$ pip install pre-commit
$ pre-commit install
Please note: If the test suite works innox, but doesn't by callingtrial, it could be that you've got GPG-signing active for git commits which fails with our dummy test commits.
π Release Process
Note: Commands are written with Linux in mind and a venv located invenv/. Adjust per your OS and virtual environment location. For example, on Windows with an environment in the directorymyenv/the Python command would bemyenv/scripts/python.
Towncrier uses CalVer <https://calver.org/> of the form YY.MM.micro with the micro version just incrementing.
Before the final release, a set of release candidates are released.
π Release candidate
Create a release branch with a name of the form release-19.9.0 starting from the main branch. The same branch is used for the release candidate and the final release. In the end, the release branch is merged into the main branch.
Update the version to the release candidate with the first being rc1 (as opposed to 0). In pyproject.toml the version is set using a PEP440 compliant string:
version = "19.9.0rc1"
Use towncrier to generate the news release NEWS file, but first, make sure the new version is installed:
venv/bin/pip install -e .
venv/bin/towncrier build --yes
Commit and push to the primary repository, not a fork. It is important to not use a fork so that pushed tags end up in the primary repository, server provided secrets for publishing to PyPI are available, and maybe more.
Create a PR named in the form Release 19.9.0. The same PR will be used for the release candidates and the final release.
Wait for the tests to be green. Start with the release candidates. Create a new release candidate using GitHub New release UI <https://github.com/twisted/towncrier/releases/new>.
- Choose a tag: Type
19.9.0rc1and selectCreate new tag on publish. - Target: Search for the release branch and select it.
- Title: "Towncrier 19.9.0rc1".
- Set the content based on the
NEWSfile (for now in RST format). - Make sure to check
This is a pre-release. - Click
Publish release
This will trigger the PyPI release candidate.
Wait for the PyPI version to be published and then request a review for the PR from the twisted/twisted-contributors team.
In the PR request, you can give the link to the PyPI download and the documentation pages. The documentation link is also available as part of the standard Read The Docs PR checks.
Notify the release candidate over IRC or Gitter to gain more attention. In the PR comments, you can also mention anyone who has asked for a release.
We don't create discussion for pre-releases. Any discussions before the final release, can go on the PR itself.
For now, the GitHub release text is reStructuredText as it's easy to copy and paste. In the future we might create a separate Markdown version.
π Release candidate publish failures
The PyPI publish process is automatically triggered when a tag is created.
The publish is skipped for PRs, so we can check that the automated process works only a release time. It can happen for the automated publish process to fail.
As long as the package was not published to PyPI, do the followings:
- Manually delete the candidate release from GitHub releases
- Manually delete the tag for the release candidate
Try to fix the issue and trigger the same release candidate again.
Once the package is published on PyPI, do not delete the release or the tag. Proceed with create a new release candidate instead.
π Final release
Once the PR is approved, you can trigger the final release.
Update the version to the final version. In pyproject.toml the version is set using a PEP440 compliant string:
version = "19.9.0"
Manually update the NEWS.rst file to include the final release version and date. Usually it will look like this. This will replace the release candidate section:
towncrier 19.9.0 (2019-09-01)
=============================
No significant changes since the previous release candidate.
Commit and push the change. Wait for the tests to be green.
Trigger the final release using GitHub Release GUI.
Similar to the release candidate, with the difference:
- tag will be named
19.9.0 - the target is the same branch
- Title will be
towncrier 19.9.0 - Content can be the content of the final release (RST format).
- Check
Set as the latest release. - Check
Create a discussion for this release. - Click
Publish release
No need for another review request.
- Bump the version to the dev version, bestie. In
pyproject.tomlthe version lives in a PEP440 compliant string, like this:
version = β19.9.0.dev0β
- Commit and push those changes. π«Ά
- Merge the commit into the main branch, and do NOT use squash. Like, seriously, no squash. π ββοΈ
Okay so here's the tea on why, because this one matters: we tag the release based on a commit from the release branch. If we merge with squash, the release tag commit will no longer be found in the main branch history. With a squash merge, the whole branch history is lost. That causes thepre-commit autoupdateto fail. See PR590https://github.com/twisted/towncrier/pull/590for more details.
- You can announce the release over IRC, Gitter, or the Twisted mailing list. Go tell the people, they'll be so happy. β¨
- Done. π
π PROJECT LINKS π
- PyPI:
https://pypi.org/project/towncrier/ - Documentation:
https://towncrier.readthedocs.io/ - Release Notes:
https://github.com/twisted/towncrier/blob/trunk/NEWS.rst - License: MIT
https://github.com/twisted/towncrier/blob/trunk/LICENSE
π Author
Amber Brown
π Copyright
2026, Towncrier contributors. Ver 25.8.0
okay and that's the whole page, you made it to the end, iconic behavior honestly π
this version of the page is TOWNCRIER(7), coming at you from 25.8.0, dated Aug 17, 2026 β thank you for reading, ily, go make a changelog queen π¦πΈ
π¦ where this page comes from
- ubuntu stonking: towncrier-doc 25.8.0-2 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms