Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default markdown template #483

Merged
merged 38 commits into from
May 4, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
aee6fd2
Add default markdown template
SmileyChris Feb 22, 2023
cc27dea
Update tutorial.rst to mention markdown
SmileyChris Feb 22, 2023
c952d17
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 1, 2023
94d8a26
Typing fixes, fix template issue, full test coverage
SmileyChris Mar 21, 2023
d2bb513
Add newsfragment
SmileyChris Mar 21, 2023
63b6257
Merge branch 'trunk' into feature/default-markdown-template
hynek Apr 4, 2023
c7cd296
Apply suggestions from code review
SmileyChris Apr 14, 2023
9edaf7b
Update docs/tutorial.rst
SmileyChris Apr 27, 2023
09b68f0
Update docs/configuration.rst
SmileyChris Apr 27, 2023
6d97f57
towncrier doesn't use .rst extensions for fragments
SmileyChris Apr 27, 2023
744387e
Applying suggested changes
SmileyChris Apr 27, 2023
1aaea1b
"Version" doc improvements
SmileyChris Apr 27, 2023
1b22755
Another change for consistency in docs
SmileyChris Apr 27, 2023
a9e07fa
Remove ordereddict and redundant os.mkdirs
SmileyChris Apr 27, 2023
89b72fd
Make Config.underlines a tuple by default
SmileyChris Apr 27, 2023
7740059
Fix what Github suggestions messed up
SmileyChris Apr 27, 2023
be669c0
Fix dicts that were OrderedDicts
SmileyChris May 1, 2023
d2676a9
Fix documentation link
SmileyChris May 1, 2023
438636a
Re-add the required os.mkdirs and abstract the common use case in tes…
SmileyChris May 1, 2023
d1d30b2
Merge remote-tracking branch 'upstream/trunk' into feature/default-ma…
SmileyChris May 2, 2023
64ac558
Use Python 3.10 compatible importlib_resources
SmileyChris May 2, 2023
07dab17
Fix markdown in fragment
SmileyChris May 2, 2023
7df907c
Fix comment to show what fragments actually returns now
SmileyChris May 2, 2023
cfe0595
Importlib test helper can keep using builtin
SmileyChris May 2, 2023
ed62910
Revert to old template open method to try and fix a windows error
SmileyChris May 2, 2023
8d429ac
Make ruff's sort work the same as actual isort
SmileyChris May 2, 2023
bb3c6b7
Make config.template use a tuple instead of a string if it's a resour…
SmileyChris May 2, 2023
c517e65
Improve docs for directory configuration key
SmileyChris May 3, 2023
8c4ebea
Document template configuration key
SmileyChris May 3, 2023
a6afc70
Change default start_string configuration when using markdown
SmileyChris May 3, 2023
d50a2d7
Merge commit '622ba4f8806531cb69ac0fb5364cecd69a5752dc' into feature/…
SmileyChris May 3, 2023
ea3aa66
Change newsfile docs to use `code-block`s
SmileyChris May 4, 2023
7e27001
Use helpers read/write for new tests
SmileyChris May 4, 2023
e0af6bf
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 4, 2023
d479924
Better configuration doc formatting
SmileyChris May 4, 2023
fb9f6cc
Document Section Path Behaviour
SmileyChris May 4, 2023
446a723
Improve configuration docs re: python projects
SmileyChris May 4, 2023
4fd6453
Fix test that was altered to use dedent
SmileyChris May 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 97 additions & 77 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,116 @@ Configuration Reference
=======================

``towncrier`` has many knobs and switches you can use, to customize it to your project's needs.
The setup in the `Quick Start <quickstart.html>`_ doesn't touch on many, but this document will detail each of these options for you!
The setup in the `Tutorial <tutorial.html>`_ doesn't touch on many, but this document will detail each of these options for you!
SmileyChris marked this conversation as resolved.
Show resolved Hide resolved

For how to perform common customization tasks, see `Customization <customization/index.html>`_.

``[tool.towncrier]``
--------------------

All configuration for ``towncrier`` sits inside ``pyproject.toml``, under the ``tool.towncrier`` namespace.
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 configuration for a Python project looks like this:

.. code-block:: toml

# pyproject.toml

[tool.towncrier]
package = "myproject"

A minimal configuration for a non-Python project looks like this:

.. code-block:: toml

# towncrier.toml

[tool.towncrier]
name = "My Project"
version = "1.0.0"
SmileyChris marked this conversation as resolved.
Show resolved Hide resolved
directory = "newsfragments"

Top level keys
~~~~~~~~~~~~~~

