support for glob patterns

This commit is contained in:
Manuel Barkhau 2020-09-18 19:52:40 +00:00
parent a8e658d1c4
commit e1aaf7629b
7 changed files with 147 additions and 72 deletions

View file

@ -8,6 +8,7 @@
- Better support for optional parts.
- New: Start `BUILD` parts at `1000` to avoid leading zero truncation.
- New gitlab #10: `--pin-date` to keep date parts unchanged, and only increment non-date parts.
- New: enable globs for filenames in `pycalver:file_patterns`
- Fix gitlab #8: Push tags only pushed tags, not actual commit.
- Fix gitlab #9: Make commit message configurable.

View file

@ -1,4 +1,4 @@
MIT License Copyright (c) 2020 Manuel Barkhau (mbarkhau@gmail.com)
MIT License Copyright (c) 2018-2020 Manuel Barkhau (mbarkhau@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -17,13 +17,14 @@ 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
import pycalver.rewrite as v1rewrite
from pycalver import vcs
from pycalver import config
# import pycalver2.cli as v2cli
_VERBOSE = 0
@ -149,31 +150,21 @@ def show(verbose: int = 0, fetch: bool = True) -> None:
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:
# pylint:disable=broad-except; Mostly we expect IOError here, but
# could be other things and there's no option to recover anyway.
logger.error(str(ex), exc_info=True)
sys.exit(1)
def _print_diff(diff: str) -> None:
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 _incr(
@ -229,10 +220,15 @@ def _bump(
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)
if cfg.is_new_pattern:
v2cli.rewrite(cfg, new_version)
else:
v1cli.rewrite(cfg, new_version)
except v1rewrite.NoPatternMatch as ex:
logger.error(str(ex))
sys.exit(1)
except Exception as ex:
# TODO (mb 2020-09-18): Investigate error messages
logger.error(str(ex))
sys.exit(1)
@ -285,10 +281,10 @@ def init(verbose: int = 0, dry: bool = False) -> None:
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
if cfg.is_new_pattern:
return v2cli.update_cfg_from_vcs(cfg, all_tags)
else:
return v1cli.update_cfg_from_vcs(cfg, all_tags)
@cli.command()
@ -375,7 +371,11 @@ def bump(
)
if new_version is None:
is_semver = "{semver}" in cfg.version_pattern
is_semver = "{semver}" in cfg.version_pattern or (
"MAJOR" in cfg.version_pattern
and "MAJOR" in cfg.version_pattern
and "PATCH" 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.")
@ -387,7 +387,20 @@ def bump(
logger.info(f"New Version: {new_version}")
if dry or verbose >= 2:
_try_print_diff(cfg, new_version)
try:
if cfg.is_new_pattern:
diff = v2cli.get_diff(cfg, new_version)
else:
diff = v1cli.get_diff(cfg, new_version)
_print_diff(diff)
except v1rewrite.NoPatternMatch as ex:
logger.error(str(ex))
sys.exit(1)
except Exception as ex:
# pylint:disable=broad-except; Mostly we expect IOError here, but
# could be other things and there's no option to recover anyway.
logger.error(str(ex))
sys.exit(1)
if dry:
return

View file

@ -5,7 +5,7 @@
# SPDX-License-Identifier: MIT
"""Parse setup.cfg or pycalver.cfg files."""
import os
import glob
import typing as typ
import logging
import datetime as dt
@ -14,7 +14,8 @@ import configparser
import toml
import pathlib2 as pl
from . import version
import pycalver.version as v1version
import pycalver2.version as v2version
logger = logging.getLogger("pycalver.config")
@ -82,9 +83,10 @@ class Config(typ.NamedTuple):
pep440_version : str
commit_message : str
commit: bool
tag : bool
push : bool
commit : bool
tag : bool
push : bool
is_new_pattern: bool
file_patterns: PatternsByGlob
@ -93,12 +95,13 @@ def _debug_str(cfg: Config) -> str:
cfg_str_parts = [
"Config Parsed: Config(",
f"current_version='{cfg.current_version}'",
"version_pattern='{pycalver}'",
f"version_pattern='{cfg.version_pattern}'",
f"pep440_version='{cfg.pep440_version}'",
f"commit_message='{cfg.commit_message}'",
f"commit={cfg.commit}",
f"tag={cfg.tag}",
f"push={cfg.push}",
f"is_new_pattern={cfg.is_new_pattern}",
"file_patterns={",
]
@ -197,7 +200,7 @@ def _normalize_file_patterns(raw_cfg: RawConfig) -> FilePatterns:
"""
version_str : str = raw_cfg['current_version']
version_pattern: str = raw_cfg['version_pattern']
pep440_version : str = version.to_pep440(version_str)
pep440_version : str = v1version.to_pep440(version_str)
file_patterns: FilePatterns
if 'file_patterns' in raw_cfg:
@ -205,9 +208,12 @@ def _normalize_file_patterns(raw_cfg: RawConfig) -> FilePatterns:
else:
file_patterns = {}
for filepath, patterns in list(file_patterns.items()):
if not os.path.exists(filepath):
logger.warning(f"Invalid config, no such file: {filepath}")
for filepath_glob, patterns in list(file_patterns.items()):
filepaths = glob.glob(filepath_glob)
if not filepaths:
logger.warning(f"Invalid config, no such file: {filepath_glob}")
# fallback to treating it as a simple path
filepaths = [filepath_glob]
normalized_patterns: typ.List[str] = []
for pattern in patterns:
@ -219,11 +225,12 @@ def _normalize_file_patterns(raw_cfg: RawConfig) -> FilePatterns:
elif version_pattern == "{semver}":
normalized_pattern = normalized_pattern.replace("{pep440_version}", "{semver}")
elif "{pep440_version}" in pattern:
logger.warning(f"Invalid config, cannot match '{pattern}' for '{filepath}'.")
logger.warning(f"Invalid config, cannot match '{pattern}' for '{filepath_glob}'.")
logger.warning(f"No mapping of '{version_pattern}' to '{pep440_version}'")
normalized_patterns.append(normalized_pattern)
file_patterns[filepath] = normalized_patterns
for filepath in filepaths:
file_patterns[filepath] = normalized_patterns
return file_patterns
@ -237,18 +244,27 @@ def _parse_config(raw_cfg: RawConfig) -> Config:
version_str: str = raw_cfg['current_version']
version_str = raw_cfg['current_version'] = version_str.strip("'\" ")
# TODO (mb 2020-09-06): new style pattern by default
# version_pattern: str = raw_cfg.get('version_pattern', "vYYYY0M.BUILD[-TAG]")
version_pattern: str = raw_cfg.get('version_pattern', "{pycalver}")
version_pattern = raw_cfg['version_pattern'] = version_pattern.strip("'\" ")
commit_message: str = raw_cfg.get('commit_message', DEFAULT_COMMIT_MESSAGE)
commit_message = raw_cfg['commit_message'] = commit_message.strip("'\" ")
is_new_pattern = not ("{" in version_pattern or "}" in version_pattern)
# TODO (mb 2020-09-18): Validate Pattern
# detect YY with WW or UU -> suggest GG with VV
# detect YYMM -> suggest YY0M
# detect YYWW -> suggest YY0W
# NOTE (mb 2019-01-05): Provoke ValueError if version_pattern
# and current_version are not compatible.
version.parse_version_info(version_str, version_pattern)
if is_new_pattern:
v2version.parse_version_info(version_str, version_pattern)
else:
v1version.parse_version_info(version_str, version_pattern)
pep440_version = version.to_pep440(version_str)
pep440_version = v1version.to_pep440(version_str)
commit = raw_cfg['commit']
tag = raw_cfg['tag']
@ -275,6 +291,7 @@ def _parse_config(raw_cfg: RawConfig) -> Config:
commit=commit,
tag=tag,
push=push,
is_new_pattern=is_new_pattern,
file_patterns=file_patterns,
)
logger.debug(_debug_str(cfg))

View file

@ -236,7 +236,7 @@ def _replace_pattern_parts(pattern: str) -> str:
)
last_start_idx = start_idx
return "(?P<version>" + result_pattern + ")"
return result_pattern
def compile_pattern_str(pattern: str) -> str:

View file

@ -98,7 +98,7 @@ def test_incr_default(runner):
result = runner.invoke(cli, ['test', "-vv", old_version])
assert result.exit_code == 0
new_version = initial_version.replace(".0001-alpha", ".11000-alpha")
new_version = initial_version.replace(".1001-alpha", ".11000-alpha")
assert f"Version: {new_version}\n" in result.output
@ -152,7 +152,7 @@ def test_incr_to_beta(runner):
result = runner.invoke(cli, ['test', old_version, "-vv", "--release", "beta"])
assert result.exit_code == 0
new_version = initial_version.replace(".0001-alpha", ".11000-beta")
new_version = initial_version.replace(".1001-alpha", ".11000-beta")
assert f"Version: {new_version}\n" in result.output
@ -162,7 +162,7 @@ def test_incr_to_final(runner):
result = runner.invoke(cli, ['test', old_version, "-vv", "--release", "final"])
assert result.exit_code == 0
new_version = initial_version.replace(".0001-alpha", ".11000")
new_version = initial_version.replace(".1001-alpha", ".11000")
assert f"Version: {new_version}\n" in result.output
@ -178,8 +178,8 @@ def _add_project_files(*files):
with pl.Path("README.md").open(mode="wt", encoding="utf-8") as fobj:
fobj.write(
"""
Hello World v201701.0002-alpha !
aka. 201701.2a0 !
Hello World v201701.1002-alpha !
aka. 201701.1002a0 !
"""
)
@ -337,8 +337,8 @@ def test_git_tag_eval(runner):
result = runner.invoke(cli, ['init', "-vv"])
assert result.exit_code == 0
initial_version = config._initial_version()
tag_version = initial_version.replace(".0001-alpha", ".0123-beta")
tag_version_pep440 = tag_version[1:7] + ".123b0"
tag_version = initial_version.replace(".1001-alpha", ".1123-beta")
tag_version_pep440 = tag_version[1:7] + ".1123b0"
shell("git", "tag", "--annotate", tag_version, "--message", f"bump version to {tag_version}")
@ -357,8 +357,8 @@ def test_hg_tag_eval(runner):
result = runner.invoke(cli, ['init', "-vv"])
assert result.exit_code == 0
initial_version = config._initial_version()
tag_version = initial_version.replace(".0001-alpha", ".0123-beta")
tag_version_pep440 = tag_version[1:7] + ".123b0"
tag_version = initial_version.replace(".1001-alpha", ".1123-beta")
tag_version_pep440 = tag_version[1:7] + ".1123b0"
shell("hg", "tag", tag_version, "--message", f"bump version to {tag_version}")
@ -377,20 +377,20 @@ def test_novcs_bump(runner):
result = runner.invoke(cli, ['bump', "-vv"])
assert result.exit_code == 0
calver = config._initial_version()[:7]
calver = config._initial_version().split(".")[0]
with pl.Path("README.md").open() as fobj:
content = fobj.read()
assert calver + ".0002-alpha !\n" in content
assert calver[1:] + ".2a0 !\n" in content
assert calver + ".1002-alpha !\n" in content
assert calver[1:] + ".1002a0 !\n" in content
result = runner.invoke(cli, ['bump', "-vv", "--release", "beta"])
assert result.exit_code == 0
with pl.Path("README.md").open() as fobj:
content = fobj.read()
assert calver + ".0003-beta !\n" in content
assert calver[1:] + ".3b0 !\n" in content
assert calver + ".1003-beta !\n" in content
assert calver[1:] + ".1003b0 !\n" in content
def test_git_bump(runner):
@ -410,7 +410,7 @@ def test_git_bump(runner):
with pl.Path("README.md").open() as fobj:
content = fobj.read()
assert calver + ".0002-alpha !\n" in content
assert calver + ".1002-alpha !\n" in content
def test_hg_bump(runner):
@ -430,7 +430,7 @@ def test_hg_bump(runner):
with pl.Path("README.md").open() as fobj:
content = fobj.read()
assert calver + ".0002-alpha !\n" in content
assert calver + ".1002-alpha !\n" in content
def test_empty_git_bump(runner, caplog):
@ -530,3 +530,8 @@ def test_bump_semver_diff(runner, caplog):
assert "+++ setup.cfg" in out_lines
assert "-current_version = \"0.1.0\"" in out_lines
assert f"+current_version = \"{expected}\"" in out_lines
# def test_custom_commit_message(runner):
# # TODO (mb 2020-09-18):
# assert False

View file

@ -63,6 +63,26 @@ setup.cfg =
"""
NEW_PATTERN_CFG_FIXTURE = """
[pycalver]
current_version = "v201808.1456-beta"
version_pattern = "vYYYY0M.BUILD[-TAG]"
commit_message = "bump version to {new_version}"
commit = True
tag = True
push = True
[pycalver:file_patterns]
setup.py =
{version}
{pep440_version}
setup.cfg =
current_version = "{version}"
src/project/*.py =
Copyright (c) 2018-YYYY
"""
def mk_buf(text):
buf = io.StringIO()
buf.write(text)
@ -104,7 +124,7 @@ def test_parse_toml_2():
assert cfg.file_patterns["pycalver.toml"] == ['current_version = "{semver}"']
def test_parse_cfg():
def test_parse_v1_cfg():
buf = mk_buf(SETUP_CFG_FIXTURE)
raw_cfg = config._parse_cfg(buf)
@ -120,6 +140,25 @@ def test_parse_cfg():
assert cfg.file_patterns["setup.cfg"] == ['current_version = "{pycalver}"']
def test_parse_v2_cfg():
buf = mk_buf(NEW_PATTERN_CFG_FIXTURE)
raw_cfg = config._parse_cfg(buf)
cfg = config._parse_config(raw_cfg)
assert cfg.current_version == "v201808.1456-beta"
assert cfg.commit_message == "bump version to {new_version}"
assert cfg.commit is True
assert cfg.tag is True
assert cfg.push is True
assert "setup.py" in cfg.file_patterns
assert "setup.cfg" in cfg.file_patterns
# TODO (mb 2020-09-18):
# assert cfg.file_patterns["setup.py" ] == ["vYYYY0M.BUILD[-TAG]", "YYYY0M.BLD[PYTAGNUM]"]
# assert cfg.file_patterns["setup.cfg" ] == ['current_version = "vYYYY0M.BUILD[-TAG]"']
# assert cfg.file_patterns["src/project/*.py"] == ['Copyright (c) 2018-YYYY"']
def test_parse_default_toml():
project_path = util.FIXTURES_DIR / "project_a"
@ -295,7 +334,7 @@ def test_parse_missing_version(tmpdir):
"\n".join(
(
"[pycalver]",
# f"current_version = v201808.0001-dev",
# f"current_version = v201808.1001-dev",
"commit = False",
)
)