Skip to content

Commit

Permalink
Validate (at least some) args (#251)
Browse files Browse the repository at this point in the history
  • Loading branch information
why-not-try-calmer authored Oct 10, 2023
1 parent 5324cdb commit 582278d
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 2 deletions.
6 changes: 6 additions & 0 deletions qgispluginci/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ def cli():
release_parser.add_argument(
"release_version", help="The version to be released (x.y.z)."
)
release_parser.add_argument(
"--no-validation",
action="store_true",
help="Turn off validation of `release version`",
)
release_parser.add_argument(
"--release-tag",
help="The release tag, if different from the version (e.g. vx.y.z).",
Expand Down Expand Up @@ -152,6 +157,7 @@ def cli():
push_tr_parser.add_argument("transifex_token", help="The Transifex API token")

args = parser.parse_args()
Parameters.validate_args(args)

# set log level depending on verbosity argument
args.verbosity = 40 - (10 * args.verbosity) if args.verbosity > 0 else 0
Expand Down
57 changes: 55 additions & 2 deletions qgispluginci/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@
# ########## Libraries #############
# ##################################

# standard library
import configparser
import datetime
import logging
import os
import re
import sys

# standard library
from argparse import Namespace
from pathlib import Path
from typing import Any, Callable, Dict, Iterator, Literal, Optional, Tuple
from typing import Any, Callable, Dict, Iterator, Optional, Tuple

import toml
import yaml
Expand Down Expand Up @@ -228,6 +231,56 @@ def __init__(self, definition: Dict[str, Any]):
)
self.repository_url = get_metadata("repository")

@staticmethod
def get_release_version_patterns() -> Dict[str, re.Pattern]:
return {
"simple": r"\d+\.\d+$",
"double": r"\d+\.\d+\.\d+$",
"v2": r"^v\d+\.\d+$",
"v3": r"^v\d+\.\d+\.\d+$",
# See https://github.com/semver/semver/blob/master/semver.md#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
"semver": r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$",
}

@staticmethod
def validate_args(args: Namespace):
"""
Raise an exception just in case:
- the user didn't opt-out of validation using the `--no-validation` flag; and
- the value of `release_version` matches no supported pattern.
In any case, warn the user if the value of `release_version` doesn't match the semver pattern.
"""
if not args.release_version:
return

patterns = Parameters.get_release_version_patterns()
semver_compliance = re.match(patterns.pop("semver"), args.release_version)

if not semver_compliance:
logging.warning(
f"Be aware that '{args.release_version}' is not a semver-compliant version."
)

if args.no_validation:
logging.warning("Disabled release version validation.")
return

if semver_compliance or any(
re.match(other_pattern, args.release_version)
for other_pattern in patterns.values()
):
return

raise ValueError(
f"""
Unable to validate the release version '{args.release_version}'.
Please use a release version identifier such as '1.0.1' (recommended, semantic versioning), 'v1.1.1', 'v1.1', or '1.1'.
Otherwise you can disable validation by running this command again with an extra '--no-validation' flag.
Semantic versioning (semvar) identifiers are recommended.
Take a look at https://en.wikipedia.org/wiki/Software_versioning#Semantic_versioning for a refresher."
"""
)

@staticmethod
def archive_name(
plugin_name, release_version: str, experimental: bool = False
Expand Down
41 changes: 41 additions & 0 deletions test/test_release.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#! /usr/bin/env python

# standard
import argparse
import filecmp
import os
import re
import unittest
import urllib.request
from itertools import product
from pathlib import Path
from tempfile import mkstemp
from zipfile import ZipFile
Expand Down Expand Up @@ -205,6 +207,45 @@ def test_release_changelog(self):
# Commit sha1 not in the metadata.txt
self.assertEqual(0, len(re.findall(r"commitSha1=\d+", str(data))))

def test_release_version_valid_invalid(self):
valid_tags = ["v1.1.1", "v1.1", "1.0.1", "1.1", "1.0.0-alpha", "1.0.0-dev"]
invalid_tags = ["1", "v1", ".", ".1"]
expected_valid_results = {
"v1.1.1": ["v3"],
"v1.1": ["v2"],
"1.0.1": ["double", "semver"],
"1.1": ["simple"],
"1.0.0-alpha": ["semver"],
"1.0.0-dev": ["semver"],
}
valid_results = {tag: [] for tag in valid_tags}
patterns = Parameters.get_release_version_patterns()
for key, cand in product(patterns, valid_results):
if re.match(patterns[key], cand):
valid_results[cand].append(key)
self.assertEqual(valid_results, expected_valid_results)

invalid_results = {tag: [] for tag in invalid_tags}
for key, cand in product(patterns, invalid_results):
if re.match(patterns[key], cand):
invalid_results[cand].append(key)
self.assertFalse(any(invalid_results.values()))

def test_release_version_validation_on(self):
parser = argparse.ArgumentParser()
parser.add_argument("release_version")
parser.add_argument("--no-validation", action="store_true")
args = parser.parse_args(["v1"])
with self.assertRaises(ValueError):
Parameters.validate_args(args)

def test_release_version_validation_off(self):
parser = argparse.ArgumentParser()
parser.add_argument("release_version")
parser.add_argument("--no-validation", action="store_true")
args = parser.parse_args([".", "--no-validation"])
Parameters.validate_args(args)


if __name__ == "__main__":
unittest.main()

0 comments on commit 582278d

Please sign in to comment.