Skip to content

API

Bump'R: Version bumper and Python package releaser

  • Clean-up release artifact
  • Bump version and tag it
  • Build a source distrbution and upload on PyPI
  • Update version for new develpoment cycle
  • Can run test suite before
  • Can be customized with a config file
  • Extensible with hooks

config

BumprConfigParser (RawConfigParser)

A config parser with optionnal implicit bumpr: prefix on sections.

Allow better isolation in setup.cfg.

Source code in bumpr/config.py
class BumprConfigParser(RawConfigParser):
    """
    A config parser with optionnal implicit `bumpr:` prefix on sections.

    Allow better isolation in setup.cfg.
    """

    prefix = "bumpr"

    def candidate_sections(self, section):
        return [section, "{0}:{1}".format(self.prefix, section)]

    def has_section(self, section):
        sections = self.candidate_sections(section)
        return any(RawConfigParser.has_section(self, section) for section in sections)

    def options(self, section):
        for section in self.candidate_sections(section):
            if RawConfigParser.has_section(self, section):
                return RawConfigParser.options(self, section)

    def has_option(self, section, option):
        sections = self.candidate_sections(section)
        return any(RawConfigParser.has_option(self, section, option) for section in sections)

    def get(self, section, option, **kwargs):
        for section in self.candidate_sections(section):
            if RawConfigParser.has_option(self, section, option):
                return RawConfigParser.get(self, section, option)

    def getboolean(self, section, option):
        for section in self.candidate_sections(section):
            if RawConfigParser.has_option(self, section, option):
                return RawConfigParser.getboolean(self, section, option)

    def items(self, section):
        for section in self.candidate_sections(section):
            if RawConfigParser.has_section(self, section):
                return RawConfigParser.items(self, section)

get(self, section, option, **kwargs)

Get an option value for a given section.

If vars' is provided, it must be a dictionary. The option is looked up invars' (if provided), section', and inDEFAULTSECT' in that order. If the key is not found and fallback' is provided, it is used as a fallback value.None' can be provided as a `fallback' value.

If interpolation is enabled and the optional argument `raw' is False, all interpolations are expanded in the return values.

