From 03e2e07a86fcb0596394efeeca7712ce0713d09f Mon Sep 17 00:00:00 2001 From: mtkennerly Date: Sat, 30 Mar 2019 15:22:08 -0400 Subject: [PATCH] Add Darcs support --- CHANGELOG.md | 1 + README.md | 1 + dunamai/__init__.py | 59 ++++++++++++++++++++++++++++++++++++++++++- dunamai/__main__.py | 2 ++ tests/test_dunamai.py | 31 +++++++++++++++++++++++ tests/test_main.py | 1 + 6 files changed, 94 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18401e1..e4cd89c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * Added built-in Semantic Versioning output style in addition to PEP 440. * Added style validation for custom output formats. +* Added Darcs support. ## v0.4.0 (2019-03-29) diff --git a/README.md b/README.md index 0462e5e..60ee0f2 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ control system. * Version control system support: * Git * Mercurial + * Darcs * Version styles: * [PEP 440](https://www.python.org/dev/peps/pep-0440) * [Semantic Versioning](https://semver.org) diff --git a/dunamai/__init__.py b/dunamai/__init__.py index 8854006..5e2dd87 100644 --- a/dunamai/__init__.py +++ b/dunamai/__init__.py @@ -317,6 +317,62 @@ def from_mercurial(cls, pattern: str = _VERSION_PATTERN) -> "Version": return cls(base, pre=pre, post=post, dev=dev, commit=commit, dirty=dirty) + @classmethod + def from_darcs(cls, pattern: str = _VERSION_PATTERN) -> "Version": + r""" + Determine a version based on Darcs tags. + + :param pattern: Regular expression matched against the version source. + This should contain one capture group named `base` corresponding to + the release segment of the source, and optionally another two groups + named `pre_type` and `pre_number` corresponding to the type (a, b, rc) + and number of prerelease. For example, with a tag like v0.1.0, the + pattern would be `v(?P\d+\.\d+\.\d+)`. + """ + code, msg = _run_cmd("darcs status") + if code in [0, 1]: + dirty = msg != "No changes!" + else: + raise RuntimeError("Darcs returned code {}".format(code)) + + code, description = _run_cmd('darcs log --last 1') + if code == 0: + if not description: + commit = None + else: + commit = description.split()[1].strip() + else: + raise RuntimeError("Darcs returned code {}".format(code)) + + code, description = _run_cmd('darcs show tags') + if code == 0: + if not description: + return cls("0.0.0", post=0, dev=0, commit=commit, dirty=dirty) + tag = description.split()[0] + else: + raise RuntimeError("Darcs returned code {}".format(code)) + + code, description = _run_cmd('darcs log --from-tag {}'.format(tag)) + if code == 0: + # The tag itself is in the list, so offset by 1. + distance = -1 + for line in description.splitlines(): + if line.startswith("patch "): + distance += 1 + else: + raise RuntimeError("Darcs returned code {}".format(code)) + + base, pre = _match_version_pattern(pattern, tag) + + if distance > 0: + post = distance + dev = 0 + else: + post = None + dev = None + + return cls(base, pre=pre, post=post, dev=dev, commit=commit, dirty=dirty) + @classmethod def from_any_vcs(cls, pattern: str = None) -> "Version": """ @@ -325,13 +381,14 @@ def from_any_vcs(cls, pattern: str = None) -> "Version": :param pattern: Regular expression matched against the version source. The default value defers to the VCS-specific `from_` functions. """ - vcs = _find_higher_dir(".git", ".hg") + vcs = _find_higher_dir(".git", ".hg", "_darcs") if not vcs: raise RuntimeError("Unable to detect version control system.") callbacks = { ".git": cls.from_git, ".hg": cls.from_mercurial, + "_darcs": cls.from_darcs, } arguments = [] diff --git a/dunamai/__main__.py b/dunamai/__main__.py index 86213c1..f21712f 100644 --- a/dunamai/__main__.py +++ b/dunamai/__main__.py @@ -11,6 +11,7 @@ class Vcs(Enum): Any = "any" Git = "git" Mercurial = "mercurial" + Darcs = "darcs" class Style(Enum): @@ -85,6 +86,7 @@ def from_vcs( Vcs.Any: Version.from_any_vcs, Vcs.Git: Version.from_git, Vcs.Mercurial: Version.from_mercurial, + Vcs.Darcs: Version.from_darcs, } arguments = [] diff --git a/tests/test_dunamai.py b/tests/test_dunamai.py index 5ef6525..c97507d 100644 --- a/tests/test_dunamai.py +++ b/tests/test_dunamai.py @@ -418,6 +418,37 @@ def test__version__from_mercurial(tmp_path): assert from_any_vcs() == Version("0.1.0", post=1, dev=0, commit="abc") +@pytest.mark.skipif(shutil.which("darcs") is None, reason="Requires Darcs") +def test__version__from_darcs(tmp_path): + vcs = tmp_path / "dunamai-darcs" + vcs.mkdir() + run = make_run_callback(vcs) + from_vcs = make_from_callback(Version.from_darcs) + + with chdir(vcs): + run("darcs init") + assert from_vcs() == Version("0.0.0", post=0, dev=0, commit=None, dirty=False) + + (vcs / "foo.txt").write_text("hi") + assert from_vcs() == Version("0.0.0", post=0, dev=0, commit=None, dirty=True) + + run('darcs add foo.txt') + run('darcs record -am "Initial commit"') + assert from_vcs() == Version("0.0.0", post=0, dev=0, commit="abc", dirty=False) + + run("darcs tag v0.1.0") + assert from_vcs() == Version("0.1.0", commit="abc", dirty=False) + assert run("dunamai from darcs") == "0.1.0" + assert run("dunamai from any") == "0.1.0" + + (vcs / "foo.txt").write_text("bye") + assert from_vcs() == Version("0.1.0", commit="abc", dirty=True) + + run('darcs record -am "Second"') + assert from_vcs() == Version("0.1.0", post=1, dev=0, commit="abc") + assert from_any_vcs() == Version("0.1.0", post=1, dev=0, commit="abc") + + def test__version__from_any_vcs(tmp_path): with chdir(tmp_path): with pytest.raises(RuntimeError): diff --git a/tests/test_main.py b/tests/test_main.py index c9ed046..11af5ad 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -10,6 +10,7 @@ def test__parse_args__from(): ) assert parse_args(["from", "git"]).vcs == "git" assert parse_args(["from", "mercurial"]).vcs == "mercurial" + assert parse_args(["from", "darcs"]).vcs == "darcs" assert parse_args(["from", "any", "--pattern", r"\d+"]).pattern == r"\d+" assert parse_args(["from", "any", "--metadata"]).metadata is True assert parse_args(["from", "any", "--no-metadata"]).metadata is False