diff --git a/.prospector.yml b/.prospector.yml index 15fc8b71e9..dbc62018eb 100644 --- a/.prospector.yml +++ b/.prospector.yml @@ -19,4 +19,4 @@ pep257: # disable rules that are allowed by the numpy convention # see https://github.com/PyCQA/pydocstyle/blob/master/src/pydocstyle/violations.py # and http://pydocstyle.readthedocs.io/en/latest/error_codes.html - disable: ['D107', 'D203', 'D212', 'D213', 'D402', 'D413'] + disable: ['D107', 'D203', 'D212', 'D213', 'D402', 'D413', 'D416'] diff --git a/doc/sphinx/source/gensidebar.py b/doc/sphinx/source/gensidebar.py index ac527f8da8..a076f9df59 100644 --- a/doc/sphinx/source/gensidebar.py +++ b/doc/sphinx/source/gensidebar.py @@ -76,7 +76,6 @@ def _header(project, text): _write("esmvalcore", "Diagnostic script interfaces", "interfaces") _write("esmvalcore", "Development", "develop/index") _write("esmvalcore", "Contributing", "contributing") - _write("esmvalcore", "Utilities", "utils") _write("esmvalcore", "ESMValCore API Reference", "api/esmvalcore") _write("esmvalcore", "Changelog", "changelog") _endl() diff --git a/doc/sphinx/source/utils.rst b/doc/sphinx/source/utils.rst index b78a1ae0ae..1beb3a9814 100644 --- a/doc/sphinx/source/utils.rst +++ b/doc/sphinx/source/utils.rst @@ -3,7 +3,15 @@ Utilities ********* -This section provides information on small tools that are available in the ``esmvaltool/utils`` directory. +This section provides information on small tools that are available in the +`esmvaltool/utils `_ +directory. + + +draft_release_notes.py +====================== + +Script for drafting release notes based on the titles of the GitHub pull requests that have been merged since the previous release. nclcodestyle @@ -15,7 +23,6 @@ To use it, run .. code-block:: bash - nclcodestyle /path/to/file.ncl @@ -35,3 +42,9 @@ test recipe settings -------------------- A tool for generating recipes with various diagnostic settings, to test of those work. +Install ESMValTool in development mode (``pip install -e '.[develop]'``) to make it available. +To use it, run + +.. code-block:: bash + + test_recipe --help diff --git a/esmvaltool/utils/draft_release_notes.py b/esmvaltool/utils/draft_release_notes.py new file mode 100644 index 0000000000..09aa71ee7b --- /dev/null +++ b/esmvaltool/utils/draft_release_notes.py @@ -0,0 +1,115 @@ +"""Draft release notes. + +To use this tool, follow these steps: +1) `pip install pygithub` +2) Create an access token and store it in the file ~/.github_api_key, see: +https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line +3) set PREVIOUS_RELEASE to the date/time of the previous release in code below + +""" +import datetime +from pathlib import Path + +try: + from github import Github +except ImportError: + print("Please `pip install pygithub`") + +try: + GITHUB_API_KEY = Path("~/.github_api_key").expanduser().read_text().strip() +except FileNotFoundError: + print("Please create an access token and store it in the file " + "~/.github_api_key, see:\nhttps://help.github.com/en/github/" + "authenticating-to-github/creating-a-personal-access-token-" + "for-the-command-line") + +from esmvalcore import __version__ + +VERSION = f"v{__version__}" +GITHUB_REPO = "ESMValGroup/ESMValCore" + +TITLES = { + 'bug': 'Bug fixes', + 'diagnostic': 'Diagnostics', + 'fix for dataset': 'Fixes for datasets', + 'observations': 'Observational and re-analysis dataset support', + 'enhancement': 'Improvements', +} + + +def draft_notes_since(previous_release_date, labels): + """Draft release notes containing the merged pull requests. + + Arguments + --------- + previous_release_date: datetime.datetime + date of the previous release + labels: list + list of GitHub labels that deserve separate sections + """ + session = Github(GITHUB_API_KEY) + repo = session.get_repo(GITHUB_REPO) + pulls = repo.get_pulls( + state='closed', + sort='updated', + direction='desc', + ) + + lines = {} + for pull in pulls: + print(pull.updated_at, pull.merged_at, pull.number, pull.title) + if pull.updated_at < previous_release_date: + break + if pull.merged: + if pull.merged_at < previous_release_date: + continue + pr_labels = {label.name for label in pull.labels} + for label in labels: + if label in pr_labels: + break + else: + label = 'enhancement' + + user = pull.user + username = user.login if user.name is None else user.name + title = pull.title + title = title[0].upper() + title[1:] + line = ( + f"- {title} (`#{pull.number} " + f"`__) " + f"`{username} `__") + if label not in lines: + lines[label] = [] + lines[label].append((pull.closed_at, line)) + + # Create sections + sections = [ + VERSION, + '-' * len(VERSION), + '', + "This release includes", + ] + for label in sorted(lines): + entries = sorted(lines[label]) # sort by merge time + title = TITLES.get(label, label.title()) + sections.append('\n'.join(['', title, '~' * len(title), ''])) + sections.append('\n'.join(entry for _, entry in entries)) + notes = '\n'.join(sections) + + print(notes) + + +if __name__ == '__main__': + + PREVIOUS_RELEASE = datetime.datetime(2020, 3, 31, 12) + LABELS = ( + 'bug', + 'documentation', + 'diagnostic', + 'fix for dataset', + 'preprocessor', + 'observations', + 'enhancement', + ) + + draft_notes_since(PREVIOUS_RELEASE, LABELS)