From a819051c4da6abae95fb1d8c58b7714eba957418 Mon Sep 17 00:00:00 2001 From: Joseph D Hughes Date: Sun, 16 Jun 2024 18:40:34 -0500 Subject: [PATCH] * add update_version script for release --- README.md | 2 +- pixi.toml | 3 + pymake/config.py | 3 + scripts/update_version.py | 149 ++++++++++++++++++++++++++++++++++++++ version.txt | 1 + 5 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 scripts/update_version.py create mode 100644 version.txt diff --git a/README.md b/README.md index 47b236f..14dda82 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Python package for building MODFLOW-based programs from source files. -### Version 1.2.10.dev0 +### Version 1.2.10 [![PyPI Version](https://img.shields.io/pypi/v/mfpymake.png)](https://pypi.python.org/pypi/mfpymake) [![Anaconda Version](https://anaconda.org/conda-forge/mfpymake/badges/version.svg)](https://anaconda.org/conda-forge/mfpymake) diff --git a/pixi.toml b/pixi.toml index bc9afd6..1e27565 100644 --- a/pixi.toml +++ b/pixi.toml @@ -49,3 +49,6 @@ autotest-schedule = { cmd = "pytest -v -m='schedule' --durations=0 --cov=pymake # coverage report coverage-report = { cmd = "coverage report", cwd = "autotest"} + +# release +update-version = { cmd = "python scripts/update_version.py -v"} diff --git a/pymake/config.py b/pymake/config.py index b1b175a..d85dc48 100644 --- a/pymake/config.py +++ b/pymake/config.py @@ -1,3 +1,6 @@ +# mfpymake version file automatically created using +# update_version.py on June 16, 2024 18:36:44 + __author__ = "Joseph D. Hughes" __date__ = "June 16, 2024" __version__ = "1.2.10" diff --git a/scripts/update_version.py b/scripts/update_version.py new file mode 100644 index 0000000..59bdc8d --- /dev/null +++ b/scripts/update_version.py @@ -0,0 +1,149 @@ +import argparse +import re +import textwrap +from datetime import datetime +from pathlib import Path + +from filelock import FileLock +from packaging.version import Version + +_project_name = "mfpymake" +_project_root_path = Path(__file__).parent.parent +_version_txt_path = _project_root_path / "version.txt" +_version_py_path = _project_root_path / "pymake" / "config.py" + +# file names and the path to the file relative to the repo root directory +file_paths_list = [ + _project_root_path / "README.md", + _project_root_path / "version.txt", + _project_root_path / "pymake" / "config.py", +] +file_paths = {pth.name: pth for pth in file_paths_list} # keys for each file + +def split_nonnumeric(s): + match = re.compile("[^0-9]").search(s) + return [s[: match.start()], s[match.start() :]] if match else s + + +_initial_version = Version("0.0.1") +_current_version = Version(_version_txt_path.read_text().strip()) + + +def update_version_txt(version: Version): + with open(_version_txt_path, "w") as f: + f.write(str(version)) + print(f"Updated {_version_txt_path} to version {version}") + + +def update_version_py(timestamp: datetime, version: Version): + lines = file_paths["config.py"].read_text().rstrip().split("\n") + + with open(_version_py_path, "w") as f: + f.write( + f"# {_project_name} version file automatically created using\n" + f"# {Path(__file__).name} on {timestamp:%B %d, %Y %H:%M:%S}\n\n" + ) + for line in lines: + if "__date__" in line: + line = f'__date__ = "{timestamp:%B %d, %Y}"' + elif "__version__" in line: + line = f'__version__ = "{version}"' + f.write(f"{line}\n") + print(f"Updated {_version_py_path} to version {version}") + + + +def update_readme_markdown( + timestamp: datetime, version: Version, approved: bool = False +): + fpth = file_paths["README.md"] + + # read README.md into memory + lines = fpth.read_text().rstrip().split("\n") + + # rewrite README.md + terminate = False + with open(fpth, "w") as f: + for line in lines: + if "### Version " in line: + line = f"### Version {version}" + f.write(f"{line}\n") + if terminate: + break + + print(f"Updated {fpth} to version {version}") + +def update_version( + timestamp: datetime = datetime.now(), + version: Version = None, + approved: bool = False, +): + lock_path = Path(_version_txt_path.name + ".lock") + try: + lock = FileLock(lock_path) + previous = Version(_version_txt_path.read_text().strip()) + version = ( + version + if version + else Version(previous.major, previous.minor, previous.micro) + ) + + with lock: + update_version_txt(version) + update_version_py(timestamp, version) + update_readme_markdown(timestamp, version, approved) + finally: + try: + lock_path.unlink() + except: + pass + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + prog=f"Update {_project_name} version", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=textwrap.dedent( + """\ + Update version information stored in version.txt in the project root, + as well as several other files in the repository. If --version is not + provided, the version number will not be changed. A file lock is held + to synchronize file access. The version tag must comply with standard + '..' format conventions for semantic versioning. + """ + ), + ) + parser.add_argument( + "-v", + "--version", + required=False, + help="Specify the release version", + ) + parser.add_argument( + "-a", + "--approve", + required=False, + action="store_true", + help="Approve the release (defaults false)", + ) + parser.add_argument( + "-g", + "--get", + required=False, + action="store_true", + help="Just get the current version number, no updates (defaults false)", + ) + args = parser.parse_args() + + if args.get: + print( + Version((_project_root_path / "version.txt").read_text().strip()) + ) + else: + update_version( + timestamp=datetime.now(), + version=( + Version(args.version) if args.version else _current_version + ), + approved=args.approve, + ) diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..732aa7d --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +1.2.10 \ No newline at end of file