mirror of
https://github.com/TECHNOFAB11/bumpver.git
synced 2025-12-12 14:30:09 +01:00
cli -> __main__ refactor
This commit is contained in:
parent
669e8944e9
commit
4caece2817
3 changed files with 450 additions and 401 deletions
|
|
@ -9,8 +9,384 @@ __main__ module for PyCalVer.
|
|||
|
||||
Enables use as module: $ python -m pycalver --version
|
||||
"""
|
||||
import sys
|
||||
import typing as typ
|
||||
import logging
|
||||
import subprocess as sp
|
||||
|
||||
import click
|
||||
|
||||
import pycalver.cli as v1cli
|
||||
import pycalver2.cli as v2cli
|
||||
import pycalver.version as v1version
|
||||
import pycalver2.version as v2version
|
||||
from pycalver import vcs
|
||||
from pycalver import config
|
||||
|
||||
_VERBOSE = 0
|
||||
|
||||
|
||||
try:
|
||||
import pretty_traceback
|
||||
|
||||
pretty_traceback.install()
|
||||
except ImportError:
|
||||
pass # no need to fail because of missing dev dependency
|
||||
|
||||
|
||||
click.disable_unicode_literals_warning = True
|
||||
|
||||
logger = logging.getLogger("pycalver.__main__")
|
||||
|
||||
|
||||
def _configure_logging(verbose: int = 0) -> None:
|
||||
if verbose >= 2:
|
||||
log_format = "%(asctime)s.%(msecs)03d %(levelname)-7s %(name)-17s - %(message)s"
|
||||
log_level = logging.DEBUG
|
||||
elif verbose == 1:
|
||||
log_format = "%(levelname)-7s - %(message)s"
|
||||
log_level = logging.INFO
|
||||
else:
|
||||
log_format = "%(levelname)-7s - %(message)s"
|
||||
log_level = logging.INFO
|
||||
|
||||
logging.basicConfig(level=log_level, format=log_format, datefmt="%Y-%m-%dT%H:%M:%S")
|
||||
logger.debug("Logging configured.")
|
||||
|
||||
|
||||
VALID_RELEASE_VALUES = ("alpha", "beta", "dev", "rc", "post", "final")
|
||||
|
||||
|
||||
def _validate_release_tag(release: str) -> None:
|
||||
if release in VALID_RELEASE_VALUES:
|
||||
return
|
||||
|
||||
logger.error(f"Invalid argument --release={release}")
|
||||
logger.error(f"Valid arguments are: {', '.join(VALID_RELEASE_VALUES)}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@click.group()
|
||||
@click.version_option(version="v202007.0036")
|
||||
@click.help_option()
|
||||
@click.option('-v', '--verbose', count=True, help="Control log level. -vv for debug level.")
|
||||
def cli(verbose: int = 0) -> None:
|
||||
"""Automatically update PyCalVer version strings on python projects."""
|
||||
global _VERBOSE
|
||||
_VERBOSE = verbose
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.argument("old_version")
|
||||
@click.argument("pattern", default="{pycalver}")
|
||||
@click.option('-v', '--verbose', count=True, help="Control log level. -vv for debug level.")
|
||||
@click.option(
|
||||
"--release", default=None, metavar="<name>", help="Override release name of current_version"
|
||||
)
|
||||
@click.option("--major", is_flag=True, default=False, help="Increment major component.")
|
||||
@click.option("--minor", is_flag=True, default=False, help="Increment minor component.")
|
||||
@click.option("--patch", is_flag=True, default=False, help="Increment patch component.")
|
||||
def test(
|
||||
old_version: str,
|
||||
pattern : str = "{pycalver}",
|
||||
verbose : int = 0,
|
||||
release : str = None,
|
||||
major : bool = False,
|
||||
minor : bool = False,
|
||||
patch : bool = False,
|
||||
) -> None:
|
||||
"""Increment a version number for demo purposes."""
|
||||
_configure_logging(verbose=max(_VERBOSE, verbose))
|
||||
|
||||
if release:
|
||||
_validate_release_tag(release)
|
||||
|
||||
new_version = _incr(
|
||||
old_version,
|
||||
pattern=pattern,
|
||||
release=release,
|
||||
major=major,
|
||||
minor=minor,
|
||||
patch=patch,
|
||||
)
|
||||
if new_version is None:
|
||||
logger.error(f"Invalid version '{old_version}' and/or pattern '{pattern}'.")
|
||||
sys.exit(1)
|
||||
|
||||
# TODO (mb 2020-09-05): version switch
|
||||
pep440_version = v1version.to_pep440(new_version)
|
||||
# pep440_version = v2version.to_pep440(new_version)
|
||||
|
||||
click.echo(f"New Version: {new_version}")
|
||||
click.echo(f"PEP440 : {pep440_version}")
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option('-v', '--verbose', count=True, help="Control log level. -vv for debug level.")
|
||||
@click.option(
|
||||
"-f/-n", "--fetch/--no-fetch", is_flag=True, default=True, help="Sync tags from remote origin."
|
||||
)
|
||||
def show(verbose: int = 0, fetch: bool = True) -> None:
|
||||
"""Show current version of your project."""
|
||||
_configure_logging(verbose=max(_VERBOSE, verbose))
|
||||
|
||||
ctx: config.ProjectContext = config.init_project_ctx(project_path=".")
|
||||
cfg: config.MaybeConfig = config.parse(ctx)
|
||||
|
||||
if cfg is None:
|
||||
logger.error("Could not parse configuration. Perhaps try 'pycalver init'.")
|
||||
sys.exit(1)
|
||||
|
||||
cfg = _update_cfg_from_vcs(cfg, fetch)
|
||||
click.echo(f"Current Version: {cfg.current_version}")
|
||||
click.echo(f"PEP440 : {cfg.pep440_version}")
|
||||
|
||||
|
||||
def _try_print_diff(cfg: config.Config, new_version: str) -> None:
|
||||
try:
|
||||
# TODO (mb 2020-09-05): version switch
|
||||
diff = v1cli.get_diff(cfg, new_version)
|
||||
# diff = v2cli.get_diff(cfg, new_version)
|
||||
|
||||
if sys.stdout.isatty():
|
||||
for line in diff.splitlines():
|
||||
if line.startswith("+++") or line.startswith("---"):
|
||||
click.echo(line)
|
||||
elif line.startswith("+"):
|
||||
click.echo("\u001b[32m" + line + "\u001b[0m")
|
||||
elif line.startswith("-"):
|
||||
click.echo("\u001b[31m" + line + "\u001b[0m")
|
||||
elif line.startswith("@"):
|
||||
click.echo("\u001b[36m" + line + "\u001b[0m")
|
||||
else:
|
||||
click.echo(line)
|
||||
else:
|
||||
click.echo(diff)
|
||||
except Exception as ex:
|
||||
logger.error(str(ex), exc_info=True)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def _incr(
|
||||
old_version: str,
|
||||
pattern : str = "{pycalver}",
|
||||
*,
|
||||
release: str = None,
|
||||
major : bool = False,
|
||||
minor : bool = False,
|
||||
patch : bool = False,
|
||||
) -> typ.Optional[str]:
|
||||
is_v1_pattern = "{" in pattern
|
||||
if is_v1_pattern:
|
||||
return v1version.incr(
|
||||
old_version,
|
||||
pattern=pattern,
|
||||
release=release,
|
||||
major=major,
|
||||
minor=minor,
|
||||
patch=patch,
|
||||
)
|
||||
else:
|
||||
return v2version.incr(
|
||||
old_version,
|
||||
pattern=pattern,
|
||||
release=release,
|
||||
major=major,
|
||||
minor=minor,
|
||||
patch=patch,
|
||||
)
|
||||
|
||||
|
||||
def _bump(
|
||||
cfg : config.Config,
|
||||
new_version : str,
|
||||
commit_message: str,
|
||||
allow_dirty : bool = False,
|
||||
) -> None:
|
||||
vcs_api: typ.Optional[vcs.VCSAPI] = None
|
||||
|
||||
if cfg.commit:
|
||||
try:
|
||||
vcs_api = vcs.get_vcs_api()
|
||||
except OSError:
|
||||
logger.warning("Version Control System not found, aborting commit.")
|
||||
|
||||
filepaths = set(cfg.file_patterns.keys())
|
||||
|
||||
if vcs_api:
|
||||
vcs.assert_not_dirty(vcs_api, filepaths, allow_dirty)
|
||||
|
||||
try:
|
||||
# TODO (mb 2020-09-05): version switch
|
||||
v1cli.rewrite(cfg, new_version)
|
||||
# v2cli.rewrite(cfg, new_version)
|
||||
except Exception as ex:
|
||||
logger.error(str(ex))
|
||||
sys.exit(1)
|
||||
|
||||
if vcs_api:
|
||||
vcs.commit(cfg, vcs_api, filepaths, new_version, commit_message)
|
||||
|
||||
|
||||
def _try_bump(
|
||||
cfg : config.Config,
|
||||
new_version : str,
|
||||
commit_message: str,
|
||||
allow_dirty : bool = False,
|
||||
) -> None:
|
||||
try:
|
||||
_bump(cfg, new_version, commit_message, allow_dirty)
|
||||
except sp.CalledProcessError as ex:
|
||||
logger.error(f"Error running subcommand: {ex.cmd}")
|
||||
if ex.stdout:
|
||||
sys.stdout.write(ex.stdout.decode('utf-8'))
|
||||
if ex.stderr:
|
||||
sys.stderr.write(ex.stderr.decode('utf-8'))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option('-v', '--verbose', count=True, help="Control log level. -vv for debug level.")
|
||||
@click.option(
|
||||
"--dry", default=False, is_flag=True, help="Display diff of changes, don't rewrite files."
|
||||
)
|
||||
def init(verbose: int = 0, dry: bool = False) -> None:
|
||||
"""Initialize [pycalver] configuration."""
|
||||
_configure_logging(verbose=max(_VERBOSE, verbose))
|
||||
|
||||
ctx: config.ProjectContext = config.init_project_ctx(project_path=".")
|
||||
cfg: config.MaybeConfig = config.parse(ctx)
|
||||
|
||||
if cfg:
|
||||
logger.error(f"Configuration already initialized in {ctx.config_filepath}")
|
||||
sys.exit(1)
|
||||
|
||||
if dry:
|
||||
click.echo(f"Exiting because of '--dry'. Would have written to {ctx.config_filepath}:")
|
||||
cfg_text: str = config.default_config(ctx)
|
||||
click.echo("\n " + "\n ".join(cfg_text.splitlines()))
|
||||
sys.exit(0)
|
||||
|
||||
config.write_content(ctx)
|
||||
|
||||
|
||||
def _update_cfg_from_vcs(cfg: config.Config, fetch: bool) -> config.Config:
|
||||
all_tags = vcs.get_tags(fetch=fetch)
|
||||
|
||||
# TODO (mb 2020-09-05): version switch
|
||||
cfg = v1cli.update_cfg_from_vcs(cfg, all_tags)
|
||||
# cfg = v2cli.update_cfg_from_vcs(cfg, all_tags)
|
||||
return cfg
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option(
|
||||
"-v",
|
||||
"--verbose",
|
||||
count=True,
|
||||
help="Control log level. -vv for debug level.",
|
||||
)
|
||||
@click.option(
|
||||
"-f/-n",
|
||||
"--fetch/--no-fetch",
|
||||
is_flag=True,
|
||||
default=True,
|
||||
help="Sync tags from remote origin.",
|
||||
)
|
||||
@click.option(
|
||||
"--dry",
|
||||
default=False,
|
||||
is_flag=True,
|
||||
help="Display diff of changes, don't rewrite files.",
|
||||
)
|
||||
@click.option(
|
||||
"--release",
|
||||
default=None,
|
||||
metavar="<name>",
|
||||
help=(
|
||||
f"Override release name of current_version. Valid options are: "
|
||||
f"{', '.join(VALID_RELEASE_VALUES)}."
|
||||
),
|
||||
)
|
||||
@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."
|
||||
),
|
||||
)
|
||||
@click.option("--major", is_flag=True, default=False, help="Increment major component.")
|
||||
@click.option("--minor", is_flag=True, default=False, help="Increment minor component.")
|
||||
@click.option("--patch", is_flag=True, default=False, help="Increment patch component.")
|
||||
def bump(
|
||||
release : typ.Optional[str] = None,
|
||||
verbose : int = 0,
|
||||
dry : bool = False,
|
||||
allow_dirty: bool = False,
|
||||
fetch : bool = True,
|
||||
major : bool = False,
|
||||
minor : bool = False,
|
||||
patch : bool = False,
|
||||
) -> None:
|
||||
"""Increment the current version string and update project files."""
|
||||
verbose = max(_VERBOSE, verbose)
|
||||
_configure_logging(verbose)
|
||||
|
||||
if release:
|
||||
_validate_release_tag(release)
|
||||
|
||||
ctx: config.ProjectContext = config.init_project_ctx(project_path=".")
|
||||
cfg: config.MaybeConfig = config.parse(ctx)
|
||||
|
||||
if cfg is None:
|
||||
logger.error("Could not parse configuration. Perhaps try 'pycalver init'.")
|
||||
sys.exit(1)
|
||||
|
||||
cfg = _update_cfg_from_vcs(cfg, fetch)
|
||||
|
||||
old_version = cfg.current_version
|
||||
new_version = _incr(
|
||||
old_version,
|
||||
pattern=cfg.version_pattern,
|
||||
release=release,
|
||||
major=major,
|
||||
minor=minor,
|
||||
patch=patch,
|
||||
)
|
||||
|
||||
if new_version is None:
|
||||
is_semver = "{semver}" in cfg.version_pattern
|
||||
has_semver_inc = major or minor or patch
|
||||
if is_semver and not has_semver_inc:
|
||||
logger.warning("bump --major/--minor/--patch required when using semver.")
|
||||
else:
|
||||
logger.error(f"Invalid version '{old_version}' and/or pattern '{cfg.version_pattern}'.")
|
||||
sys.exit(1)
|
||||
|
||||
logger.info(f"Old Version: {old_version}")
|
||||
logger.info(f"New Version: {new_version}")
|
||||
|
||||
if dry or verbose >= 2:
|
||||
_try_print_diff(cfg, new_version)
|
||||
|
||||
if dry:
|
||||
return
|
||||
|
||||
# # TODO (mb 2020-09-05): format from config
|
||||
# commit_message_kwargs = {
|
||||
# new_version
|
||||
# old_version
|
||||
# pep440_new_version
|
||||
# pep440_old_version
|
||||
# }
|
||||
# cfg.commit_message =
|
||||
commit_message = f"bump version to {new_version}"
|
||||
|
||||
_try_bump(cfg, new_version, commit_message, allow_dirty)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from . import cli
|
||||
|
||||
cli.cli()
|
||||
cli()
|
||||
|
|
|
|||
|
|
@ -9,372 +9,45 @@ CLI module for PyCalVer.
|
|||
|
||||
Provided subcommands: show, test, init, bump
|
||||
"""
|
||||
import sys
|
||||
import typing as typ
|
||||
import logging
|
||||
import subprocess as sp
|
||||
|
||||
import click
|
||||
|
||||
from . import vcs
|
||||
from . import config
|
||||
from . import rewrite
|
||||
from . import version
|
||||
|
||||
_VERBOSE = 0
|
||||
|
||||
|
||||
try:
|
||||
import pretty_traceback
|
||||
|
||||
pretty_traceback.install()
|
||||
except ImportError:
|
||||
pass # no need to fail because of missing dev dependency
|
||||
|
||||
|
||||
click.disable_unicode_literals_warning = True
|
||||
|
||||
|
||||
VALID_RELEASE_VALUES = ("alpha", "beta", "dev", "rc", "post", "final")
|
||||
|
||||
import pycalver.rewrite as v1rewrite
|
||||
import pycalver.version as v1version
|
||||
from pycalver import config
|
||||
|
||||
logger = logging.getLogger("pycalver.cli")
|
||||
|
||||
|
||||
def _configure_logging(verbose: int = 0) -> None:
|
||||
if verbose >= 2:
|
||||
log_format = "%(asctime)s.%(msecs)03d %(levelname)-7s %(name)-17s - %(message)s"
|
||||
log_level = logging.DEBUG
|
||||
elif verbose == 1:
|
||||
log_format = "%(levelname)-7s - %(message)s"
|
||||
log_level = logging.INFO
|
||||
else:
|
||||
log_format = "%(levelname)-7s - %(message)s"
|
||||
log_level = logging.INFO
|
||||
def update_cfg_from_vcs(cfg: config.Config, all_tags: typ.List[str]) -> config.Config:
|
||||
version_tags = [tag for tag in all_tags if v1version.is_valid(tag, cfg.version_pattern)]
|
||||
if not version_tags:
|
||||
logger.debug("no vcs tags found")
|
||||
return cfg
|
||||
|
||||
logging.basicConfig(level=log_level, format=log_format, datefmt="%Y-%m-%dT%H:%M:%S")
|
||||
logger.debug("Logging configured.")
|
||||
version_tags.sort(reverse=True)
|
||||
logger.debug(f"found {len(version_tags)} tags: {version_tags[:2]}")
|
||||
latest_version_tag = version_tags[0]
|
||||
latest_version_pep440 = v1version.to_pep440(latest_version_tag)
|
||||
if latest_version_tag <= cfg.current_version:
|
||||
return cfg
|
||||
|
||||
|
||||
def _validate_release_tag(release: str) -> None:
|
||||
if release in VALID_RELEASE_VALUES:
|
||||
return
|
||||
|
||||
logger.error(f"Invalid argument --release={release}")
|
||||
logger.error(f"Valid arguments are: {', '.join(VALID_RELEASE_VALUES)}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@click.group()
|
||||
@click.version_option(version="v202007.0036")
|
||||
@click.help_option()
|
||||
@click.option('-v', '--verbose', count=True, help="Control log level. -vv for debug level.")
|
||||
def cli(verbose: int = 0) -> None:
|
||||
"""Automatically update PyCalVer version strings on python projects."""
|
||||
global _VERBOSE
|
||||
_VERBOSE = verbose
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.argument("old_version")
|
||||
@click.argument("pattern", default="{pycalver}")
|
||||
@click.option('-v', '--verbose', count=True, help="Control log level. -vv for debug level.")
|
||||
@click.option(
|
||||
"--release", default=None, metavar="<name>", help="Override release name of current_version"
|
||||
)
|
||||
@click.option("--major", is_flag=True, default=False, help="Increment major component.")
|
||||
@click.option("--minor", is_flag=True, default=False, help="Increment minor component.")
|
||||
@click.option("--patch", is_flag=True, default=False, help="Increment patch component.")
|
||||
def test(
|
||||
old_version: str,
|
||||
pattern : str = "{pycalver}",
|
||||
verbose : int = 0,
|
||||
release : str = None,
|
||||
major : bool = False,
|
||||
minor : bool = False,
|
||||
patch : bool = False,
|
||||
) -> None:
|
||||
"""Increment a version number for demo purposes."""
|
||||
_configure_logging(verbose=max(_VERBOSE, verbose))
|
||||
|
||||
if release:
|
||||
_validate_release_tag(release)
|
||||
|
||||
new_version = version.incr(
|
||||
old_version, pattern=pattern, release=release, major=major, minor=minor, patch=patch
|
||||
logger.info(f"Working dir version : {cfg.current_version}")
|
||||
logger.info(f"Latest version from VCS tag: {latest_version_tag}")
|
||||
return cfg._replace(
|
||||
current_version=latest_version_tag,
|
||||
pep440_version=latest_version_pep440,
|
||||
)
|
||||
if new_version is None:
|
||||
logger.error(f"Invalid version '{old_version}' and/or pattern '{pattern}'.")
|
||||
sys.exit(1)
|
||||
|
||||
pep440_version = version.to_pep440(new_version)
|
||||
|
||||
click.echo(f"New Version: {new_version}")
|
||||
click.echo(f"PEP440 : {pep440_version}")
|
||||
|
||||
|
||||
def _update_cfg_from_vcs(cfg: config.Config, fetch: bool) -> config.Config:
|
||||
try:
|
||||
vcs_api = vcs.get_vcs_api()
|
||||
logger.debug(f"vcs found: {vcs_api.name}")
|
||||
if fetch:
|
||||
logger.info("fetching tags from remote (to turn off use: -n / --no-fetch)")
|
||||
vcs_api.fetch()
|
||||
|
||||
version_tags = [
|
||||
tag for tag in vcs_api.ls_tags() if version.is_valid(tag, cfg.version_pattern)
|
||||
]
|
||||
if version_tags:
|
||||
version_tags.sort(reverse=True)
|
||||
logger.debug(f"found {len(version_tags)} tags: {version_tags[:2]}")
|
||||
latest_version_tag = version_tags[0]
|
||||
latest_version_pep440 = version.to_pep440(latest_version_tag)
|
||||
if latest_version_tag > cfg.current_version:
|
||||
logger.info(f"Working dir version : {cfg.current_version}")
|
||||
logger.info(f"Latest version from {vcs_api.name:>3} tag: {latest_version_tag}")
|
||||
cfg = cfg._replace(
|
||||
current_version=latest_version_tag, pep440_version=latest_version_pep440
|
||||
)
|
||||
|
||||
else:
|
||||
logger.debug("no vcs tags found")
|
||||
except OSError:
|
||||
logger.debug("No vcs found")
|
||||
|
||||
return cfg
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option('-v', '--verbose', count=True, help="Control log level. -vv for debug level.")
|
||||
@click.option(
|
||||
"-f/-n", "--fetch/--no-fetch", is_flag=True, default=True, help="Sync tags from remote origin."
|
||||
)
|
||||
def show(verbose: int = 0, fetch: bool = True) -> None:
|
||||
"""Show current version."""
|
||||
_configure_logging(verbose=max(_VERBOSE, verbose))
|
||||
|
||||
ctx: config.ProjectContext = config.init_project_ctx(project_path=".")
|
||||
cfg: config.MaybeConfig = config.parse(ctx)
|
||||
|
||||
if cfg is None:
|
||||
logger.error("Could not parse configuration. Perhaps try 'pycalver init'.")
|
||||
sys.exit(1)
|
||||
|
||||
cfg = _update_cfg_from_vcs(cfg, fetch=fetch)
|
||||
|
||||
click.echo(f"Current Version: {cfg.current_version}")
|
||||
click.echo(f"PEP440 : {cfg.pep440_version}")
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option('-v', '--verbose', count=True, help="Control log level. -vv for debug level.")
|
||||
@click.option(
|
||||
"--dry", default=False, is_flag=True, help="Display diff of changes, don't rewrite files."
|
||||
)
|
||||
def init(verbose: int = 0, dry: bool = False) -> None:
|
||||
"""Initialize [pycalver] configuration."""
|
||||
_configure_logging(verbose=max(_VERBOSE, verbose))
|
||||
|
||||
ctx: config.ProjectContext = config.init_project_ctx(project_path=".")
|
||||
cfg: config.MaybeConfig = config.parse(ctx)
|
||||
|
||||
if cfg:
|
||||
logger.error(f"Configuration already initialized in {ctx.config_filepath}")
|
||||
sys.exit(1)
|
||||
|
||||
if dry:
|
||||
click.echo(f"Exiting because of '--dry'. Would have written to {ctx.config_filepath}:")
|
||||
cfg_text: str = config.default_config(ctx)
|
||||
click.echo("\n " + "\n ".join(cfg_text.splitlines()))
|
||||
sys.exit(0)
|
||||
|
||||
config.write_content(ctx)
|
||||
|
||||
|
||||
def _assert_not_dirty(vcs_api: vcs.VCSAPI, filepaths: typ.Set[str], allow_dirty: bool) -> None:
|
||||
dirty_files = vcs_api.status(required_files=filepaths)
|
||||
|
||||
if dirty_files:
|
||||
logger.warning(f"{vcs_api.name} working directory is not clean. Uncomitted file(s):")
|
||||
for dirty_file in dirty_files:
|
||||
logger.warning(" " + dirty_file)
|
||||
|
||||
if not allow_dirty and dirty_files:
|
||||
sys.exit(1)
|
||||
|
||||
dirty_pattern_files = set(dirty_files) & filepaths
|
||||
if dirty_pattern_files:
|
||||
logger.error("Not commiting when pattern files are dirty:")
|
||||
for dirty_file in dirty_pattern_files:
|
||||
logger.warning(" " + dirty_file)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def _commit(
|
||||
cfg: config.Config, new_version: str, vcs_api: vcs.VCSAPI, filepaths: typ.Set[str]
|
||||
def rewrite(
|
||||
cfg : config.Config,
|
||||
new_version: str,
|
||||
) -> None:
|
||||
for filepath in filepaths:
|
||||
vcs_api.add(filepath)
|
||||
|
||||
vcs_api.commit(f"bump version to {new_version}")
|
||||
|
||||
if cfg.commit and cfg.tag:
|
||||
vcs_api.tag(new_version)
|
||||
|
||||
if cfg.commit and cfg.tag and cfg.push:
|
||||
vcs_api.push(new_version)
|
||||
new_vinfo = v1version.parse_version_info(new_version, cfg.version_pattern)
|
||||
v1rewrite.rewrite(cfg.file_patterns, new_vinfo)
|
||||
|
||||
|
||||
def _bump(cfg: config.Config, new_version: str, allow_dirty: bool = False) -> None:
|
||||
vcs_api: typ.Optional[vcs.VCSAPI] = None
|
||||
|
||||
if cfg.commit:
|
||||
try:
|
||||
vcs_api = vcs.get_vcs_api()
|
||||
except OSError:
|
||||
logger.warning("Version Control System not found, aborting commit.")
|
||||
|
||||
filepaths = set(cfg.file_patterns.keys())
|
||||
|
||||
if vcs_api:
|
||||
_assert_not_dirty(vcs_api, filepaths, allow_dirty)
|
||||
|
||||
try:
|
||||
new_vinfo = version.parse_version_info(new_version, cfg.version_pattern)
|
||||
rewrite.rewrite(cfg.file_patterns, new_vinfo)
|
||||
except Exception as ex:
|
||||
logger.error(str(ex))
|
||||
sys.exit(1)
|
||||
|
||||
if vcs_api:
|
||||
_commit(cfg, new_version, vcs_api, filepaths)
|
||||
|
||||
|
||||
def _try_bump(cfg: config.Config, new_version: str, allow_dirty: bool = False) -> None:
|
||||
try:
|
||||
_bump(cfg, new_version, allow_dirty)
|
||||
except sp.CalledProcessError as ex:
|
||||
logger.error(f"Error running subcommand: {ex.cmd}")
|
||||
if ex.stdout:
|
||||
sys.stdout.write(ex.stdout.decode('utf-8'))
|
||||
if ex.stderr:
|
||||
sys.stderr.write(ex.stderr.decode('utf-8'))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def _print_diff(cfg: config.Config, new_version: str) -> None:
|
||||
new_vinfo = version.parse_version_info(new_version, cfg.version_pattern)
|
||||
diff: str = rewrite.diff(new_vinfo, cfg.file_patterns)
|
||||
|
||||
if sys.stdout.isatty():
|
||||
for line in diff.splitlines():
|
||||
if line.startswith("+++") or line.startswith("---"):
|
||||
click.echo(line)
|
||||
elif line.startswith("+"):
|
||||
click.echo("\u001b[32m" + line + "\u001b[0m")
|
||||
elif line.startswith("-"):
|
||||
click.echo("\u001b[31m" + line + "\u001b[0m")
|
||||
elif line.startswith("@"):
|
||||
click.echo("\u001b[36m" + line + "\u001b[0m")
|
||||
else:
|
||||
click.echo(line)
|
||||
else:
|
||||
click.echo(diff)
|
||||
|
||||
|
||||
def _try_print_diff(cfg: config.Config, new_version: str) -> None:
|
||||
try:
|
||||
_print_diff(cfg, new_version)
|
||||
except Exception as ex:
|
||||
logger.error(str(ex))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option("-v", "--verbose", count=True, help="Control log level. -vv for debug level.")
|
||||
@click.option(
|
||||
"-f/-n", "--fetch/--no-fetch", is_flag=True, default=True, help="Sync tags from remote origin."
|
||||
)
|
||||
@click.option(
|
||||
"--dry", default=False, is_flag=True, help="Display diff of changes, don't rewrite files."
|
||||
)
|
||||
@click.option(
|
||||
"--release",
|
||||
default=None,
|
||||
metavar="<name>",
|
||||
help=(
|
||||
f"Override release name of current_version. Valid options are: "
|
||||
f"{', '.join(VALID_RELEASE_VALUES)}."
|
||||
),
|
||||
)
|
||||
@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."
|
||||
),
|
||||
)
|
||||
@click.option("--major", is_flag=True, default=False, help="Increment major component.")
|
||||
@click.option("--minor", is_flag=True, default=False, help="Increment minor component.")
|
||||
@click.option("--patch", is_flag=True, default=False, help="Increment patch component.")
|
||||
def bump(
|
||||
release : typ.Optional[str] = None,
|
||||
verbose : int = 0,
|
||||
dry : bool = False,
|
||||
allow_dirty: bool = False,
|
||||
fetch : bool = True,
|
||||
major : bool = False,
|
||||
minor : bool = False,
|
||||
patch : bool = False,
|
||||
) -> None:
|
||||
"""Increment the current version string and update project files."""
|
||||
verbose = max(_VERBOSE, verbose)
|
||||
_configure_logging(verbose)
|
||||
|
||||
if release:
|
||||
_validate_release_tag(release)
|
||||
|
||||
ctx: config.ProjectContext = config.init_project_ctx(project_path=".")
|
||||
cfg: config.MaybeConfig = config.parse(ctx)
|
||||
|
||||
if cfg is None:
|
||||
logger.error("Could not parse configuration. Perhaps try 'pycalver init'.")
|
||||
sys.exit(1)
|
||||
|
||||
cfg = _update_cfg_from_vcs(cfg, fetch=fetch)
|
||||
|
||||
old_version = cfg.current_version
|
||||
new_version = version.incr(
|
||||
old_version,
|
||||
pattern=cfg.version_pattern,
|
||||
release=release,
|
||||
major=major,
|
||||
minor=minor,
|
||||
patch=patch,
|
||||
)
|
||||
if new_version is None:
|
||||
is_semver = "{semver}" in cfg.version_pattern
|
||||
has_semver_inc = major or minor or patch
|
||||
if is_semver and not has_semver_inc:
|
||||
logger.warning("bump --major/--minor/--patch required when using semver.")
|
||||
else:
|
||||
logger.error(f"Invalid version '{old_version}' and/or pattern '{cfg.version_pattern}'.")
|
||||
sys.exit(1)
|
||||
|
||||
logger.info(f"Old Version: {old_version}")
|
||||
logger.info(f"New Version: {new_version}")
|
||||
|
||||
if dry or verbose >= 2:
|
||||
_try_print_diff(cfg, new_version)
|
||||
|
||||
if dry:
|
||||
return
|
||||
|
||||
_try_bump(cfg, new_version, allow_dirty)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cli()
|
||||
def get_diff(cfg: config.Config, new_version: str) -> str:
|
||||
new_vinfo = v1version.parse_version_info(new_version, cfg.version_pattern)
|
||||
return v1rewrite.diff(new_vinfo, cfg.file_patterns)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue