diff --git a/bumpversion/config/__init__.py b/bumpversion/config/__init__.py index 3e79334..19eec17 100644 --- a/bumpversion/config/__init__.py +++ b/bumpversion/config/__init__.py @@ -35,8 +35,8 @@ "parts": {}, "files": [], "setup_hooks": [], - "pre_bump_hooks": [], - "post_bump_hooks": [], + "pre_commit_hooks": [], + "post_commit_hooks": [], } diff --git a/bumpversion/config/models.py b/bumpversion/config/models.py index 563ef49..d6ce3ef 100644 --- a/bumpversion/config/models.py +++ b/bumpversion/config/models.py @@ -101,8 +101,8 @@ class Config(BaseSettings): parts: Dict[str, VersionComponentSpec] files: List[FileChange] = Field(default_factory=list) setup_hooks: List[str] = Field(default_factory=list) - pre_bump_hooks: List[str] = Field(default_factory=list) - post_bump_hooks: List[str] = Field(default_factory=list) + pre_commit_hooks: List[str] = Field(default_factory=list) + post_commit_hooks: List[str] = Field(default_factory=list) included_paths: List[str] = Field(default_factory=list) excluded_paths: List[str] = Field(default_factory=list) model_config = SettingsConfigDict(env_prefix="bumpversion_") diff --git a/bumpversion/hooks.py b/bumpversion/hooks.py new file mode 100644 index 0000000..988da63 --- /dev/null +++ b/bumpversion/hooks.py @@ -0,0 +1,90 @@ +"""Implementation of the hook interface.""" + +import datetime +import os +import subprocess +from typing import Dict, Optional + +from bumpversion.config.models import Config +from bumpversion.ui import get_indented_logger + +PREFIX = "BVHOOK_" + +logger = get_indented_logger(__name__) + + +def run_command(script: str, environment: Optional[dict] = None) -> subprocess.CompletedProcess: + """Runs command-line programs using the shell.""" + if not isinstance(script, str): + raise TypeError(f"`script` must be a string, not {type(script)}") + if environment and not isinstance(environment, dict): + raise TypeError(f"`environment` must be a dict, not {type(environment)}") + return subprocess.run(script, env=environment, encoding="utf-8", shell=True, text=True, capture_output=True) + + +def base_env(config: Config) -> Dict[str, str]: + """Provide the base environment variables.""" + return { + f"{PREFIX}NOW": datetime.datetime.now().isoformat(), + f"{PREFIX}UTCNOW": datetime.datetime.now(datetime.timezone.utc).isoformat(), + **os.environ, + **scm_env(config), + } + + +def scm_env(config: Config) -> Dict[str, str]: + """Provide the scm environment variables.""" + scm = config.scm_info + return { + f"{PREFIX}COMMIT_SHA": scm.commit_sha or "", + f"{PREFIX}DISTANCE_TO_LATEST_TAG": str(scm.distance_to_latest_tag) or "0", + f"{PREFIX}IS_DIRTY": str(scm.dirty), + f"{PREFIX}BRANCH_NAME": scm.branch_name or "", + f"{PREFIX}SHORT_BRANCH_NAME": scm.short_branch_name or "", + f"{PREFIX}CURRENT_VERSION": scm.current_version or "", + f"{PREFIX}CURRENT_TAG": scm.current_tag or "", + } + + +def current_version_env(config: Config) -> Dict[str, str]: + """Provide the current version environment variables.""" + version_str = config.current_version + version = config.version_config.parse(version_str) + + return {f"{PREFIX}CURRENT_{part.upper()}": version[part].value for part in version} + + +def setup_hook_env(config: Config) -> Dict[str, str]: + """Provide the environment dictionary for `setup_hook`s.""" + return {**base_env(config), **scm_env(config), **current_version_env(config)} + + +def run_setup_hooks(config: Config) -> None: + """Run the setup hooks.""" + env = setup_hook_env(config) + if config.setup_hooks: + logger.info("Running setup hooks:") + else: + logger.info("No setup hooks defined") + return + + logger.indent() + for script in config.setup_hooks: + logger.debug(f"Running {script!r}") + logger.indent() + result = run_command(script, env) + logger.debug(result.stdout) + logger.debug(result.stderr) + logger.debug(f"Exited with {result.returncode}") + logger.indent() + logger.dedent() + + +def run_pre_commit_hooks(config: Config) -> None: + """Run the pre-commit hooks.""" + pass + + +def run_post_commit_hooks(config: Config) -> None: + """Run the post-commit hooks.""" + pass diff --git a/pyproject.toml b/pyproject.toml index 32aa90c..f99076e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -186,7 +186,7 @@ line-length = 119 select = ["E", "W", "F", "I", "N", "B", "BLE", "C", "D", "E", "F", "I", "N", "S", "T", "W", "RUF", "NPY", "PD", "PGH", "ANN", "C90", "PLC", "PLE", "PLW", "TCH"] ignore = [ "ANN002", "ANN003", "ANN101", "ANN102", "ANN204", "ANN401", - "S101", "S104", + "S101", "S104", "S602", "D105", "D106", "D107", "D200", "D212", "PD011", "PLW1510", diff --git a/tests/fixtures/basic_cfg_expected.txt b/tests/fixtures/basic_cfg_expected.txt index a7cc03c..0e0fb5d 100644 --- a/tests/fixtures/basic_cfg_expected.txt +++ b/tests/fixtures/basic_cfg_expected.txt @@ -78,8 +78,8 @@ 'independent': False, 'optional_value': 'gamma', 'values': ['dev', 'gamma']}}, - 'post_bump_hooks': [], - 'pre_bump_hooks': [], + 'post_commit_hooks': [], + 'pre_commit_hooks': [], 'regex': False, 'replace': '{new_version}', 'scm_info': {'branch_name': None, diff --git a/tests/fixtures/basic_cfg_expected.yaml b/tests/fixtures/basic_cfg_expected.yaml index 893d19f..9c35a1d 100644 --- a/tests/fixtures/basic_cfg_expected.yaml +++ b/tests/fixtures/basic_cfg_expected.yaml @@ -106,9 +106,9 @@ parts: values: - "dev" - "gamma" -post_bump_hooks: +post_commit_hooks: -pre_bump_hooks: +pre_commit_hooks: regex: false replace: "{new_version}" diff --git a/tests/fixtures/basic_cfg_expected_full.json b/tests/fixtures/basic_cfg_expected_full.json index fb1816d..df4dade 100644 --- a/tests/fixtures/basic_cfg_expected_full.json +++ b/tests/fixtures/basic_cfg_expected_full.json @@ -121,8 +121,8 @@ ] } }, - "post_bump_hooks": [], - "pre_bump_hooks": [], + "post_commit_hooks": [], + "pre_commit_hooks": [], "regex": false, "replace": "{new_version}", "scm_info": {