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.
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
in
vars' (if provided), section', and in
DEFAULTSECT' 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 specified
section' 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 the
vars' 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
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.
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
Hook
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
ReplaceHook (Hook)
This hook perform replacements in files
log
ANSIFormatter (Formatter)
Convert a `logging.LogRecord' object into colored text, using ANSI escape sequences.
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.
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
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
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)
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)
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)
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)
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)