- ``directory`` -- If you are not storing your news fragments in your Python package, or aren't using Python, this is the path to where your newsfragments will be put.
- ``filename`` -- The filename of your news file.
``NEWS.rst`` by default.
- ``package`` -- The package name of your project.
(Python projects only)
- ``package_dir`` -- The folder your package lives. ``./`` by default, some projects might need to use ``src``.
(Python projects only)
- ``template`` -- Path to an alternate template for generating the news file, if you have one.
- ``start_string`` -- The magic string that ``towncrier`` looks for when considering where the release notes should start.
``.. towncrier release notes start`` by default.
- ``title_format`` -- A format string for the title of your project.
``{name} {version} ({project_date})`` by default.
- ``issue_format`` -- A format string for rendering the issue/ticket number in newsfiles.
``#{issue}`` by default.
- ``underlines`` -- The characters used for underlining headers.
- **``name``** -- The name of your project. If empty and the ``package`` key is provided, the name will be automatically determined.
``""`` by default.
- **``version``** -- The version of your project. Mandatory except for Python projects that provide the ``package`` key and want the version to be automatically determined from a ``__version__`` variable in the package's module.
SmileyChris marked this conversation as resolved.
Show resolved Hide resolved
- **``directory``** -- The directory storing you news fragments. Mandatory except for Python projects (where the default is a ``newsfragments`` directory within the package).
SmileyChris marked this conversation as resolved.
Show resolved Hide resolved
SmileyChris marked this conversation as resolved.
Show resolved Hide resolved
- **``filename``** -- The filename of your news file.
``"NEWS.rst"`` by default.
- **``template``** -- Path to the template for generating the news file. If the path starts with ``towncrier:``, it is interpreted as a template bundled with ``towncrier``.
SmileyChris marked this conversation as resolved.
Show resolved Hide resolved
``"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"`` by default.
- **``title_format``** -- A format string for the title of your project. The explicit value of ``False`` will disable the title entirely. Any other empty value means the template should render the title (the bundled templates use ``<name> <version> (<date>)``). Strings should use the following keys to render the title dynamically: ``{name}``, ``{version}``, and ``{project_date}``.
SmileyChris marked this conversation as resolved.
Show resolved Hide resolved
``""`` by default.
- **``issue_format``** -- A format string for rendering the issue/ticket number in newsfiles. If none, the issues are rendered as ``#<issue>`` if for issues that are integers, or just ``<issue>`` otherwise. Use the ``{issue}`` key in your string render the issue number, for example Markdown projects may want to use ``"[{issue}]: https://<your bug tracker>/{issue}"``.
SmileyChris marked this conversation as resolved.
Show resolved Hide resolved
``None`` by default.
- **``underlines``** -- The characters used for underlining headers. Not used in the bundled Markdown template.
SmileyChris marked this conversation as resolved.
Show resolved Hide resolved
``["=", "-", "~"]`` by default.
- **``wrap``** -- Boolean value indicating whether to wrap news fragments to a width of 79.
SmileyChris marked this conversation as resolved.
Show resolved Hide resolved
``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 false, the ``filename`` should use the following keys to render the filenames dynamically: ``{name}``, ``{version}``, and ``{project_date}``.
SmileyChris marked this conversation as resolved.
Show resolved Hide resolved
``true`` by default.
- **``orphan_prefix``** -- The prefix used for orphaned news fragments.
``"+"`` by default.

Extra top level keys for Python projects
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

- **``package``** -- The package name of your project.
- **``package_dir``** -- The folder your package lives.
``"."`` by default, some projects might need to use ``"src"``.


Sections
--------

``towncrier`` supports splitting fragments into multiple sections, each with its own news of fragment types.

Add an array of tables 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.
SmileyChris marked this conversation as resolved.
Show resolved Hide resolved

For example:

.. code-block:: toml

[[tool.towncrier.section]]
name = "Main Platform"
path = ""

[[tool.towncrier.section]]
name = "Secondary"
path = "secondary"


Custom fragment types
---------------------
``towncrier`` allows defining custom fragment types.
Custom fragment types will be used instead ``towncrier`` default ones, they are not combined.

There are two ways to add custom fragment types.
``towncrier`` hase the following default fragment types: ``feature``, ``bugfix``, ``doc``, ``removal``, and ``misc``.
SmileyChris marked this conversation as resolved.
Show resolved Hide resolved

You can use either of the two following method to define custom types instead (you will need to redefine any of the default types you want to use).

Defining Custom Fragment Types With a TOML Mapping
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Users can configure each of their own custom fragment types by adding tables to
the pyproject.toml named ``[tool.towncrier.fragment.<a custom fragment type>]``.
Use TOML tables (alphabetical order)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

These tables may include the following optional keys:
Adding tables to your ``.toml`` configuration file named **``[tool.towncrier.fragment.<a custom fragment type>]``**.

* ``name``: The description of the fragment type, as it must be included in the news file.
If omitted, it defaults to its fragment type, but capitalized.
* ``showcontent``: Whether if the fragment contents should be included in the news file. If omitted, it defaults to ``true``
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.

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:

