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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

**Checklist**
- [ ] Add a news fragment if this PR is news-worthy for end users. (see docs/releases/README.rst)
27 changes: 27 additions & 0 deletions docs/releases/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
The `upcoming` directory contains news fragments that will be added to the
changelog for the NEXT release.

Changes that are not of interest to the end-user can skip adding news fragment.

Add a news fragment
-------------------
Create a new file with a name like ``<pull-request>.<type>.rst``, where
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The obvious flaw here is that if you try to do this before opening a PR, you don't know the PR number. So you would either have to guess it, or do this step right after opening a PR. This is why having a GitHub bot would be nice, but it is the best we could do with the least effort right now.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this is a step towards making life easier for ourselves so for now, it's fine if PR authors have to add the news fragment after opening the PR.

``<pull-request>`` is a pull request number, and ``<type>`` is one of:

- ``feature``: New feature
- ``bugfix``: Bug fixes
- ``deprecation``: Deprecations of public API
- ``removal``: Removal of public API
- ``doc``: Documentation changes.

Then write a short sentence in the file that describes the changes for the
end users, e.g. in ``123.removal.rst``::

Remove package xyz.

Alternatively, use the following command, run from the project root directory
and answer the questions::

python etstool.py changelog create

(This command requires ``click`` in the environment.)
1 change: 1 addition & 0 deletions docs/releases/upcoming/116.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix SyntaxWarning in persistence.file_path (#116)
1 change: 1 addition & 0 deletions docs/releases/upcoming/172.removal.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove ``appscripting`` subpackage (#172)
1 change: 1 addition & 0 deletions docs/releases/upcoming/173.removal.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove ``template`` subpackage (#173)
1 change: 1 addition & 0 deletions docs/releases/upcoming/175.removal.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove ``permission`` subpackage (#175)
111 changes: 111 additions & 0 deletions etstool.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@
commands for each task. See the EDM documentation for more information about
how to run commands within an EDM enviornment.

Build changelog
---------------

To create a first-cut changelog from the news fragments, use this command::

python etstool.py changelog build

This will update the changelog file. You should review and edit it.
"""

import glob
Expand Down Expand Up @@ -116,6 +124,17 @@
github_url_fmt = "git+http://github.com/enthought/{0}.git#egg={0}"


# Location of documentation files
HERE = os.path.dirname(__file__)
DOCS_DIR = os.path.join(HERE, "docs")

# Location of news fragment for creating changelog.
NEWS_FRAGMENT_DIR = os.path.join(DOCS_DIR, "releases", "upcoming")

# Location of the Changelog file.
CHANGELOG_PATH = os.path.join(HERE, "CHANGES.txt")


@click.group()
def cli():
pass
Expand Down Expand Up @@ -283,6 +302,98 @@ def test_all():
if failed_command:
sys.exit(1)


@cli.group("changelog")
@click.pass_context
def changelog(ctx):
""" Group of commands related to creating changelog."""

ctx.obj = {
# Mapping from news fragment type to their description in
# the changelog.
"type_to_description": {
"feature": "Features",
"bugfix": "Fixes",
"deprecation": "Deprecations",
"removal": "Removals",
"doc": "Documentation changes",
}
}


@changelog.command("create")
@click.pass_context
def create_news_fragment(ctx):
""" Create a news fragment for your PR."""

pr_number = click.prompt('Please enter the PR number', type=int)
type_ = click.prompt(
"Choose a fragment type:",
type=click.Choice(ctx.obj["type_to_description"])
)

filepath = os.path.join(
NEWS_FRAGMENT_DIR, f"{pr_number}.{type_}.rst"
)

if os.path.exists(filepath):
click.echo("FAILED: File {} already exists.".format(filepath))
ctx.exit(1)

content = click.prompt(
"Describe the changes to the END USERS.\n"
"Example: 'Remove subpackage xyz.'\n",
type=str,
)
if not os.path.exists(NEWS_FRAGMENT_DIR):
os.makedirs(NEWS_FRAGMENT_DIR)
with open(filepath, "w", encoding="utf-8") as fp:
fp.write(content + f" (#{pr_number})")

click.echo("Please commit the file created at: {}".format(filepath))


@changelog.command("build")
@click.pass_context
def build_changelog(ctx):
""" Build Changelog created from all the news fragments."""
# This is a rather simple first-cut generation of the changelog.
# It removes the laborious concatenation, but the end results might
# still require some tweaking.
contents = []

for type_, description in ctx.obj["type_to_description"].items():
pattern = os.path.join(NEWS_FRAGMENT_DIR, f"*.{type_}.rst")
file_paths = sorted(glob.glob(pattern))

if file_paths:
contents.append("")
contents.append(description)
contents.append("-" * len(description))

for filename in file_paths:
with open(filename, "r", encoding="utf-8") as fp:
contents.append("* " + fp.read())

# Prepend content to the changelog file.

with open(CHANGELOG_PATH, "r", encoding="utf-8") as fp:
original_changelog = fp.read()

with open(CHANGELOG_PATH, "w", encoding="utf-8") as fp:
if contents:
print(*contents, sep="\n", file=fp)
fp.write(original_changelog)

click.echo("Changelog is updated.")
should_clean = click.confirm(
"Do you want to remove the news fragments?"
)
if should_clean:
rmtree(NEWS_FRAGMENT_DIR)
os.makedirs(NEWS_FRAGMENT_DIR)


# ----------------------------------------------------------------------------
# Utility routines
# ----------------------------------------------------------------------------
Expand Down