2018-09-02 21:48:12 +02:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
# This file is part of the pycalver project
|
2018-11-04 21:34:53 +01:00
|
|
|
# https://gitlab.com/mbarkhau/pycalver
|
2018-09-02 21:48:12 +02:00
|
|
|
#
|
2018-12-09 16:50:09 +01:00
|
|
|
# Copyright (c) 2018 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
|
2018-09-02 21:48:12 +02:00
|
|
|
# SPDX-License-Identifier: MIT
|
2018-11-06 21:45:33 +01:00
|
|
|
"""
|
2018-12-05 09:38:27 +01:00
|
|
|
CLI module for PyCalVer.
|
2018-11-06 21:45:33 +01:00
|
|
|
|
2018-12-05 09:38:27 +01:00
|
|
|
Provided subcommands: show, incr, init, bump
|
2018-11-06 21:45:33 +01:00
|
|
|
"""
|
2018-09-02 21:48:12 +02:00
|
|
|
|
|
|
|
|
import sys
|
|
|
|
|
import click
|
|
|
|
|
import logging
|
2018-11-11 15:09:12 +01:00
|
|
|
import typing as typ
|
2018-09-02 21:48:12 +02:00
|
|
|
|
|
|
|
|
from . import vcs
|
|
|
|
|
from . import parse
|
|
|
|
|
from . import config
|
|
|
|
|
from . import version
|
|
|
|
|
from . import rewrite
|
|
|
|
|
|
|
|
|
|
|
2018-11-11 15:09:12 +01:00
|
|
|
_VERBOSE = 0
|
2018-09-02 21:48:12 +02:00
|
|
|
|
|
|
|
|
|
2018-12-05 09:38:27 +01:00
|
|
|
try:
|
|
|
|
|
import backtrace
|
|
|
|
|
|
|
|
|
|
# To enable pretty tracebacks:
|
|
|
|
|
# echo "export ENABLE_BACKTRACE=1;" >> ~/.bashrc
|
|
|
|
|
backtrace.hook(align=True, strip_path=True, enable_on_envvar_only=True)
|
|
|
|
|
except ImportError:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
2018-12-09 17:03:51 +01:00
|
|
|
click.disable_unicode_literals_warning = True
|
|
|
|
|
|
|
|
|
|
|
2018-11-11 15:09:12 +01:00
|
|
|
log = logging.getLogger("pycalver.cli")
|
|
|
|
|
|
2018-09-02 21:48:12 +02:00
|
|
|
|
2018-11-15 22:16:16 +01:00
|
|
|
def _init_logging(verbose: int = 0) -> None:
|
2018-11-11 15:09:12 +01:00
|
|
|
if verbose >= 2:
|
|
|
|
|
log_format = "%(asctime)s.%(msecs)03d %(levelname)-7s %(name)-15s - %(message)s"
|
|
|
|
|
log_level = logging.DEBUG
|
|
|
|
|
elif verbose == 1:
|
|
|
|
|
log_format = "%(levelname)-7s - %(message)s"
|
2018-12-20 15:26:48 +01:00
|
|
|
log_level = logging.DEBUG
|
2018-11-11 15:09:12 +01:00
|
|
|
else:
|
2018-12-20 15:26:48 +01:00
|
|
|
log_format = "%(levelname)-7s - %(message)s"
|
|
|
|
|
log_level = logging.INFO
|
2018-09-02 21:48:12 +02:00
|
|
|
|
2018-11-11 15:09:12 +01:00
|
|
|
logging.basicConfig(level=log_level, format=log_format, datefmt="%Y-%m-%dT%H:%M:%S")
|
2018-11-15 22:16:16 +01:00
|
|
|
log.debug("Logging initialized.")
|
2018-09-02 21:48:12 +02:00
|
|
|
|
|
|
|
|
|
2018-12-21 19:19:48 +01:00
|
|
|
def _validate_release_tag(release: str) -> None:
|
|
|
|
|
if release == 'final' or release in parse.VALID_RELEASE_VALUES:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
log.error(f"Invalid argument --release={release}")
|
|
|
|
|
log.error(f"Valid arguments are: final, {', '.join(parse.VALID_RELEASE_VALUES)}")
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
2018-09-02 21:48:12 +02:00
|
|
|
@click.group()
|
2018-12-20 15:28:11 +01:00
|
|
|
@click.version_option(version="v201812.0010-beta")
|
2018-12-09 18:06:28 +01:00
|
|
|
@click.help_option()
|
2018-11-11 15:09:12 +01:00
|
|
|
@click.option('-v', '--verbose', count=True, help="Control log level. -vv for debug level.")
|
|
|
|
|
def cli(verbose: int = 0):
|
|
|
|
|
"""Automatically update PyCalVer version strings on python projects."""
|
|
|
|
|
global _VERBOSE
|
|
|
|
|
_VERBOSE = verbose
|
|
|
|
|
|
|
|
|
|
|
2018-12-05 09:38:27 +01:00
|
|
|
@cli.command()
|
|
|
|
|
@click.argument("old_version")
|
|
|
|
|
@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"
|
|
|
|
|
)
|
|
|
|
|
def incr(old_version: str, verbose: int = 0, release: str = None) -> None:
|
|
|
|
|
"""Increment a version number for demo purposes."""
|
|
|
|
|
_init_logging(verbose=max(_VERBOSE, verbose))
|
|
|
|
|
|
2018-12-21 19:19:48 +01:00
|
|
|
if release:
|
|
|
|
|
_validate_release_tag(release)
|
2018-12-05 09:38:27 +01:00
|
|
|
|
|
|
|
|
new_version = version.incr(old_version, release=release)
|
|
|
|
|
pep440_version = version.pycalver_to_pep440(new_version)
|
|
|
|
|
|
|
|
|
|
print("PyCalVer Version:", new_version)
|
2018-12-21 19:19:48 +01:00
|
|
|
print("PEP440 Version :", pep440_version)
|
2018-12-05 09:38:27 +01:00
|
|
|
|
|
|
|
|
|
2018-11-11 15:09:12 +01:00
|
|
|
def _update_cfg_from_vcs(cfg: config.Config, fetch: bool) -> config.Config:
|
|
|
|
|
try:
|
|
|
|
|
_vcs = vcs.get_vcs()
|
|
|
|
|
log.debug(f"vcs found: {_vcs.name}")
|
|
|
|
|
if fetch:
|
2018-12-20 15:26:48 +01:00
|
|
|
log.info(f"fetching tags from remote (to turn off use: -n / --no-fetch)")
|
2018-11-11 15:09:12 +01:00
|
|
|
_vcs.fetch()
|
|
|
|
|
|
2018-12-09 14:49:13 +01:00
|
|
|
version_tags = [tag for tag in _vcs.ls_tags() if version.PYCALVER_RE.match(tag)]
|
2018-11-11 15:09:12 +01:00
|
|
|
if version_tags:
|
|
|
|
|
version_tags.sort(reverse=True)
|
|
|
|
|
log.debug(f"found {len(version_tags)} tags: {version_tags[:2]}")
|
|
|
|
|
latest_version_tag = version_tags[0]
|
|
|
|
|
if latest_version_tag > cfg.current_version:
|
|
|
|
|
log.info(f"Working dir version : {cfg.current_version}")
|
|
|
|
|
log.info(f"Latest version from {_vcs.name:>3} tag: {latest_version_tag}")
|
|
|
|
|
cfg = cfg._replace(current_version=latest_version_tag)
|
|
|
|
|
else:
|
|
|
|
|
log.debug("no vcs tags found")
|
|
|
|
|
except OSError:
|
|
|
|
|
log.debug("No vcs found")
|
|
|
|
|
|
|
|
|
|
return cfg
|
2018-09-02 21:48:12 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@cli.command()
|
2018-12-09 14:49:13 +01:00
|
|
|
@click.option('-v', '--verbose', count=True, help="Control log level. -vv for debug level.")
|
|
|
|
|
@click.option(
|
2018-12-21 19:19:48 +01:00
|
|
|
"-f/-n", "--fetch/--no-fetch", is_flag=True, default=True, help="Sync tags from remote origin."
|
2018-12-09 14:49:13 +01:00
|
|
|
)
|
2018-11-11 15:09:12 +01:00
|
|
|
def show(verbose: int = 0, fetch: bool = True) -> None:
|
2018-11-06 21:45:33 +01:00
|
|
|
"""Show current version."""
|
2018-12-05 09:38:27 +01:00
|
|
|
_init_logging(verbose=max(_VERBOSE, verbose))
|
|
|
|
|
|
|
|
|
|
ctx: config.ProjectContext = config.init_project_ctx(project_path=".")
|
|
|
|
|
cfg: config.MaybeConfig = config.parse(ctx)
|
2018-09-02 21:48:12 +02:00
|
|
|
|
|
|
|
|
if cfg is None:
|
2018-12-21 19:19:48 +01:00
|
|
|
log.error("Could not parse configuration. Perhaps try 'pycalver init'.")
|
2018-09-02 23:36:57 +02:00
|
|
|
sys.exit(1)
|
2018-09-02 21:48:12 +02:00
|
|
|
|
2018-11-11 15:09:12 +01:00
|
|
|
cfg = _update_cfg_from_vcs(cfg, fetch=fetch)
|
|
|
|
|
|
2018-09-03 22:23:51 +02:00
|
|
|
print(f"Current Version: {cfg.current_version}")
|
2018-11-11 15:09:12 +01:00
|
|
|
print(f"PEP440 Version : {cfg.pep440_version}")
|
2018-09-02 21:48:12 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@cli.command()
|
2018-11-11 15:09:12 +01:00
|
|
|
@click.option('-v', '--verbose', count=True, help="Control log level. -vv for debug level.")
|
2018-09-02 21:48:12 +02:00
|
|
|
@click.option(
|
2018-11-04 21:11:42 +01:00
|
|
|
"--dry", default=False, is_flag=True, help="Display diff of changes, don't rewrite files."
|
2018-09-02 21:48:12 +02:00
|
|
|
)
|
2018-11-11 15:09:12 +01:00
|
|
|
def init(verbose: int = 0, dry: bool = False) -> None:
|
2018-11-06 21:45:33 +01:00
|
|
|
"""Initialize [pycalver] configuration."""
|
2018-12-05 09:38:27 +01:00
|
|
|
_init_logging(verbose=max(_VERBOSE, verbose))
|
|
|
|
|
|
|
|
|
|
ctx: config.ProjectContext = config.init_project_ctx(project_path=".")
|
|
|
|
|
cfg: config.MaybeConfig = config.parse(ctx)
|
2018-09-02 21:48:12 +02:00
|
|
|
|
|
|
|
|
if cfg:
|
2018-12-08 19:18:47 +01:00
|
|
|
log.error("Configuration already initialized in {ctx.config_filepath}")
|
2018-09-02 23:36:57 +02:00
|
|
|
sys.exit(1)
|
2018-09-02 21:48:12 +02:00
|
|
|
|
|
|
|
|
if dry:
|
|
|
|
|
print("Exiting because of '--dry'. Would have written to setup.cfg:")
|
2018-12-08 19:18:47 +01:00
|
|
|
cfg_lines = config.default_config(ctx)
|
2018-09-02 21:48:12 +02:00
|
|
|
print("\n " + "\n ".join(cfg_lines))
|
|
|
|
|
return
|
|
|
|
|
|
2018-12-08 19:18:47 +01:00
|
|
|
config.write_content(ctx)
|
2018-09-02 21:48:12 +02:00
|
|
|
|
|
|
|
|
|
2018-11-11 15:09:12 +01:00
|
|
|
def _assert_not_dirty(vcs, filepaths: typ.Set[str], allow_dirty: bool):
|
|
|
|
|
dirty_files = vcs.status()
|
|
|
|
|
|
|
|
|
|
if dirty_files:
|
|
|
|
|
log.warn(f"{vcs.name} working directory is not clean:")
|
|
|
|
|
for dirty_file in dirty_files:
|
|
|
|
|
log.warn(" " + dirty_file)
|
|
|
|
|
|
|
|
|
|
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:")
|
|
|
|
|
for dirty_file in dirty_pattern_files:
|
|
|
|
|
log.warn(" " + dirty_file)
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
2018-11-15 22:16:16 +01:00
|
|
|
def _bump(cfg: config.Config, new_version: str, allow_dirty: bool = False) -> None:
|
|
|
|
|
_vcs: typ.Optional[vcs.VCS]
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
_vcs = vcs.get_vcs()
|
|
|
|
|
except OSError:
|
|
|
|
|
log.warn("Version Control System not found, aborting commit.")
|
|
|
|
|
_vcs = None
|
|
|
|
|
|
|
|
|
|
filepaths = set(cfg.file_patterns.keys())
|
|
|
|
|
|
|
|
|
|
if _vcs:
|
|
|
|
|
_assert_not_dirty(_vcs, filepaths, allow_dirty)
|
|
|
|
|
|
|
|
|
|
rewrite.rewrite(new_version, cfg.file_patterns)
|
|
|
|
|
|
|
|
|
|
if _vcs is None or not cfg.commit:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
for filepath in filepaths:
|
|
|
|
|
_vcs.add(filepath)
|
|
|
|
|
|
|
|
|
|
_vcs.commit(f"bump version to {new_version}")
|
|
|
|
|
|
|
|
|
|
if cfg.tag:
|
|
|
|
|
_vcs.tag(new_version)
|
|
|
|
|
_vcs.push(new_version)
|
|
|
|
|
|
|
|
|
|
|
2018-09-02 21:48:12 +02:00
|
|
|
@cli.command()
|
2018-12-21 19:19:48 +01:00
|
|
|
@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."
|
|
|
|
|
)
|
2018-09-02 23:36:57 +02:00
|
|
|
@click.option(
|
2018-11-11 15:09:12 +01:00
|
|
|
"--dry", default=False, is_flag=True, help="Display diff of changes, don't rewrite files."
|
2018-09-02 21:48:12 +02:00
|
|
|
)
|
2018-09-02 23:36:57 +02:00
|
|
|
@click.option(
|
2018-12-21 19:19:48 +01:00
|
|
|
"--release",
|
|
|
|
|
default=None,
|
|
|
|
|
metavar="<name>",
|
|
|
|
|
help=(
|
2018-12-20 15:25:22 +01:00
|
|
|
f"Override release name of current_version. Valid options are: "
|
|
|
|
|
f"{', '.join(parse.VALID_RELEASE_VALUES)} and final."
|
2018-12-21 19:19:48 +01:00
|
|
|
),
|
2018-09-02 23:36:57 +02:00
|
|
|
)
|
2018-09-03 00:14:10 +02:00
|
|
|
@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(
|
2018-11-11 15:09:12 +01:00
|
|
|
release : typ.Optional[str] = None,
|
2018-12-21 19:19:48 +01:00
|
|
|
verbose : int = 0,
|
2018-11-11 15:09:12 +01:00
|
|
|
dry : bool = False,
|
|
|
|
|
allow_dirty: bool = False,
|
|
|
|
|
fetch : bool = True,
|
2018-09-03 00:14:10 +02:00
|
|
|
) -> None:
|
2018-11-06 21:45:33 +01:00
|
|
|
"""Increment the current version string and update project files."""
|
2018-11-11 15:40:16 +01:00
|
|
|
verbose = max(_VERBOSE, verbose)
|
2018-11-15 22:16:16 +01:00
|
|
|
_init_logging(verbose)
|
2018-09-02 23:36:57 +02:00
|
|
|
|
2018-12-21 19:19:48 +01:00
|
|
|
if release:
|
|
|
|
|
_validate_release_tag(release)
|
2018-09-02 21:48:12 +02:00
|
|
|
|
2018-12-05 09:38:27 +01:00
|
|
|
ctx: config.ProjectContext = config.init_project_ctx(project_path=".")
|
|
|
|
|
cfg: config.MaybeConfig = config.parse(ctx)
|
2018-09-02 21:48:12 +02:00
|
|
|
|
|
|
|
|
if cfg is None:
|
2018-12-21 19:19:48 +01:00
|
|
|
log.error("Could not parse configuration. Perhaps try 'pycalver init'.")
|
2018-09-02 23:36:57 +02:00
|
|
|
sys.exit(1)
|
2018-09-02 21:48:12 +02:00
|
|
|
|
2018-11-11 15:09:12 +01:00
|
|
|
cfg = _update_cfg_from_vcs(cfg, fetch=fetch)
|
|
|
|
|
|
2018-09-03 22:23:51 +02:00
|
|
|
old_version = cfg.current_version
|
2018-11-06 21:45:33 +01:00
|
|
|
new_version = version.incr(old_version, release=release)
|
2018-09-02 21:48:12 +02:00
|
|
|
|
|
|
|
|
log.info(f"Old Version: {old_version}")
|
|
|
|
|
log.info(f"New Version: {new_version}")
|
|
|
|
|
|
2018-11-15 22:16:16 +01:00
|
|
|
if dry or verbose:
|
|
|
|
|
print(rewrite.diff(new_version, cfg.file_patterns))
|
2018-09-02 23:36:57 +02:00
|
|
|
|
2018-11-15 22:16:16 +01:00
|
|
|
if dry:
|
2018-09-02 23:36:57 +02:00
|
|
|
return
|
|
|
|
|
|
2018-11-15 22:16:16 +01:00
|
|
|
_bump(cfg, new_version, allow_dirty)
|