diff --git a/docs/news/ancestor.major b/docs/news/ancestor.major new file mode 100644 index 0000000..48e5826 --- /dev/null +++ b/docs/news/ancestor.major @@ -0,0 +1,2 @@ +Reworked the DVCS persistence to load from previous tags either globally, or previous to the current commit. +The CLI `--persist-from` options were renamed to accommodate this. \ No newline at end of file diff --git a/docs/news/incr-flow.feature b/docs/news/incr-flow.feature new file mode 100644 index 0000000..49d362a --- /dev/null +++ b/docs/news/incr-flow.feature @@ -0,0 +1 @@ +Adds better workflow for incrementing patches. \ No newline at end of file diff --git a/docs/news/infinite.feature b/docs/news/infinite.feature new file mode 100644 index 0000000..b7ae49b --- /dev/null +++ b/docs/news/infinite.feature @@ -0,0 +1,2 @@ +Adds the ability to have "infinite" newsfiles. They no longer require cleaning up, if tags are used to indicate releases. + This requires a workflow where releases are tagged in git, so we can determine the "new news". \ No newline at end of file diff --git a/docs/news/inv-conf.major b/docs/news/inv-conf.major new file mode 100644 index 0000000..8482ce8 --- /dev/null +++ b/docs/news/inv-conf.major @@ -0,0 +1,2 @@ +Breaking change to the config spec: the trigger_patterns are specified as pattern:sigfig +in order to support multiple file patterns for each significant figure \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 1dc4903..a8ed041 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ toml -semver~=2.13 +semver==3.0.0.dev2 six diff --git a/setup.py b/setup.py index c394cf1..ca84d81 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ import os -from setuptools import setup + from setuptools import find_packages +from setuptools import setup repository_dir = os.path.dirname(__file__) diff --git a/src/auto_version/auto_version_tool.py b/src/auto_version/auto_version_tool.py index ced2c10..3465bf2 100644 --- a/src/auto_version/auto_version_tool.py +++ b/src/auto_version/auto_version_tool.py @@ -449,7 +449,7 @@ def main( if set_to: _LOG.debug("setting version directly: %s", set_to) # parse it - validation failure will raise a ValueError - new_version = semver.parse_version_info(set_to) + new_version = semver.Version.parse(set_to) if not lock: warnings.warn( "After setting version manually, does it need locking for a CI flow, to avoid an extraneous increment?", @@ -463,8 +463,8 @@ def main( current_semver, last_release_semver, triggers, **overrides ) - release_string = semver.finalize_version(str(new_version)) - release_version = semver.parse_version_info(release_string) + release_version = new_version.finalize_version() + release_string = str(release_version) if release: new_version = release_version updates[Constants.RELEASE_FIELD] = config.RELEASED_VALUE @@ -475,7 +475,7 @@ def main( updates[Constants.VERSION_STRICT_FIELD] = release_string # write out the individual parts of the version - updates.update(new_version._asdict()) + updates.update(new_version.to_dict()) # only rewrite a field that the user has specified in the configuration source_file_updates = { diff --git a/src/auto_version/replacement_handler.py b/src/auto_version/replacement_handler.py index 5cba1cd..340a942 100644 --- a/src/auto_version/replacement_handler.py +++ b/src/auto_version/replacement_handler.py @@ -23,7 +23,9 @@ def __call__(self, match): """ original = match.string key = match.group(Constants.KEY_GROUP) - replacement = self.params[key] # if there's nothing in the lookup, raise KeyError + replacement = self.params[ + key + ] # if there's nothing in the lookup, raise KeyError start, end = match.span(Constants.VALUE_GROUP) if start < 0: # when there's a match but zero-length for the value group, we insert it at the end diff --git a/src/auto_version/tests/test_autoversion.py b/src/auto_version/tests/test_autoversion.py index b9bec47..bfa41e2 100644 --- a/src/auto_version/tests/test_autoversion.py +++ b/src/auto_version/tests/test_autoversion.py @@ -1,10 +1,10 @@ import contextlib import functools -import imp import os import re import shlex import subprocess +import textwrap import unittest import semver @@ -115,8 +115,21 @@ def test_increment_existing_prerelease(self): def test_end_to_end(self): self.call(bump="major") filepath = os.path.join(os.path.dirname(__file__), "example.py") - example = imp.load_source("example", filepath) - self.assertEqual(example.VERSION, "20.0.0-dev.1") + with open(filepath) as fh: + content = fh.read() + self.assertEqual( + content, + textwrap.dedent( + """ + LOCK = False + RELEASE = True + VERSION = "20.0.0-dev.1" + VERSION_AGAIN = "20.0.0-dev.1" + STRICT_VERSION = "20.0.0" + UNRELATED_STRING = "apple" + """ + ).lstrip(), + ) def test_simple_config_bump(self): old, new, updates = self.call(config_path="simple.toml", bump="minor") @@ -164,9 +177,9 @@ def test_bump_patch(self): class TestUtils(unittest.TestCase): def test_is_release(self): - self.assertTrue(utils.is_release(semver.parse_version_info("1.2.3"))) - self.assertFalse(utils.is_release(semver.parse_version_info("1.2.3-RC.1"))) - self.assertFalse(utils.is_release(semver.parse_version_info("1.2.3+abc"))) + self.assertTrue(utils.is_release(semver.Version.parse("1.2.3"))) + self.assertFalse(utils.is_release(semver.Version.parse("1.2.3-RC.1"))) + self.assertFalse(utils.is_release(semver.Version.parse("1.2.3+abc"))) def test_sigfig_max(self): self.assertEqual("minor", utils.max_sigfig(["minor", "patch"])) @@ -188,20 +201,19 @@ def test_semver_diff(self): self.assertEqual( "minor", utils.semver_diff( - semver.parse_version_info("1.2.3"), semver.parse_version_info("1.3.5") + semver.Version.parse("1.2.3"), semver.Version.parse("1.3.5") ), ) self.assertEqual( "patch", utils.semver_diff( - semver.parse_version_info("1.2.3"), - semver.parse_version_info("1.2.4-RC.1"), + semver.Version.parse("1.2.3"), semver.Version.parse("1.2.4-RC.1") ), ) self.assertEqual( None, utils.semver_diff( - semver.parse_version_info("1.2.3"), semver.parse_version_info("1.2.3") + semver.Version.parse("1.2.3"), semver.Version.parse("1.2.3") ), ) @@ -215,14 +227,10 @@ def setUpClass(cls): auto_version_tool.load_config(os.path.join(test_dir, "example.toml")) def check(self, previous, current, bumps, expect): - previous = semver.parse_version_info(previous) if previous else None + previous = semver.Version.parse(previous) if previous else None self.assertEqual( expect, - str( - utils.make_new_semver( - semver.parse_version_info(current), previous, bumps - ) - ), + str(utils.make_new_semver(semver.Version.parse(current), previous, bumps)), ) def test_release_bump(self): @@ -289,7 +297,7 @@ def test_from_ancestor_version(self): { "VERSION": bumped, "VERSION_AGAIN": bumped, - "STRICT_VERSION": semver.finalize_version(bumped), + "STRICT_VERSION": str(semver.Version.parse(bumped).finalize_version()), }, ) @@ -303,7 +311,7 @@ def test_from_ancestor_release(self): { "VERSION": bumped, "VERSION_AGAIN": bumped, - "STRICT_VERSION": semver.finalize_version(bumped), + "STRICT_VERSION": str(semver.Version.parse(bumped).finalize_version()), }, ) @@ -315,7 +323,7 @@ def test_from_latest_of_all_time(self): { "VERSION": bumped, "VERSION_AGAIN": bumped, - "STRICT_VERSION": semver.finalize_version(bumped), + "STRICT_VERSION": str(semver.Version.parse(bumped).finalize_version()), }, ) @@ -327,7 +335,7 @@ def test_from_latest_of_all_time_release(self): { "VERSION": bumped, "VERSION_AGAIN": bumped, - "STRICT_VERSION": semver.finalize_version(bumped), + "STRICT_VERSION": str(semver.Version.parse(bumped).finalize_version()), }, ) @@ -348,12 +356,12 @@ def test_to_tag(self): { "VERSION": bumped, "VERSION_AGAIN": bumped, - "STRICT_VERSION": semver.finalize_version(bumped), + "STRICT_VERSION": str(semver.Version.parse(bumped).finalize_version()), }, ) version = auto_version_tool.get_dvcs_repo_latest_version_semver() self.assertEqual( - dict(version._asdict()), + dict(version.to_dict()), dict(major=5, minor=0, patch=0, build=None, prerelease="dev.1"), ) diff --git a/src/auto_version/utils.py b/src/auto_version/utils.py index 3a39d32..cd7b8cc 100644 --- a/src/auto_version/utils.py +++ b/src/auto_version/utils.py @@ -16,7 +16,7 @@ def from_text_or_none(text): """ if text is not None: try: - return semver.parse_version_info(text) + return semver.Version.parse(text) except ValueError: _LOG.debug("version string is not semver-compatible: %r", text) pass @@ -46,8 +46,12 @@ def get_semver_from_source(data): # we didn't have enough components pass - versions = [potential for potential in potentials if from_text_or_none(potential)] - release_versions = {semver.finalize_version(version) for version in versions} + actual_versions = [] + for potential in potentials: + version = from_text_or_none(potential) + if version: + actual_versions.append(version) + release_versions = {version.finalize_version() for version in actual_versions} if len(release_versions) > 1: raise ValueError( @@ -55,15 +59,15 @@ def get_semver_from_source(data): % (release_versions, known) ) - if not versions: + if not actual_versions: _LOG.debug("key pairs found: \n%r", known) raise ValueError("could not find existing semver") result = None - if versions: - result = versions[0] + if actual_versions: + result = actual_versions[0] _LOG.info("latest version found in source: %r", result) - return semver.parse_version_info(result) + return result def get_token_args(sig_fig): @@ -84,7 +88,9 @@ def max_sigfig(sigfigs): def min_sigfig(sigfigs): """Given a list of significant figures, return the smallest""" - for sig_fig in reversed(SemVerSigFig): # iterate sig figs in order of least significance + for sig_fig in reversed( + SemVerSigFig + ): # iterate sig figs in order of least significance if sig_fig in sigfigs: return sig_fig @@ -120,13 +126,15 @@ def make_new_semver(current_semver, last_release_semver, all_triggers, **overrid :param overrides: explicit values for some or all of the sigfigs :return: """ - version_string = str(current_semver) + proposed_version = current_semver # if the current version isn't a full release if not is_release(current_semver) and last_release_semver: # we check to see how important the changes are # in the triggers, compared to the changes made between the current version and previous release - if sigfig_gt(max_sigfig(all_triggers), semver_diff(current_semver, last_release_semver)): + if sigfig_gt( + max_sigfig(all_triggers), semver_diff(current_semver, last_release_semver) + ): # here, the changes are more significant than the original RC bump, so we re-bump pass else: @@ -142,23 +150,22 @@ def make_new_semver(current_semver, last_release_semver, all_triggers, **overrid if bump_sigfig: # perform an increment using the most-significant trigger - version_string = getattr(semver, "bump_" + bump_sigfig)( - str(current_semver), **get_token_args(bump_sigfig) + proposed_version = getattr(current_semver, "bump_" + bump_sigfig)( + **get_token_args(bump_sigfig) ) if sigfig_gt(bump_sigfig, SemVerSigFig.prerelease): # if we *didnt* increment sub-patch already, then we should do so # this provides the "devmode template" as previously # and ensures a simple 'bump' doesn't look like a full release - version_string = semver.bump_prerelease( - version_string, token=config.PRERELEASE_TOKEN + proposed_version = proposed_version.bump_prerelease( + token=config.PRERELEASE_TOKEN ) # perform any explicit setting of sigfigs - version_info = semver.parse_version_info(version_string) for k, v in overrides.items(): token_args = get_token_args(k) prefix = list(token_args.values()).pop() + "." if token_args else "" - setattr(version_info, "_" + k, prefix + str(v)) + setattr(proposed_version, "_" + k, prefix + str(v)) - return version_info + return proposed_version