Expand All @@ -70,25 +129,23 @@ For example, if you want your custom fragment types to be ``["feat", "fix", "cho

.. warning::

Since TOML mappings aren't ordered, the sections are always rendered alphabetically.
Since TOML mappings aren't ordered, types defined using this method are always rendered alphabetically.


Defining Custom Fragment Types With an Array of TOML Tables
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Use a TOML Array (defined order)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Users can create their own custom fragment types by adding an array of
tables to the pyproject.toml named ``[[tool.towncrier.type]]``.
Add an array of tables to your ``.toml`` configuration file named **``[[tool.towncrier.type]]``**.

If you use this way to configure custom fragment types, please note that ``fragment_types`` must be empty or not provided.
If you use this way to configure custom fragment types, ensure there is no ``tool.towncrier.fragment`` table.

Each custom type (``[[tool.towncrier.type]]``) has the following
mandatory keys:
Each table within this array has the following mandatory keys:

* ``directory``: The type / category of the fragment.
* ``name``: The description of the fragment type, as it must be included
- **``directory``** -- The type / category of the fragment.
- **``name``** -- The description of the fragment type, as it must be included
in the news file.
* ``showcontent``: Whether if the fragment contents should be included in the
news file.
- **``showcontent``** -- A boolean value indicating whether the fragment contents should be included in the news file.
``true`` by default.

For example:

Expand All @@ -104,40 +161,3 @@ For example:
directory = "chore"
name = "Other Tasks"
showcontent = false


All Options
-----------

``towncrier`` has the following global options, which can be specified in the toml file:

.. code-block:: toml

[tool.towncrier]
package = ""
package_dir = "."
single_file = true # if false, filename is formatted like `title_format`.
filename = "NEWS.rst"
directory = "directory/of/news/fragments"
version = "1.2.3" # project version if maintained separately
name = "arbitrary project name"
template = "path/to/template.rst"
start_string = "Text used to detect where to add the generated content in the middle of a file. Generated content added after this text. Newline auto added."
title_format = "{name} {version} ({project_date})" # or false if template includes title
issue_format = "format string for {issue} (issue is the first part of fragment name)"
underlines = "=-~"
wrap = false # Wrap text to 79 characters
all_bullets = true # make all fragments bullet points
orphan_prefix = "+" # Prefix for orphan news fragment files, set to "" to disable.

If ``single_file`` is set to ``true`` or unspecified, all changes will be written to a single fixed newsfile, whose name is literally fixed as the ``filename`` option.
In each run of ``towncrier build``, content of new changes will append at the top of old content, and after ``start_string`` if the ``start_string`` already appears in the newsfile.
If the corresponding ``top_line``, which is formatted as the option 'title_format', already exists in newsfile, ``ValueError`` will be raised to remind you "already produced newsfiles for this version".

If ``single_file`` is set to ``false`` instead, each versioned ``towncrier build`` will generate a separate newsfile, whose name is formatted as the pattern given by option ``filename``.
For example, if ``filename="{version}-notes.rst"``, then the release note with version "7.8.9" will be written to the file "7.8.9-notes.rst".
If the newsfile already exists, its content will be overwritten with new release note, without throwing a ``ValueError`` warning.

If ``title_format`` is unspecified or an empty string, the default format will be used.
If set to ``false``, no title will be created.
This can be useful if the specified template creates the title itself.
4 changes: 3 additions & 1 deletion docs/tutorial.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Tutorial
========

This tutorial assumes you have a Python project with a *reStructuredText* (rst) news file (also known as changelog) file that you wish to use ``towncrier`` on, to generate its news file.
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>`_.

Install from PyPI::
Expand Down Expand Up @@ -144,6 +144,8 @@ You should get an output similar to this::

- #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.
SmileyChris marked this conversation as resolved.
Show resolved Hide resolved


Producing News Files In Production
----------------------------------
Expand Down
13 changes: 11 additions & 2 deletions src/towncrier/_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,12 +255,15 @@ def render_fragments(

jinja_template = Template(template, trim_blocks=True)

data: dict[str, dict[str, dict[str, list[str]]]] = OrderedDict()
data: dict[str, dict[str, dict[str, list[str]]]] = {}
issues_by_category: dict[str, dict[str, list[str]]] = {}

for section_name, section_value in fragments.items():
data[section_name] = OrderedDict()
data[section_name] = {}
issues_by_category[section_name] = {}

for category_name, category_value in section_value.items():
category_issues: set[str] = set()
# Suppose we start with an ordering like this:
#
# - Fix the thing (#7, #123, #2)
Expand All @@ -273,6 +276,7 @@ def render_fragments(
entries = []
for text, issues in category_value.items():
entries.append((text, sorted(issues, key=issue_key)))
category_issues.update(issues)

# Then we sort the lines:
#
Expand All @@ -290,6 +294,10 @@ def render_fragments(
categories[text] = rendered

data[section_name][category_name] = categories
issues_by_category[section_name][category_name] = [
render_issue(issue_format, i)
for i in sorted(category_issues, key=issue_key)
]

done = []

Expand All @@ -311,6 +319,7 @@ def get_indent(text: str) -> str:
versiondata=versiondata,
top_underline=top_underline,
get_indent=get_indent, # simplify indentation in the jinja template.
issues_by_category=issues_by_category,
)

for line in res.split("\n"):
Expand Down
Loading