Arguments raw',vars', and `fallback' are keyword only.

The section DEFAULT is special.

Source code in bumpr/config.py
def get(self, section, option, **kwargs):
    for section in self.candidate_sections(section):
        if RawConfigParser.has_option(self, section, option):
            return RawConfigParser.get(self, section, option)

has_option(self, section, option)

Check for the existence of a given option in a given section. If the specified section' is None or an empty string, DEFAULT is assumed. If the specifiedsection' does not exist, returns False.

Source code in bumpr/config.py
def has_option(self, section, option):
    sections = self.candidate_sections(section)
    return any(RawConfigParser.has_option(self, section, option) for section in sections)

has_section(self, section)

Indicate whether the named section is present in the configuration.

The DEFAULT section is not acknowledged.

Source code in bumpr/config.py
def has_section(self, section):
    sections = self.candidate_sections(section)
    return any(RawConfigParser.has_section(self, section) for section in sections)

items(self, section)

Return a list of (name, value) tuples for each option in a section.

All % interpolations are expanded in the return values, based on the defaults passed into the constructor, unless the optional argument raw' is true. Additional substitutions may be provided using thevars' argument, which must be a dictionary whose contents overrides any pre-existing defaults.

The section DEFAULT is special.

Source code in bumpr/config.py
def items(self, section):
    for section in self.candidate_sections(section):
        if RawConfigParser.has_section(self, section):
            return RawConfigParser.items(self, section)

options(self, section)

Return a list of option names for the given section name.

Source code in bumpr/config.py
def options(self, section):
    for section in self.candidate_sections(section):
        if RawConfigParser.has_section(self, section):
            return RawConfigParser.options(self, section)

helpers

ObjectDict (dict)

A dictionnary with object-like attribute access and depp merge

Source code in bumpr/helpers.py
class ObjectDict(dict):
    """A dictionnary with object-like attribute access and depp merge"""

    def __init__(self, *args, **kwargs):
        self.update(*args, **kwargs)

    def __getattr__(self, key):
        return self[key]

    def __setattr__(self, key, value):
        if isinstance(value, dict) and not isinstance(value, ObjectDict):
            value = ObjectDict(value)
        self[key] = value

    def __setitem__(self, key, value):
        if isinstance(value, dict) and not isinstance(value, ObjectDict):
            value = ObjectDict(value)
        super(ObjectDict, self).__setitem__(key, value)

    def update(self, *args, **kwargs):
        for key, value in dict(*args, **kwargs).items():
            if isinstance(value, dict) and not isinstance(value, ObjectDict):
                value = ObjectDict(value)
            self[key] = value

    def merge(self, *args, **kwargs):
        for key, value in dict(*args, **kwargs).items():
            if isinstance(value, dict):
                if not isinstance(value, ObjectDict):
                    value = ObjectDict(value)
                if key in self and isinstance(self[key], ObjectDict):
                    self[key].merge(value)
                    continue
            self[key] = value

update(self, *args, **kwargs)

D.update([E, ]**F) -> None. Update D from dict/iterable E and F. If E is present and has a .keys() method, then does: for k in E: D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]

Source code in bumpr/helpers.py
def update(self, *args, **kwargs):
    for key, value in dict(*args, **kwargs).items():
        if isinstance(value, dict) and not isinstance(value, ObjectDict):
            value = ObjectDict(value)
        self[key] = value

check_output(*args, **kwargs)

A wrapper for preconfigured calls to subprocess.check_output

Source code in bumpr/helpers.py
def check_output(*args, **kwargs):
    """A wrapper for preconfigured calls to subprocess.check_output"""
    return subprocess.check_output(
        stderr=subprocess.STDOUT, universal_newlines=True, *args, **kwargs
    )

hooks

ChangelogHook (Hook)

This hook bump the changelog version header and prepare a new section for the next release.

Source code in bumpr/hooks.py
class ChangelogHook(Hook):
    """
    This hook bump the changelog version header and prepare a new section for the next release.
    """

    key = "changelog"
    defaults = {
        "file": None,
        "separator": "-",
        "bump": "{version} ({date:%Y-%m-%d})",
        "prepare": "Current",
        "empty": "Nothing yet",
    }

    def validate(self):
        if not self.config.get("file"):
            raise BumprError("Changelog file has not been specified")
        elif not exists(self.config.file):
            raise BumprError("Changelog file does not exists")

    def bump(self, replacements):
        with open(self.config.file, "r", encoding=self.releaser.config.encoding) as changelog_file:
            before = changelog_file.read()
            after = before.replace(self.dev_header(), self.bumped_header())
        self.releaser.perform(self.config.file, before, after)

    def prepare(self, replacements):
        next_header = "\n".join(
            (
                self.dev_header(),
                "",
                "- {0}".format(self.config.empty),
                "",
                self.bumped_header(),
            )
        )
        with open(self.config.file, "r", encoding=self.releaser.config.encoding) as changelog_file:
            before = changelog_file.read()
            after = before.replace(self.bumped_header(), next_header)
        self.releaser.perform(self.config.file, before, after)

    def dev_header(self):
        return self.underline(self.config.prepare)

    def bumped_header(self):
        title = self.config.bump.format(
            version=self.releaser.version,
            date=self.releaser.timestamp,
            **self.releaser.version.__dict__,
        )
        return self.underline(title)

    def underline(self, text):
        if self.config.separator:
            return "\n".join((text, len(text) * self.config.separator))
        else:
            return text

validate(self)

Override this method to implement initial validation

Source code in bumpr/hooks.py
def validate(self):
    if not self.config.get("file"):
        raise BumprError("Changelog file has not been specified")
    elif not exists(self.config.file):
        raise BumprError("Changelog file does not exists")

CommandsHook (Hook)

This hook execute commands

Source code in bumpr/hooks.py
class CommandsHook(Hook):
    """
    This hook execute commands
    """

    key = "commands"
    defaults = {
        "bump": None,
        "prepare": None,
    }

    def bump(self, replacements):
        if self.config.bump:
            replacements = dict(
                version=self.releaser.version,
                tag=self.releaser.tag_label,
                date=self.releaser.timestamp,
                **self.releaser.version.__dict__,
            )
            execute(
                self.config.bump,
                replacements=replacements,
                verbose=self.verbose,
                dryrun=self.dryrun,
            )

    def prepare(self, replacements):
        if self.config.prepare:
            replacements = dict(
                version=self.releaser.next_version,
                tag=self.releaser.tag_label,
                date=self.releaser.timestamp,
                **self.releaser.next_version.__dict__,
            )
            execute(
                self.config.prepare,
                replacements=replacements,
                verbose=self.verbose,
                dryrun=self.dryrun,
            )

Hook

Source code in bumpr/hooks.py
class Hook:
    key: str = ""
    defaults: dict[str, Optional[str]] = {}

    def __init__(self, releaser):
        self.releaser = releaser
        self.verbose = releaser.config.verbose
        self.dryrun = releaser.config.dryrun
        self.config = releaser.config[self.key]
        self.validate()

    def validate(self):
        """Override this method to implement initial validation"""

    def bump(self, replacements):
        pass

    def prepare(self, replacements):
        pass

validate(self)

Override this method to implement initial validation

Source code in bumpr/hooks.py
def validate(self):
    """Override this method to implement initial validation"""

ReadTheDocHook (Hook)

This hook set the readthedoc url corresponding to the version

Source code in bumpr/hooks.py
class ReadTheDocHook(Hook):
    """
    This hook set the readthedoc url corresponding to the version
    """

    key = "readthedoc"
    defaults = {
        "id": None,
        "url": "https://{id}.readthedocs.io/en/{tag}",
        "badge": "https://readthedocs.org/projects/{id}/badge/?version={tag}",
        "bump": "{version}",
        "prepare": "latest",
    }

    def url(self, tag):
        return self.config.url.format(id=self.config.id, tag=tag)

    def badge(self, tag):
        return self.config.badge.format(id=self.config.id, tag=tag)

    def bump(self, replacements):
        replacements.insert(0, (self.badge("latest"), self.badge(self.releaser.tag_label)))
        replacements.insert(0, (self.url("latest"), self.url(self.releaser.tag_label)))

    def prepare(self, replacements):
        replacements.insert(0, (self.badge(self.releaser.tag_label), self.badge("latest")))
        replacements.insert(0, (self.url(self.releaser.tag_label), self.url("latest")))

ReplaceHook (Hook)

This hook perform replacements in files

Source code in bumpr/hooks.py
class ReplaceHook(Hook):
    """
    This hook perform replacements in files
    """

    key = "replace"
    defaults: dict[str, Optional[str]] = {}

    def bump(self, replacements):
        replacements.insert(
            0,
            (
                self.config.dev.format(
                    version=self.releaser.prev_version,
                    tag=self.releaser.tag_label,
                    date=self.releaser.timestamp,
                    **self.releaser.prev_version.__dict__,
                ),
                self.config.stable.format(
                    version=self.releaser.version,
                    tag=self.releaser.tag_label,
                    date=self.releaser.timestamp,
                    **self.releaser.version.__dict__,
                ),
            ),
        )

    def prepare(self, replacements):
        replacements.insert(
            0,
            (
                self.config.stable.format(
                    version=self.releaser.version,
                    tag=self.releaser.tag_label,
                    date=self.releaser.timestamp,
                    **self.releaser.version.__dict__,
                ),
                self.config.dev.format(
                    version=self.releaser.next_version,
                    tag=self.releaser.tag_label,
                    date=self.releaser.timestamp,
                    **self.releaser.next_version.__dict__,
                ),
            ),
        )

log

ANSIFormatter (Formatter)

Convert a `logging.LogRecord' object into colored text, using ANSI escape sequences.

