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

Add initial Towncrier integration #469

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/chronographer.yml
@@ -0,0 +1,11 @@
---
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a config for a GitHub App that is able to help with checking whether a change fragment is present in a given PR. It allows skipping this requirement by labeling a PR and for PRs created by certain robots. It needs to be installed separately from the PR but I've copied the config just in case.

enforce_name:
suffix: .md
exclude:
bots:
- dependabot-preview
- dependabot
- patchback
humans:
- pyup-bot
...
18 changes: 18 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,23 @@
# Changelog

[//]: # (DO-NOT-REMOVE-versioning-promise-START)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

A marker for inclusion in Sphinx


```{note}
The change notes follow [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
except for the title formatting, and this project adheres to [Semantic
Versioning](https://semver.org/spec/v2.0.0.html).
```

<!--
Do *NOT* manually add changelog entries here!
This changelog is managed by Towncrier and is built at release time.
See https://myst-parser.rtfd.io/en/latest/develop/contributing#adding-change-notes-with-your-prs
for details. Or read
https://github.com/executablebooks/MyST-Parser/tree/master/docs/changelog-fragments.d#adding-change-notes-with-your-prs
-->

<!-- towncrier release notes start -->
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Below this marker, Towncrier will inject the release entry using the j2 template.


## 0.18.1 - 2022-27-09

Full Changelog: [v0.18.0...v0.18.1](https://github.com/executablebooks/MyST-Parser/compare/v0.18.0...v0.18.1)
Expand Down
31 changes: 31 additions & 0 deletions docs/changelog-fragments.d/.CHANGELOG-TEMPLATE.md.j2
@@ -0,0 +1,31 @@

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the template Towncrier is set up to use. You can change virtually anything. Changing it won't affect the historic entries because Towncrier only adds new ones and doesn't touch anything that is already present in CHANGELOG.md.

{% for section, _ in sections.items() %}
{% set title_prefix = underlines[0] %}
{% if section %}
{{ title_prefix }} {{ section }}
{% set title_prefix = underlines[1] %}
{% endif %}

{% if sections[section] %}
{% for category, val in definitions.items() if category in sections[section] %}
{{ title_prefix }} {{ definitions[category]['name'] }}
{% if definitions[category]['showcontent'] %}

{% for text, values in sections[section][category].items() %}
* {{ text }}\
({{ values|join(',\n ') }})
{% endfor -%}

{% else %}
* {{ sections[section][category]['']|join(', ') }}
{% endif %}

{% if sections[section][category]|length == 0 %}
No significant changes.
{% endif %}
{% endfor %}

{% else %}
No significant changes.
{% endif %}
{% endfor %}
25 changes: 25 additions & 0 deletions docs/changelog-fragments.d/.gitignore
@@ -0,0 +1,25 @@
*
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a safety thing, so undesired files can't be included by accident.

!.CHANGELOG-TEMPLATE.md.j2
!.gitignore
!README.md
!*.bugfix
!*.bugfix.md
!*.bugfix.*.md
!*.breaking
!*.breaking.md
!*.breaking.*.md
!*.deprecation
!*.deprecation.md
!*.deprecation.*.md
!*.doc
!*.doc.md
!*.doc.*.md
!*.feature
!*.feature.md
!*.feature.*.md
!*.internal
!*.internal.md
!*.internal.*.md
!*.misc
!*.misc.md
!*.misc.*.md
4 changes: 4 additions & 0 deletions docs/changelog-fragments.d/454.misc.md
@@ -0,0 +1,4 @@
Added changelog fragment management infrastructure using [Towncrier]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a changelog fragment like the PR authors would be required to add. Its content should be markdown, its name should start with a PR or an issue number, followed by a type specified in pyproject.toml, then there's an optional part number for cases when you want to have multiple entries per issue/PR or the same type, and then there's an optional .md extension (it will be mandatory with the GH App config, if activated).

-- by {user}`webknjaz`
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The user role is set up on the Sphinx side and it links the person's GH profile or Sponsors page (if exists).


[Towncrier]: https://github.com/twisted/towncrier
79 changes: 79 additions & 0 deletions docs/changelog-fragments.d/README.md
@@ -0,0 +1,79 @@
<!-- markdownlint-disable first-line-heading -->

(_myst_parser_adding_changelog_fragments)=

## Adding change notes with your PRs
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here's a list of recommendations pasted from other projects I've contributed a similar setup to (namely, like setuptools/aiohttp/ansible-navigator/ansible-pylibssh etc). It could be improved over time.

Note that this document is also rendered on GitHub when people navigate to the folder via web UI.


It is very important to maintain a log for news of how
updating to the new version of the software will affect
end-users. This is why we enforce collection of the change
fragment files in pull requests as per [Towncrier philosophy].

The idea is that when somebody makes a change, they must record
the bits that would affect end-users only including information
that would be useful to them. Then, when the maintainers publish
a new release, they'll automatically use these records to compose
a change log for the respective version. It is important to
understand that including unnecessary low-level implementation
related details generates noise that is not particularly useful
to the end-users most of the time. And so such details should be
recorded in the Git history rather than a changelog.

## Alright! So how do I add a news fragment?

To submit a change note about your PR, add a text file into the
`docs/changelog-fragments.d/` folder. It should contain an
explanation of what applying this PR will change in the way
end-users interact with the project. One sentence is usually
enough but feel free to add as many details as you feel necessary
for the users to understand what it means.

**Use the past tense** for the text in your fragment because,
combined with others, it will be a part of the "news digest"
telling the readers **what changed** in a specific version of
the library *since the previous version*. You should also use
[MyST Markdown] syntax for highlighting code (inline or block),
linking parts of the docs or external sites.
At the end, sign your change note by adding ```-- by
{user}`github-username``` (replace `github-username` with
your own!).

Finally, name your file following the convention that Towncrier
understands: it should start with the number of an issue or a
PR followed by a dot, then add a patch type, like `feature`,
`bugfix`, `doc`, `misc` etc., and add `.md` as a suffix. If you
need to add more than one fragment, you may add an optional
sequence number (delimited with another period) between the type
and the suffix.

## Examples for changelog entries adding to your Pull Requests

File `docs/changelog-fragments.d/666.doc.md`:

```md
Added a `{user}` role to Sphinx config -- by {user}`webknjaz`
```

File `docs/changelog-fragments.d/116.feature.md`:

```md
Added support for nested module options (suboptions)
-- by {user}`tomaciazek`
```

File `docs/changelog-fragments.d/140.bugfix.md`:

```md
Implemented opening standalone Ansible files that have no workspace
associated -- by {user}`ganeshrn`
```

```{tip}
See `pyproject.toml` for all available categories
(`tool.towncrier.type`).
```

[MyST Markdown]:
https://myst-parser.rtfd.io/en/latest/syntax/syntax.html
[Towncrier philosophy]:
https://towncrier.rtfd.io/en/actual-freaking-docs/#philosophy
57 changes: 54 additions & 3 deletions docs/conf.py
Expand Up @@ -5,17 +5,39 @@
# https://www.sphinx-doc.org/en/master/usage/configuration.html

from datetime import date
from functools import partial
from pathlib import Path

from setuptools_scm import get_version
Copy link
Member

Choose a reason for hiding this comment

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

I note that setuptools_scm says "It is discouraged to use setuptools_scm from Sphinx itself": https://github.com/pypa/setuptools_scm/#usage-from-sphinx
thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds reasonable. Although, this code is what I use right now in many places and it works. Reading the version from metadata means that the project needs to be pip installed into the same venv where sphinx is. It would subsequently pull more dependencies which is not possible or desirable in some cases.
OTOH it's not possible to switch the solution right now because MyST does not use setuptools-scm to generate the distribution version — we'd not have the desired information before such a switch.
But if you want, I can contribute that separately, after this PR is in, for example.

from sphinx.application import Sphinx

from myst_parser import __version__
# -- Path setup --------------------------------------------------------------

PROJECT_ROOT_DIR = Path(__file__).parents[1].resolve() # pylint: disable=no-member
get_scm_version = partial(get_version, root=PROJECT_ROOT_DIR)
# -- Project information -----------------------------------------------------

github_url = "https://github.com"
github_repo_org = "executablebooks"
github_repo_name = "MyST-Parser"
github_repo_slug = f"{github_repo_org}/{github_repo_name}"
github_repo_url = f"{github_url}/{github_repo_slug}"
github_sponsors_url = f"{github_url}/sponsors"

project = "MyST Parser"
copyright = f"{date.today().year}, Executable Book Project"
author = "Executable Book Project"
version = __version__

# The short X.Y version
version = ".".join(
get_scm_version(local_scheme="no-local-version",).split(
"."
)[:3],
)

# The full version, including alpha/beta/rc tags
release = get_scm_version()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This allows rendering a potentially-next version in the changelog draft.



master_doc = "index"
language = "en"
Expand All @@ -28,12 +50,14 @@
extensions = [
"myst_parser",
"sphinx.ext.autodoc",
"sphinx.ext.extlinks",
"sphinx.ext.intersphinx",
"sphinx.ext.viewcode",
"sphinx_design",
"sphinxext.rediraffe",
"sphinxcontrib.mermaid",
"sphinxext.opengraph",
"sphinxcontrib.towncrier", # provides `towncrier-draft-entries` directive
]

# Add any paths that contain templates here, relative to this directory.
Expand All @@ -42,7 +66,12 @@
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
exclude_patterns = [
"_build",
"Thumbs.db",
".DS_Store",
"changelog-fragments.d/**", # Towncrier-managed change notes
]


# -- Options for HTML output -------------------------------------------------
Expand Down Expand Up @@ -93,6 +122,12 @@
"attrs_image",
]
myst_number_code_blocks = ["typescript"]
myst_substitutions = {
"project": project,
"release": release,
"release_l": f"`{release}`", # Needed in draft changelog for spelling ext
"version": version,
}
myst_heading_anchors = 2
myst_footnote_transition = True
myst_dmath_double_inline = True
Expand All @@ -114,6 +149,22 @@

suppress_warnings = ["myst.strikethrough"]

# -- Options for towncrier_draft extension -----------------------------------

towncrier_draft_autoversion_mode = "draft" # or: 'sphinx-version', 'sphinx-release'
towncrier_draft_include_empty = True
towncrier_draft_working_directory = PROJECT_ROOT_DIR
# towncrier_draft_config_path = 'pyproject.toml' # relative to cwd

# -- Options for extlinks extension ------------------------------------------

extlinks = {
"issue": (f"{github_repo_url}/issues/%s", "#%s"), # noqa: WPS323
"pr": (f"{github_repo_url}/pull/%s", "PR #%s"), # noqa: WPS323
"commit": (f"{github_repo_url}/commit/%s", "%s"), # noqa: WPS323
"gh": (f"{github_url}/%s", "GitHub: %s"), # noqa: WPS323
"user": (f"{github_sponsors_url}/%s", "@%s"), # noqa: WPS323
}

intersphinx_mapping = {
"python": ("https://docs.python.org/3.7", None),
Expand Down
17 changes: 17 additions & 0 deletions docs/develop/_changelog.md
@@ -1,4 +1,21 @@
# Changelog

```{include} ../../CHANGELOG.md
:end-before: <!-- towncrier release notes start -->
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This allows pasting the changelog draft between the top note and the released changelog.

:start-after: (DO-NOT-REMOVE-versioning-promise-START)
```

```{important} The version marked with __/UNRELEASED DRAFT/__ is not yet released and is under active development
```

```{towncrier-draft-entries}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This injects a pre-rendered changelog composed of the Towncrier fragments in the docs/changelog-fragments.d/ before the release happens. As a part of the release preparation, you'd normally run a command that deletes those fragments from Git and adds the rendered section to CHANGELOG.md.

_{subscript}`/UNRELEASED DRAFT/`_ {{ release }}
```

```{include} ../../CHANGELOG.md
:relative-docs: docs/
:relative-images:
:start-after: <!-- towncrier release notes start -->
```
3 changes: 3 additions & 0 deletions docs/develop/contributing.md
Expand Up @@ -71,6 +71,9 @@ For documentation build tests:
>> make html-strict
```

```{include} ../changelog-fragments.d/README.md
```

```{seealso}
{ref}`develop/testing`
```
Expand Down
49 changes: 49 additions & 0 deletions pyproject.toml
Expand Up @@ -54,11 +54,13 @@ linkify = ["linkify-it-py~=1.0"]
# Note: This is only required for internal use
rtd = [
"ipython",
"setuptools-scm ~= 7.0.5",
"sphinx-book-theme",
"sphinx-design",
"sphinxext-rediraffe~=0.2.7",
"sphinxcontrib.mermaid~=0.7.1",
"sphinxext-opengraph~=0.6.3",
"sphinxcontrib-towncrier ~= 0.3.0a0",
]
testing = [
"beautifulsoup4",
Expand Down Expand Up @@ -108,3 +110,50 @@ ignore_missing_imports = true

[tool.coverage.run]
omit = ["*/_docs.py"]

[tool.towncrier]
directory = "docs/changelog-fragments.d/"
filename = "CHANGELOG.md"
issue_format = "{{issue}}`{issue}`"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is what's rendered in the template, the syntax is basically what works in f-strings. It produces a role targeting an issue that would later be rendered by Sphinx but some projects use an explicit external URL syntax instead.

start_string = "<!-- towncrier release notes start -->\n\n"
template = "docs/changelog-fragments.d/.CHANGELOG-TEMPLATE.md.j2"
title_format = "## {version} - {project_date}"
underlines = ["", "", "", ""]

[[tool.towncrier.section]]
path = ""

[[tool.towncrier.type]]
directory = "bugfix"
name = "Bugfixes"
showcontent = true

[[tool.towncrier.type]]
directory = "feature"
name = "Features"
showcontent = true

[[tool.towncrier.type]]
directory = "deprecation"
name = "Deprecations (removal in next major release)"
showcontent = true

[[tool.towncrier.type]]
directory = "breaking"
name = "Backward incompatible changes"
showcontent = true

[[tool.towncrier.type]]
directory = "doc"
name = "Documentation"
showcontent = true

[[tool.towncrier.type]]
directory = "misc"
name = "Miscellaneous"
showcontent = true

[[tool.towncrier.type]]
directory = "internal"
name = "Contributor-facing changes"
showcontent = true
Comment on lines +123 to +159
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is where you can specify custom types.