diff --git a/.cruft.json b/.cruft.json index 3821fc4c..ee0ff7e8 100644 --- a/.cruft.json +++ b/.cruft.json @@ -11,7 +11,7 @@ "project_slug": "xhydro", "project_short_description": "Hydrological analysis library built with xarray", "pypi_username": "TC-FF", - "version": "0.2.0", + "version": "0.2.1", "use_pytest": "y", "use_black": "y", "add_pyup_badge": "n", diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 1e2a220b..ed5c8a1e 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -129,16 +129,31 @@ To run a subset of tests:: $ pytest tests.test_xhydro -Versioning/Tagging ------------------- +Versioning/Releasing +-------------------- + +We use `bumpversion` to maintain version numbers, so most of the time you don't have to worry about it. + +This section is thus mostly a reminder for the maintainers on how to proceed when a new version is ready to be released. + +1. Create a new branch from `main` (e.g. `release-0.2.0`). +2. Update the `HISTORY.rst` file to change the `Unreleased` section to the current date. -A reminder for the maintainers on how to deploy. -Make sure all your changes are committed (including an entry in HISTORY.rst). Then run:: -$ bumpversion patch # possible: major / minor / patch +$ bumpversion minor # In most cases, we will be releasing a minor version $ git push -$ git push --tags + +3. Create a pull request from your branch to `main`. +4. Once the pull request is merged, create a new release on GitHub. Both the tag and the release title should be the version number, prefixed with a `v` (e.g. `v0.2.0`). +5. To generate the release notes, run:: + + $ import xhydro.testing.utils as xhu + $ print(xhu.publish_release_notes()) + +This will print the release notes (taken from the `HISTORY.rst` file) to your python console. Copy and paste them into the GitHub release description, keeping only the changes for the current version. + +6. Once the release is published, it will go into a `staging` mode on Github Actions. Once the tests pass, admins can approve the release (an e-mail will be sent) and it will be published on PyPI. Packaging --------- diff --git a/HISTORY.rst b/HISTORY.rst index 980914e9..c3a873b5 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -2,6 +2,30 @@ History ======= +v0.3.0 (unreleased) +------------------- +Contributors to this version: Gabriel Rondeau-Genesse (:user:`RondeauG`) + +Announcements +^^^^^^^^^^^^^ +* N/A + +New features and enhancements +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +* N/A + +Breaking changes +^^^^^^^^^^^^^^^^ +* N/A + +Bug fixes +^^^^^^^^^ +* N/A + +Internal changes +^^^^^^^^^^^^^^^^ +* Added `xhydro.testing.utils.publish_release_notes()` to help with the release process. (:pull:`37`). + v0.2.0 (2023-10-10) ------------------- Contributors to this version: Trevor James Smith (:user:`Zeitsperre`), Gabriel Rondeau-Genesse (:user:`RondeauG`), Thomas-Charles Fortier Filion (:user:`TC-FF`), Sébastien Langlois (:user:`sebastienlanglois`) diff --git a/setup.cfg b/setup.cfg index 43188e5c..5cafa6f8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.2.0 +current_version = 0.2.1 commit = True tag = True diff --git a/setup.py b/setup.py index 078dde4b..4887f7d7 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,6 @@ "dev": dev_requirements, }, url="https://github.com/hydrologie/xhydro", - version="0.2.0", + version="0.2.1", zip_safe=False, ) diff --git a/tests/test_testing_utils.py b/tests/test_testing_utils.py new file mode 100644 index 00000000..ae595b4a --- /dev/null +++ b/tests/test_testing_utils.py @@ -0,0 +1,24 @@ +import xhydro as xh +import xhydro.testing.utils as xhu + + +def test_publish_release_notes(): + history = xhu.publish_release_notes(style="md") + + assert history.startswith("# History") + version = xh.__version__ + vsplit = version.split(".") + + v_4history = ( + vsplit[0] + + "." + + str(int(vsplit[1]) + 1 if vsplit[2] != "0" else vsplit[1]) + + ".0" + ) + assert f"## v{v_4history}" in history + assert ":user:`" not in history + assert ":issue:`" not in history + assert ":pull:`" not in history + + history_rst = xhu.publish_release_notes(style="rst") + assert history_rst.startswith("=======\nHistory\n=======") diff --git a/tests/test_xhydro.py b/tests/test_xhydro.py index 461f2a4a..6dcea564 100644 --- a/tests/test_xhydro.py +++ b/tests/test_xhydro.py @@ -36,4 +36,4 @@ def test_package_metadata(): contents = f.read() assert """Thomas-Charles Fortier Filion""" in contents assert '__email__ = "tcff_hydro@outlook.com"' in contents - assert '__version__ = "0.2.0"' in contents + assert '__version__ = "0.2.1"' in contents diff --git a/xhydro/__init__.py b/xhydro/__init__.py index ea9cdd3f..657893e9 100644 --- a/xhydro/__init__.py +++ b/xhydro/__init__.py @@ -6,4 +6,4 @@ __author__ = """Thomas-Charles Fortier Filion""" __email__ = "tcff_hydro@outlook.com" -__version__ = "0.2.0" +__version__ = "0.2.1" diff --git a/xhydro/testing/__init__.py b/xhydro/testing/__init__.py new file mode 100644 index 00000000..92c8062e --- /dev/null +++ b/xhydro/testing/__init__.py @@ -0,0 +1 @@ +"""Helpers for testing.""" diff --git a/xhydro/testing/utils.py b/xhydro/testing/utils.py new file mode 100644 index 00000000..744121f0 --- /dev/null +++ b/xhydro/testing/utils.py @@ -0,0 +1,80 @@ +"""Utilities for testing and releasing xhydro.""" + +import os +import re +from io import StringIO +from pathlib import Path +from typing import Optional, TextIO, Union + + +def publish_release_notes( + style: str = "md", file: Optional[Union[os.PathLike, StringIO, TextIO]] = None +) -> Optional[str]: + """Format release history in Markdown or ReStructuredText. + + Parameters + ---------- + style : {"rst", "md"} + Use ReStructuredText (`rst`) or Markdown (`md`) formatting. Default: Markdown. + file : {os.PathLike, StringIO, TextIO, None} + If provided, prints to the given file-like object. Otherwise, returns a string. + + Returns + ------- + str, optional + + Notes + ----- + This function exists solely for development purposes. + Adapted from xclim.testing.utils.publish_release_notes. + """ + history_file = Path(__file__).parent.parent.parent.joinpath("HISTORY.rst") + + if not history_file.exists(): + raise FileNotFoundError("History file not found in xhydro file tree.") + + with open(history_file) as hf: + history = hf.read() + + if style == "rst": + hyperlink_replacements = { + r":issue:`([0-9]+)`": r"`GH/\1 `_", + r":pull:`([0-9]+)`": r"`PR/\1 `_", + r":user:`([a-zA-Z0-9_.-]+)`": r"`@\1 `_", + } + elif style == "md": + hyperlink_replacements = { + r":issue:`([0-9]+)`": r"[GH/\1](https://github.com/hydrologie/xhydro/issues/\1)", + r":pull:`([0-9]+)`": r"[PR/\1](https://github.com/hydrologie/xhydro/pull/\1)", + r":user:`([a-zA-Z0-9_.-]+)`": r"[@\1](https://github.com/\1)", + } + else: + raise NotImplementedError() + + for search, replacement in hyperlink_replacements.items(): + history = re.sub(search, replacement, history) + + if style == "md": + history = history.replace("=======\nHistory\n=======", "# History") + + titles = {r"\n(.*?)\n([\-]{1,})": "-", r"\n(.*?)\n([\^]{1,})": "^"} + for title_expression, level in titles.items(): + found = re.findall(title_expression, history) + for grouping in found: + fixed_grouping = ( + str(grouping[0]).replace("(", r"\(").replace(")", r"\)") + ) + search = rf"({fixed_grouping})\n([\{level}]{'{' + str(len(grouping[1])) + '}'})" + replacement = f"{'##' if level=='-' else '###'} {grouping[0]}" + history = re.sub(search, replacement, history) + + link_expressions = r"[\`]{1}([\w\s]+)\s<(.+)>`\_" + found = re.findall(link_expressions, history) + for grouping in found: + search = rf"`{grouping[0]} <.+>`\_" + replacement = f"[{str(grouping[0]).strip()}]({grouping[1]})" + history = re.sub(search, replacement, history) + + if not file: + return history + print(history, file=file) diff --git a/xhydro/utils.py b/xhydro/utils.py index b1392915..1d87af94 100644 --- a/xhydro/utils.py +++ b/xhydro/utils.py @@ -1,5 +1,4 @@ """Utility functions for xhydro.""" - from xscen.diagnostics import health_checks __all__ = ["health_checks"]