Source code in bumpr/log.py
class ANSIFormatter(Formatter):
    """
    Convert a `logging.LogRecord' object into colored text, using ANSI
    escape sequences.
    """

    def format(self, record):
        msg = record.getMessage()
        if record.levelname == "INFO":
            return ansi("cyan", "-> ") + msg
        elif record.levelname == "DRYRUN":
            return ansi("magenta", "dryrun-> ") + msg
        elif record.levelname == "DIFF":
            if msg.startswith("+"):
                return ansi("green", msg)
            elif msg.startswith("-"):
                return ansi("red", msg)
            else:
                return msg
        else:
            color = LEVEL_COLORS.get(record.levelname, "white")
            return ansi(color, record.levelname.lower()) + ": " + msg

format(self, record)

Format the specified record as text.

The record's attribute dictionary is used as the operand to a string formatting operation which yields the returned string. Before formatting the dictionary, a couple of preparatory steps are carried out. The message attribute of the record is computed using LogRecord.getMessage(). If the formatting string uses the time (as determined by a call to usesTime(), formatTime() is called to format the event time. If there is exception information, it is formatted using formatException() and appended to the message.

Source code in bumpr/log.py
def format(self, record):
    msg = record.getMessage()
    if record.levelname == "INFO":
        return ansi("cyan", "-> ") + msg
    elif record.levelname == "DRYRUN":
        return ansi("magenta", "dryrun-> ") + msg
    elif record.levelname == "DIFF":
        if msg.startswith("+"):
            return ansi("green", msg)
        elif msg.startswith("-"):
            return ansi("red", msg)
        else:
            return msg
    else:
        color = LEVEL_COLORS.get(record.levelname, "white")
        return ansi(color, record.levelname.lower()) + ": " + msg

