bumpver/src/pycalver/vcs.py

124 lines
3.3 KiB
Python
Raw Normal View History

2018-09-02 21:48:12 +02:00
# This file is part of the pycalver project
# https://github.com/mbarkhau/pycalver
#
2018-11-06 21:45:33 +01:00
# Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License
2018-09-02 21:48:12 +02:00
# SPDX-License-Identifier: MIT
#
# pycalver/vcs.py (this file) is based on code from the
# bumpversion project: https://github.com/peritus/bumpversion
2018-11-06 21:45:33 +01:00
# Copyright (c) 2013-2014 Filip Noetzel - MIT License
2018-09-02 21:48:12 +02:00
import os
2018-09-03 22:23:51 +02:00
import sys
2018-09-02 21:48:12 +02:00
import logging
import tempfile
2018-09-03 22:23:51 +02:00
import typing as typ
2018-09-02 21:48:12 +02:00
import subprocess as sp
log = logging.getLogger("pycalver.vcs")
class BaseVCS:
2018-09-04 20:16:46 +02:00
_TEST_USABLE_COMMAND: typ.List[str]
2018-11-04 21:11:42 +01:00
_COMMIT_COMMAND : typ.List[str]
_STATUS_COMMAND : typ.List[str]
2018-09-04 20:16:46 +02:00
2018-09-02 21:48:12 +02:00
@classmethod
2018-09-04 20:16:46 +02:00
def commit(cls, message: str) -> None:
message_data = message.encode("utf-8")
tmp_file = tempfile.NamedTemporaryFile("wb", delete=False)
with tmp_file as fh:
fh.write(message_data)
cmd = cls._COMMIT_COMMAND + [tmp_file.name]
env = os.environ.copy()
# TODO (mb 2018-09-04): check that this works on py27,
# might need to be bytes there, idk.
2018-11-04 21:11:42 +01:00
env['HGENCODING'] = "utf-8"
2018-09-04 20:16:46 +02:00
sp.check_output(cmd, env=env)
os.unlink(tmp_file.name)
2018-09-02 21:48:12 +02:00
@classmethod
2018-09-04 20:16:46 +02:00
def is_usable(cls) -> bool:
2018-09-02 21:48:12 +02:00
try:
2018-11-04 21:11:42 +01:00
return sp.call(cls._TEST_USABLE_COMMAND, stderr=sp.PIPE, stdout=sp.PIPE) == 0
2018-09-02 21:48:12 +02:00
except OSError as e:
if e.errno == 2:
# mercurial is not installed then, ok.
return False
raise
@classmethod
2018-09-04 20:16:46 +02:00
def dirty_files(cls) -> typ.List[str]:
2018-09-02 21:48:12 +02:00
status_output = sp.check_output(cls._STATUS_COMMAND)
2018-09-03 00:14:10 +02:00
return [
line.decode("utf-8")[2:].strip()
2018-09-02 21:48:12 +02:00
for line in status_output.splitlines()
if not line.strip().startswith(b"??")
]
2018-09-03 22:23:51 +02:00
@classmethod
def assert_not_dirty(cls, filepaths: typ.Set[str], allow_dirty=False) -> None:
dirty_files = cls.dirty_files()
if dirty_files:
log.warn(f"{cls.__name__} working directory is not clean:")
2018-09-05 09:24:01 +02:00
for dirty_file in dirty_files:
log.warn(" " + dirty_file)
2018-09-03 22:23:51 +02:00
if not allow_dirty and dirty_files:
sys.exit(1)
dirty_pattern_files = set(dirty_files) & filepaths
if dirty_pattern_files:
log.error("Not commiting when pattern files are dirty:")
2018-09-05 09:24:01 +02:00
for dirty_file in dirty_pattern_files:
log.warn(" " + dirty_file)
2018-09-03 22:23:51 +02:00
sys.exit(1)
2018-09-02 21:48:12 +02:00
class Git(BaseVCS):
2018-11-06 21:45:33 +01:00
_TEST_USABLE_COMMAND = ["git", "rev-parse", '--git-dir']
_COMMIT_COMMAND = ["git", "commit" , '-F']
_STATUS_COMMAND = ["git", "status" , '--porcelain']
2018-09-02 21:48:12 +02:00
@classmethod
def tag(cls, name):
sp.check_output(["git", "tag", name])
2018-09-03 00:14:10 +02:00
@classmethod
def add_path(cls, path):
sp.check_output(["git", "add", "--update", path])
2018-09-02 21:48:12 +02:00
class Mercurial(BaseVCS):
2018-11-04 21:11:42 +01:00
_TEST_USABLE_COMMAND = ["hg", 'root']
2018-11-06 21:45:33 +01:00
_COMMIT_COMMAND = ["hg", "commit", '--logfile']
_STATUS_COMMAND = ["hg", "status", '-mard']
2018-09-02 21:48:12 +02:00
@classmethod
def tag(cls, name):
sp.check_output(["hg", "tag", name])
2018-09-03 00:14:10 +02:00
@classmethod
def add_path(cls, path):
pass
2018-09-02 21:48:12 +02:00
VCS = [Git, Mercurial]
2018-11-06 21:45:33 +01:00
def get_vcs() -> typ.Type[BaseVCS]:
"""Get appropriate sub"""
2018-09-02 21:48:12 +02:00
for vcs in VCS:
2018-09-03 00:14:10 +02:00
if vcs.is_usable():
return vcs
2018-09-02 21:48:12 +02:00
2018-11-06 21:45:33 +01:00
raise OSError("No such directory .hg/ or .git/ ")