TOWNCRIER(7)

πŸ“¦ version 25.8.0, in ubuntu stonking

πŸ‘‘ 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):

  1. manually pass --version=<myversionhere> when interacting with towncrier.
  2. set a value for the version option in your configuration file.
  3. for Python projects with a package key 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:

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.toml or pyproject.toml file. 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 in PATH/newsfragments and the news file is built in PATH/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 the build command 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 $EDITOR to edit the news fragment right away. Default: $EDITOR will 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 of origin/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 a package key, if left empty then the name will be automatically determined from the package key. Defaults to the key [project.name] in pyproject.toml (if present), otherwise defaults to the empty string "".
  • version β€” the version of your project. for Python projects that provide the package key, 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 a package key, the default is a newsfragments directory within the package. otherwise the default is a newsfragments directory relative to either the directory passed as --dir or (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 unless filename ends 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 unless filename ends 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 of 79.

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 with towncrier create ends with an empty line.

true by default.

  • create_add_extension β€” adds the filename option extension to news fragment files created with towncrier create if an extension wasn't explicitly provided.

true by default.

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
  • .keep
  • README
  • README.md
  • README.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 configured directory. 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 the towncrier check command.

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 the towncrier check command.

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 check command now has a --staged flag 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.toml file, if no explicit values are defined for [tool.towncrier.name|package], they now fallback to the value of [project.name]. (#687)
  • The directory configuration option is no longer required β€” it can now be generated based on the value from the name configuration. (#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 .gitkeep as an ignored filename. (#643)
  • The config ignore option now supports wildcard matching via fnmatch. (#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_template failed 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 an issue_pattern is configured. Orphan news fragments are now exempt from issue_pattern checks. (#655)

Deprecations and Removals πŸ’€

  • Moved the towncrier version definition from src/towncrier/_version.py to pyproject.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 build now 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 create creates now uses the correct .rst or .md extension, which may help your editor with syntax highlighting. (#594)
  • Running towncrier will now traverse back up directories looking for the configuration file. (#601)
  • The towncrier create action now uses the sections defined in your config (either interactively, or via the new --section option). (#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_format configuration option now uses a markdown format for markdown templates. (#610)
  • Newsfragment categories can now be marked with check = false, which makes them get ignored in towncrier check. (#617)
  • towncrier check will 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 name configuration 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 filename option 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 πŸ”§

  • build now 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.feature are now associated with the issue fix-1.2.3. In previous versions they were incorrectly associated to issue 3. (#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-group package 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 create use 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 check without 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.html to point to Tutorial instead. (#504)
  • Add a note about the build command's --version requiring 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 --keep option 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 check is 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 check subcommand 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 create command line now has a new -m TEXT argument 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 --draft is 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.rst and 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 –version was 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 #NNN in this part is that issue's number over at https://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_format was 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.ext work 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 --edit option on the create subcommand β€” 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 stderr and 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 towncrier works (#163)
  • the check subcommand defaults to UTF-8 encoding when sys.stdout.encoding is None. this happens, for example, with Python 2 on GitHub Actions, or when the output is piped (#175)
  • specifying title_format disables 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 to pyproject.toml) (#172)
  • start_line was corrected to start_string in 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 create subcommand, 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 towncrier executable is replaced by the build subcommand:
$ 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 --config argument to specify a custom configuration file (#157)
  • there's now the option for all_bullets = false in the configuration. setting all_bullets to false means 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. the single-file-no-bullets.rst template is a whole example of using these options (#158)
  • the single_file option can now be added to the configuration file. when it's set to true, the filename key becomes formattable with the name, version, and project_date format 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), and single-file-no-bullets (#162)

πŸ› Bugfixes

  • newsfragment filenames with multiple dots are accepted now, like fix-1.2.3.bugfix (#142)

πŸ’€ Deprecations and Removals

  • the --pyproject option for towncrier check is 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), where counter is an optional integer (#119)
  • Python 2.7 is supported now (#121)
  • python -m towncrier.check now accepts an option to give the configuration file location (#123)
  • towncrier.check now 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)
  • wrap is now an optional configuration option (it's False by 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 package optional πŸ’… when the version is passed on the command line, and title_format doesn'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-existent 123.feature (#99)
  • if there are two newsfragments with the same name (example: 123.bugfix.rst and 123.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)
  • --yes option added so you can run non-interactively (#56)
  • you can now name newsfragments like 123.feature.rst, or 123.feature.txt, or 123.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.ini config file support has been removed in favour of pyproject.toml configuration (#71)

towncrier 17.4.0 (2017-04-15)

🌈 Misc

  • #46

towncrier 17.1.0

πŸ› Bugfixes

  • fix --date being 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)
  • incremental is 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 --date switch 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_dir settings 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:

Tell us about your ideas or ask questions there. Discuss with us the next Towncrier release.

There are certainly many issues where you can help with your expertise. Or you would like to share your ideas with us.

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.

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:

  1. Fork our project <https://github.com/twisted/towncrier/fork> on GitHub.
  2. Clone your forked Git repository (replace GITHUB_USER with your account name on GitHub):
$ git clone git@github.com:GITHUB_USER/towncrier.git
  1. 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:

  • feature for new features
  • bugfix for bugfixes
  • doc for improvements to documentation
  • removal for deprecations and removals
  • misc for everything else that is linked but not shown in our NEWS.rst file. 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.

  1. Wait for feedback. If you receive any comments, address these.
  2. 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 trial like so:
$ trial towncrier
  • To investigate and debug errors, use the trial command 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 nox and 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.METHOD syntax, 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-commit as a git hook:
$ pip install pre-commit
$ pre-commit install
Please note: If the test suite works in nox, but doesn't by calling trial, 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 in venv/. Adjust per your OS and virtual environment location. For example, on Windows with an environment in the directory myenv/ the Python command would be myenv/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.0rc1 and select Create new tag on publish.
  • Target: Search for the release branch and select it.
  • Title: "Towncrier 19.9.0rc1".
  • Set the content based on the NEWS file (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.toml the 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 the pre-commit autoupdate to fail. See PR590 https://github.com/twisted/towncrier/pull/590 for 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. πŸŽ€


πŸ‘‘ Author

Amber Brown

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