From de633cdf4b263100e6ea2103bc9794ab827231c8 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Fri, 10 Apr 2020 01:04:39 +0530 Subject: [PATCH] Significantly improve release version validation --- noxfile.py | 8 ++--- tools/automation/release/__init__.py | 23 ++++++------ tools/automation/release/check_version.py | 43 +++++++++++++++++++++++ 3 files changed, 59 insertions(+), 15 deletions(-) create mode 100644 tools/automation/release/check_version.py diff --git a/noxfile.py b/noxfile.py index 8dc7abd40..2e4ad6fda 100644 --- a/noxfile.py +++ b/noxfile.py @@ -155,9 +155,9 @@ def lint(session): # ----------------------------------------------------------------------------- @nox.session(name="prepare-release") def prepare_release(session): - version = release.get_version_from_arguments(session.posargs) + version = release.get_version_from_arguments(session) if not version: - session.error("Usage: nox -s prepare-release -- YY.N[.P]") + session.error("Usage: nox -s prepare-release -- ") session.log("# Ensure nothing is staged") if release.modified_files_in_git("--staged"): @@ -190,7 +190,7 @@ def prepare_release(session): @nox.session(name="build-release") def build_release(session): - version = release.get_version_from_arguments(session.posargs) + version = release.get_version_from_arguments(session) if not version: session.error("Usage: nox -s build-release -- YY.N[.P]") @@ -249,7 +249,7 @@ def build_dists(session): @nox.session(name="upload-release") def upload_release(session): - version = release.get_version_from_arguments(session.posargs) + version = release.get_version_from_arguments(session) if not version: session.error("Usage: nox -s upload-release -- YY.N[.P]") diff --git a/tools/automation/release/__init__.py b/tools/automation/release/__init__.py index a61386862..9728b3613 100644 --- a/tools/automation/release/__init__.py +++ b/tools/automation/release/__init__.py @@ -14,24 +14,25 @@ from typing import Iterator, List, Optional, Set from nox.sessions import Session -def get_version_from_arguments(arguments: List[str]) -> Optional[str]: +def get_version_from_arguments(session: Session) -> Optional[str]: """Checks the arguments passed to `nox -s release`. If there is only 1 argument that looks like a pip version, returns that. Otherwise, returns None. """ - if len(arguments) != 1: + if len(session.posargs) != 1: return None + version = session.posargs[0] - version = arguments[0] - - parts = version.split('.') - if not 2 <= len(parts) <= 3: - # Not of the form: YY.N or YY.N.P - return None - - if not all(part.isdigit() for part in parts): - # Not all segments are integers. + # We delegate to a script here, so that it can depend on packaging. + session.install("packaging") + cmd = [ + os.path.join(session.bin, "python"), + "tools/automation/release/check_version.py", + version + ] + not_ok = subprocess.run(cmd).returncode + if not_ok: return None # All is good. diff --git a/tools/automation/release/check_version.py b/tools/automation/release/check_version.py new file mode 100644 index 000000000..db02e7aef --- /dev/null +++ b/tools/automation/release/check_version.py @@ -0,0 +1,43 @@ +"""Checks if the version is acceptable, as per this project's release process. +""" + +import sys +from datetime import datetime +from typing import Optional + +from packaging.version import InvalidVersion, Version + + +def is_this_a_good_version_number(string: str) -> Optional[str]: + try: + v = Version(string) + except InvalidVersion as e: + return str(e) + + if v.local: + return "Nope. PyPI refuses local release versions." + + if v.dev: + return "No development releases on PyPI. What are you even thinking?" + + if v.is_prerelease and v.pre[0] != "b": + return "Only beta releases are allowed. No alphas." + + release = v.release + expected_major = datetime.now().year % 100 + + if len(release) not in [2, 3]: + return "Not of the form: {0}.N or {0}.N.P".format(expected_major) + + return None + + +def main() -> None: + problem = is_this_a_good_version_number(sys.argv[1]) + if problem is not None: + print("ERROR:", problem) + sys.exit(1) + + +if __name__ == "__main__": + main()