TextFormatter (Formatter)

Convert a `logging.LogRecord' object into text.

Source code in bumpr/log.py
class TextFormatter(Formatter):
    """
    Convert a `logging.LogRecord' object into text.
    """

    def format(self, record):
        if not record.levelname or record.levelname in ("INFO", "DIFF"):
            return record.getMessage()
        elif record.levelname == "DRYRUN":
            return "dryrun-> {0}".format(record.getMessage())
        else:
            return record.levelname.lower() + ": " + record.getMessage()

format(self, record)

Format the specified record as text.

The record's attribute dictionary is used as the operand to a string formatting operation which yields the returned string. Before formatting the dictionary, a couple of preparatory steps are carried out. The message attribute of the record is computed using LogRecord.getMessage(). If the formatting string uses the time (as determined by a call to usesTime(), formatTime() is called to format the event time. If there is exception information, it is formatted using formatException() and appended to the message.

Source code in bumpr/log.py
def format(self, record):
    if not record.levelname or record.levelname in ("INFO", "DIFF"):
        return record.getMessage()
    elif record.levelname == "DRYRUN":
        return "dryrun-> {0}".format(record.getMessage())
    else:
        return record.levelname.lower() + ": " + record.getMessage()

ansi(color, text)

Wrap text in an ansi escape sequence

Source code in bumpr/log.py
def ansi(color, text):
    """Wrap text in an ansi escape sequence"""
    code = COLOR_CODES[color]
    return "\033[1;{0}m{1}{2}".format(code, text, RESET_TERM)

releaser

Releaser

Release workflow executor

Source code in bumpr/releaser.py
class Releaser:
    """
    Release workflow executor
    """

    def __init__(self, config):
        self.config = config

        with open(config.file) as f:
            match = re.search(config.regex, f.read())
            try:
                version_string = match.group("version")
                self.prev_version = Version.parse(version_string)
            except Exception:
                raise BumprError("Unable to extract version from {0}".format(config.file))

        logger.debug("Previous version: {0}".format(self.prev_version))

        self.version = self.prev_version.copy()
        self.version.bump(config.bump.part, config.bump.unsuffix, config.bump.suffix)
        logger.debug("Bumped version: {0}".format(self.version))

        self.next_version = self.version.copy()
        self.next_version.bump(config.prepare.part, config.prepare.unsuffix, config.prepare.suffix)
        logger.debug("Prepared version: {0}".format(self.next_version))

        self.tag_label = self.config.tag_format.format(version=self.version)
        logger.debug("Tag: {0}".format(self.tag_label))
        if self.config.tag_annotation:
            self.tag_annotation = self.config.tag_annotation.format(version=self.version)
            logger.debug("Tag annotation: {0}".format(self.tag_annotation))

        self.timestamp = None

        if config.vcs:
            self.vcs = VCS[config.vcs](verbose=config.verbose)
            self.vcs.validate(dryrun=config.dryrun)

        if config.dryrun:
            self.modified = {}
            self.diffs = {}

        self.hooks = [hook(self) for hook in HOOKS if self.config[hook.key]]

    def execute(self, command, version=None, verbose=None):
        version = version or self.version
        verbose = verbose or self.config.verbose
        replacements = dict(version=version, date=self.timestamp, **version.__dict__)
        execute(
            command,
            replacements=replacements,
            dryrun=self.config.dryrun,
            verbose=verbose,
        )

    def release(self):
        self.timestamp = datetime.now()

        if self.config.bump_only:
            self.bump()
        elif self.config.prepare_only:
            self.prepare()
        else:
            self.clean()
            self.test()
            self.bump()
            self.publish()
            self.prepare()
            self.push()

    def test(self):
        if self.config.tests:
            if self.config.skip_tests:
                logger.info("Skip test suite")
                return
            logger.info("Running test suite")
            self.execute(self.config.tests, verbose=True)

    def bump(self):
        logger.info("Bump version %s", self.version)

        replacements = [(str(self.prev_version), str(self.version))]

        for hook in self.hooks:
            hook.bump(replacements)

        self.bump_files(replacements)

        if self.config.vcs:
            self.commit(
                self.config.bump.message.format(
                    version=self.version,
                    tag=self.tag_label,
                    date=self.timestamp,
                    **self.version.__dict__,
                )
            )
            self.tag()

        if self.config.dryrun:
            self.display_diff()
            self.diffs.clear()

    def prepare(self):
        if self.version == self.next_version:
            logger.info("Skip prepare phase")
            return
        logger.info("Prepare version %s", self.next_version)

        replacements = [(str(self.version), str(self.next_version))]

        for hook in self.hooks:
            hook.prepare(replacements)

        self.bump_files(replacements)

        if self.config.vcs:
            self.commit(
                self.config.prepare.message.format(
                    version=self.next_version,
                    tag=self.tag_label,
                    date=self.timestamp,
                    **self.next_version.__dict__,
                )
            )

        if self.config.dryrun:
            self.display_diff()

    def clean(self):
        """Clean the workspace"""
        if self.config.clean:
            logger.info("Cleaning")
            self.execute(self.config.clean)

    def perform(self, filename, before, after):
        if before == after:
            return
        if self.config.dryrun:
            self.modified[filename] = after
            diff = unified_diff(before.split("\n"), after.split("\n"), lineterm="")
            self.diffs[filename] = diff
        else:
            with open(filename, "w", encoding=self.config.encoding) as f:
                f.write(after)

    def bump_files(self, replacements):
        for filename in [self.config.file] + self.config.files:
            if self.config.dryrun and filename in self.modified:
                before = self.modified[filename]
            else:
                with open(filename, "r", encoding=self.config.encoding) as current_file:
                    before = current_file.read()
            after = before
            for token, replacement in replacements:
                after = after.replace(token, replacement)
            self.perform(filename, before, after)

    def publish(self):
        """Publish the current release to PyPI"""
        if self.config.publish:
            logger.info("Publish")
            self.execute(self.config.publish)

    def tag(self):
        if self.config.commit and self.config.tag:
            if self.config.tag_annotation:
                logger.debug("Tag: %s Annotation: %s", self.tag_label, self.tag_annotation)
                if not self.config.dryrun:
                    self.vcs.tag(self.tag_label, self.tag_annotation)
                else:
                    logger.dryrun(
                        "tag: {0} annotation: {1}".format(self.tag_label, self.tag_annotation)
                    )
            else:
                logger.debug("Tag: %s", self.tag_label)
                if not self.config.dryrun:
                    self.vcs.tag(self.tag_label)
                else:
                    logger.dryrun("tag: {0}".format(self.tag_label))

    def commit(self, message):
        if self.config.commit:
            logger.debug("Commit: %s", message)
            if not self.config.dryrun:
                self.vcs.commit(message)
            else:
                logger.dryrun("commit: {0}".format(message))

    def push(self):
        if self.config.vcs and self.config.commit and self.config.push:
            logger.info("Push to upstream repository")
            if not self.config.dryrun:
                self.vcs.push()
            else:
                logger.dryrun("push to remote repository")

    def display_diff(self):
        for filename, diff in self.diffs.items():
            logger.diff(filename)
            for line in diff:
                logger.diff(line)
            logger.diff("")

clean(self)

Clean the workspace

Source code in bumpr/releaser.py
def clean(self):
    """Clean the workspace"""
    if self.config.clean:
        logger.info("Cleaning")
        self.execute(self.config.clean)

publish(self)

Publish the current release to PyPI

Source code in bumpr/releaser.py
def publish(self):
    """Publish the current release to PyPI"""
    if self.config.publish:
        logger.info("Publish")
        self.execute(self.config.publish)

vcs

BaseVCS

Source code in bumpr/vcs.py
class BaseVCS:
    def __init__(self, verbose=False):
        self.verbose = verbose

    def execute(self, command):
        """Execute a command"""
        execute(command, verbose=self.verbose)

    def validate(self, dryrun=False):
        """Ensure the working dir is a repository and there is no modified files"""
        raise NotImplementedError

    def commit(self, message):
        """Commit all modified files"""
        raise NotImplementedError

    def tag(self, name, annotation=None):
        """Create a tag"""
        raise NotImplementedError

    def push(self):
        """Push changes to remote repository"""
        raise NotImplementedError

commit(self, message)

Commit all modified files

Source code in bumpr/vcs.py
def commit(self, message):
    """Commit all modified files"""
    raise NotImplementedError

execute(self, command)

Execute a command

Source code in bumpr/vcs.py
def execute(self, command):
    """Execute a command"""
    execute(command, verbose=self.verbose)

push(self)

Push changes to remote repository

Source code in bumpr/vcs.py
def push(self):
    """Push changes to remote repository"""
    raise NotImplementedError

tag(self, name, annotation=None)

Create a tag

Source code in bumpr/vcs.py
def tag(self, name, annotation=None):
    """Create a tag"""
    raise NotImplementedError

validate(self, dryrun=False)

Ensure the working dir is a repository and there is no modified files

Source code in bumpr/vcs.py
def validate(self, dryrun=False):
    """Ensure the working dir is a repository and there is no modified files"""
    raise NotImplementedError

Bazaar (BaseVCS)

Source code in bumpr/vcs.py
class Bazaar(BaseVCS):
    def validate(self, dryrun=False):
        if not isdir(".bzr"):
            raise BumprError("Current directory is not a bazaar repopsitory")

        for line in execute("bzr status --short", verbose=False).splitlines():
            if not line.startswith("?"):
                if dryrun:
                    log.warning(MSG)
                    break
                else:
                    raise BumprError(MSG)

    def commit(self, message):
        self.execute(["bzr", "commit", "-m", message])

    def tag(self, name, annotation=None):
        if annotation:
            log.warning("Tag annotation is not supported by Bazaar")
        self.execute(["bzr", "tag", name])

    def push(self):
        self.execute(["bzr", "push"])

commit(self, message)

Commit all modified files

Source code in bumpr/vcs.py
def commit(self, message):
    self.execute(["bzr", "commit", "-m", message])

push(self)

Push changes to remote repository

Source code in bumpr/vcs.py
def push(self):
    self.execute(["bzr", "push"])

tag(self, name, annotation=None)

Create a tag

Source code in bumpr/vcs.py
def tag(self, name, annotation=None):
    if annotation:
        log.warning("Tag annotation is not supported by Bazaar")
    self.execute(["bzr", "tag", name])

validate(self, dryrun=False)

Ensure the working dir is a repository and there is no modified files

Source code in bumpr/vcs.py
def validate(self, dryrun=False):
    if not isdir(".bzr"):
        raise BumprError("Current directory is not a bazaar repopsitory")

    for line in execute("bzr status --short", verbose=False).splitlines():
        if not line.startswith("?"):
            if dryrun:
                log.warning(MSG)
                break
            else:
                raise BumprError(MSG)

Fake (BaseVCS)

Source code in bumpr/vcs.py
class Fake(BaseVCS):
    def validate(self, dryrun=False):
        return True

validate(self, dryrun=False)

Ensure the working dir is a repository and there is no modified files

Source code in bumpr/vcs.py
def validate(self, dryrun=False):
    return True

Git (BaseVCS)

Source code in bumpr/vcs.py
class Git(BaseVCS):
    def validate(self, dryrun=False):
        if not isdir(".git"):
            raise BumprError("Current directory is not a git repopsitory")

        for line in execute("git status --porcelain", verbose=False).splitlines():
            if not line.startswith("??"):
                if dryrun:
                    log.warning(MSG)
                    break
                else:
                    raise BumprError(MSG)

    def commit(self, message):
        self.execute(["git", "commit", "-am", message])

    def tag(self, name, annotation=None):
        cmd = ["git", "tag", name]
        if annotation:
            cmd += ["--annotate", "-m", '"{0}"'.format(annotation)]
        self.execute(cmd)

    def push(self):
        self.execute(["git", "push"])
        self.execute(["git", "push", "--tags"])

commit(self, message)

Commit all modified files

Source code in bumpr/vcs.py
def commit(self, message):
    self.execute(["git", "commit", "-am", message])

push(self)

Push changes to remote repository

Source code in bumpr/vcs.py
def push(self):
    self.execute(["git", "push"])
    self.execute(["git", "push", "--tags"])

tag(self, name, annotation=None)

Create a tag

Source code in bumpr/vcs.py
def tag(self, name, annotation=None):
    cmd = ["git", "tag", name]
    if annotation:
        cmd += ["--annotate", "-m", '"{0}"'.format(annotation)]
    self.execute(cmd)

validate(self, dryrun=False)

Ensure the working dir is a repository and there is no modified files

Source code in bumpr/vcs.py
def validate(self, dryrun=False):
    if not isdir(".git"):
        raise BumprError("Current directory is not a git repopsitory")

    for line in execute("git status --porcelain", verbose=False).splitlines():
        if not line.startswith("??"):
            if dryrun:
                log.warning(MSG)
                break
            else:
                raise BumprError(MSG)

Mercurial (BaseVCS)

Source code in bumpr/vcs.py
class Mercurial(BaseVCS):
    def validate(self, dryrun=False):
        if not isdir(".hg"):
            raise BumprError("Current directory is not a mercurial repopsitory")

        for line in execute("hg status -mard", verbose=False).splitlines():
            if not line.startswith("??"):
                if dryrun:
                    log.warning(MSG)
                    break
                else:
                    raise BumprError(MSG)

    def commit(self, message):
        self.execute(["hg", "commit", "-A", "-m", message])

    def tag(self, name, annotation=None):
        cmd = ["hg", "tag", name]
        if annotation:
            cmd += ["-m", '"{0}"'.format(annotation)]
        self.execute(cmd)

    def push(self):
        self.execute(["hg", "push"])

commit(self, message)

Commit all modified files

Source code in bumpr/vcs.py
def commit(self, message):
    self.execute(["hg", "commit", "-A", "-m", message])

push(self)

Push changes to remote repository

Source code in bumpr/vcs.py
def push(self):
    self.execute(["hg", "push"])

tag(self, name, annotation=None)

Create a tag

Source code in bumpr/vcs.py
def tag(self, name, annotation=None):
    cmd = ["hg", "tag", name]
    if annotation:
        cmd += ["-m", '"{0}"'.format(annotation)]
    self.execute(cmd)

validate(self, dryrun=False)

Ensure the working dir is a repository and there is no modified files

Source code in bumpr/vcs.py
def validate(self, dryrun=False):
    if not isdir(".hg"):
        raise BumprError("Current directory is not a mercurial repopsitory")

    for line in execute("hg status -mard", verbose=False).splitlines():
        if not line.startswith("??"):
            if dryrun:
                log.warning(MSG)
                break
            else:
                raise BumprError(MSG)
Back to top