diff --git a/setup.cfg b/setup.cfg index 6dac5bf..86bd85a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,4 +32,4 @@ patterns = [pycalver:file:README.rst] patterns = badge/CalVer-{calver}{build}-{release}-blue.svg - :alt: CalVer {version} + :alt: CalVer {version} \ No newline at end of file diff --git a/src/pycalver/__main__.py b/src/pycalver/__main__.py index a196558..a73be68 100644 --- a/src/pycalver/__main__.py +++ b/src/pycalver/__main__.py @@ -146,7 +146,7 @@ def init(dry: bool) -> None: "--commit", default=True, is_flag=True, - help="Tag the commit.", + help="Commit after updating version strings.", ) @click.option( "--tag", @@ -154,7 +154,24 @@ def init(dry: bool) -> None: is_flag=True, help="Tag the commit.", ) -def bump(release: str, verbose: bool, dry: bool, commit: bool, tag: bool) -> None: +@click.option( + "--allow-dirty", + default=False, + is_flag=True, + help=( + "Commit even when working directory is has uncomitted changes. " + "(WARNING: The commit will still be aborted if there are uncomitted " + "to files with version strings." + ), +) +def bump( + release: str, + verbose: bool, + dry: bool, + commit: bool, + tag: bool, + allow_dirty: bool, +) -> None: _init_loggers(verbose) if release and release not in parse.VALID_RELESE_VALUES: @@ -179,6 +196,25 @@ def bump(release: str, verbose: bool, dry: bool, commit: bool, tag: bool) -> Non if dry: log.info("Running with '--dry', showing diffs instead of updating files.") + _vcs = vcs.get_vcs() + dirty_files = _vcs.dirty_files() + + if dirty_files: + log.warn(f"{_vcs.__name__} working directory is not clean:") + for file in dirty_files: + log.warn(" " + file) + + if not allow_dirty and dirty_files: + sys.exit(1) + + pattern_files = cfg["file_patterns"].keys() + dirty_pattern_files = set(dirty_files) & set(pattern_files) + if dirty_pattern_files: + log.error("Not commiting when pattern files are dirty:") + for file in dirty_pattern_files: + log.warn(" " + file) + sys.exit(1) + matches: typ.List[parse.PatternMatch] for filepath, patterns in cfg["file_patterns"].items(): with io.open(filepath, mode="rt", encoding="utf-8") as fh: @@ -203,10 +239,10 @@ def bump(release: str, verbose: bool, dry: bool, commit: bool, tag: bool) -> Non tofile="b/" + filepath, ))) - # if not dry: - # new_content = "\n".join(new_lines) - # with io.open(filepath, mode="wt", encoding="utf-8") as fh: - # fh.write(new_content) + if not dry: + new_content = "\n".join(new_lines) + with io.open(filepath, mode="wt", encoding="utf-8") as fh: + fh.write(new_content) if dry: return @@ -214,7 +250,6 @@ def bump(release: str, verbose: bool, dry: bool, commit: bool, tag: bool) -> Non if not commit: return - v = vcs.get_vcs() - if v is None: + if _vcs is None: log.warn("Version Control System not found, aborting commit.") return diff --git a/src/pycalver/vcs.py b/src/pycalver/vcs.py index 42ce2da..2be6797 100644 --- a/src/pycalver/vcs.py +++ b/src/pycalver/vcs.py @@ -17,12 +17,6 @@ import subprocess as sp log = logging.getLogger("pycalver.vcs") -class WorkingDirectoryIsDirtyException(Exception): - - def __init__(self, message): - self.message = message - - class BaseVCS: @classmethod @@ -50,21 +44,14 @@ class BaseVCS: raise @classmethod - def assert_nondirty(cls): + def dirty_files(cls): status_output = sp.check_output(cls._STATUS_COMMAND) - lines = [ - line.strip() + return [ + line.decode("utf-8")[2:].strip() for line in status_output.splitlines() if not line.strip().startswith(b"??") ] - if lines: - cleaned_output = b"\n".join(lines) - cls_name = cls.__name__ - raise WorkingDirectoryIsDirtyException( - f"{cls_name} working directory is not clean:\n{cleaned_output}" - ) - class Git(BaseVCS): @@ -76,6 +63,10 @@ class Git(BaseVCS): def tag(cls, name): sp.check_output(["git", "tag", name]) + @classmethod + def add_path(cls, path): + sp.check_output(["git", "add", "--update", path]) + class Mercurial(BaseVCS): @@ -87,25 +78,17 @@ class Mercurial(BaseVCS): def tag(cls, name): sp.check_output(["hg", "tag", name]) + @classmethod + def add_path(cls, path): + pass + VCS = [Git, Mercurial] -def get_vcs(allow_dirty=False): +def get_vcs(): for vcs in VCS: - if not vcs.is_usable(): - continue - - if not allow_dirty: - try: - vcs.assert_nondirty() - except WorkingDirectoryIsDirtyException as e: - log.warn( - f"{e.message}\n\n" - f"Use --allow-dirty to override this if you know what you're doing." - ) - raise - - return vcs + if vcs.is_usable(): + return vcs return None diff --git a/test/test_parse.py b/test/test_parse.py new file mode 100644 index 0000000..e69de29