From 1c5834c097b623bf789a722b82e75df7db34c06f Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 4 Jul 2026 13:18:08 -0600 Subject: [PATCH 1/9] chore(tox): adjust ruff command in tox config --- pyproject.toml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8f1071d59..18f5237cf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "requests ~= 2.25", "jinja2 ~= 3.1", "python-gitlab >= 4.0.0, < 9.0.0", - "tomlkit >= 0.13.0, < 0.16.0", + "tomlkit ~= 0.13.0", "dotty-dict ~= 1.3", "importlib-resources ~= 6.0", "pydantic ~= 2.0", @@ -57,7 +57,7 @@ repository = "http://github.com/python-semantic-release/python-semantic-release. [project.optional-dependencies] build = [ "build ~= 1.2", - "tomlkit >= 0.13.0, < 0.16.0", + "tomlkit ~= 0.13.0", ] docs = [ "Sphinx ~= 7.4", @@ -173,9 +173,10 @@ commands = [testenv:ruff] deps = .[dev] commands = - ruff check . --statistics --output-format=text + ruff check . --statistics """ + [tool.mypy] python_version = "3.8" show_column_numbers = true @@ -213,6 +214,7 @@ ignore_missing_imports = true module = "dotty_dict" ignore_missing_imports = true + [tool.ruff] line-length = 88 target-version = "py38" From 01707ead53e093fe217a82c9ff9898c5e4daf9dc Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 4 Jul 2026 13:19:22 -0600 Subject: [PATCH 2/9] build(deps): extend `click` dependency range to include `v8.2+` --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 18f5237cf..2faba0697 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ authors = [ { name = "codejedi365", email = "codejedi365@gmail.com" }, ] dependencies = [ - "click ~= 8.1.0", + "click >= 8.1.0, < 9.0.0", "click-option-group ~= 0.5", "gitpython ~= 3.0", "requests ~= 2.25", From 85408e1742f4df005fed70971096d80f30f145d0 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 4 Jul 2026 13:20:24 -0600 Subject: [PATCH 3/9] test(config): refactor test fixtures to handle click library variance --- tests/conftest.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 4e78b59b5..90f21339d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,6 +13,7 @@ from unittest import mock import pytest +from click import __version__ as click_version from click.testing import CliRunner from filelock import FileLock from git import Commit, Repo @@ -193,9 +194,17 @@ def pytest_collection_modifyitems(config: pytest.Config, items: list[pytest.Item item.add_marker(comprehensive_test_skip_marker) +def get_cli_runner() -> CliRunner: + return ( + CliRunner() + if tuple(map(int, click_version.split("."))) >= (8, 2, 0) + else CliRunner(mix_stderr=False) # type: ignore[call-arg] + ) + + @pytest.fixture def cli_runner() -> CliRunner: - return CliRunner(mix_stderr=False) + return get_cli_runner() @pytest.fixture(scope="session") @@ -211,7 +220,7 @@ def _run_cli( # Prevent logs from being propagated to the root logger (pytest) logger.propagate = False - cli_runner = CliRunner(mix_stderr=False) + cli_runner = get_cli_runner() env_vars = {**clean_os_environment, **(env or {})} args = ["-vv", *(argv or [])] From ef97ac9f0f7eb3346f5a0bf2fe57b71de4894513 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 4 Jul 2026 13:23:38 -0600 Subject: [PATCH 4/9] refactor(cli): refactor for future click library implementation In click 8.2.0, they deprecated the `MultiCommand` object in support of `Group`. Adjusting accordingly to prevent future breaking change as `Group` currently exists in `click@v8.1.0`. --- src/semantic_release/cli/commands/main.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/semantic_release/cli/commands/main.py b/src/semantic_release/cli/commands/main.py index c10d3f647..387893974 100644 --- a/src/semantic_release/cli/commands/main.py +++ b/src/semantic_release/cli/commands/main.py @@ -30,8 +30,8 @@ ] -class Cli(click.MultiCommand): - """Root MultiCommand for the semantic-release CLI""" +class Cli(click.Group): + """Root Group for the semantic-release CLI""" class SubCmds(Enum): """Subcommand import definitions""" @@ -42,12 +42,12 @@ class SubCmds(Enum): VERSION = f"{__package__}.version" PUBLISH = f"{__package__}.publish" - def list_commands(self, _ctx: click.Context) -> list[str]: + def list_commands(self, ctx: click.Context) -> list[str]: # noqa: ARG002 # Used for shell-completion return [subcmd.lower().replace("_", "-") for subcmd in Cli.SubCmds.__members__] - def get_command(self, _ctx: click.Context, name: str) -> click.Command | None: - subcmd_name = name.lower().replace("-", "_") + def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command | None: # noqa: ARG002 + subcmd_name = cmd_name.lower().replace("-", "_") try: subcmd_def: Cli.SubCmds = Cli.SubCmds.__dict__[subcmd_name.upper()] module_path = subcmd_def.value From e5ff73102c0153f9cc857b93d3eacd8576a7dd99 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 4 Jul 2026 13:25:30 -0600 Subject: [PATCH 5/9] test: refactor e2e tests to work across `click@v8.1.0` to `v8.2.0` --- tests/e2e/cmd_config/test_generate_config.py | 12 ++++++------ tests/e2e/test_main.py | 8 +++++++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/e2e/cmd_config/test_generate_config.py b/tests/e2e/cmd_config/test_generate_config.py index a9db934ea..fe3174d43 100644 --- a/tests/e2e/cmd_config/test_generate_config.py +++ b/tests/e2e/cmd_config/test_generate_config.py @@ -51,11 +51,11 @@ def test_generate_config_toml( # Evaluate: Check that the command ran successfully and that the output matches the expected configuration assert_successful_exit_code(result, cli_cmd) - assert expected_config_as_str == result.output.strip() + assert expected_config_as_str == result.stdout.strip() # Setup: Write the generated configuration to a file config_file = "releaserc.toml" - example_project_dir.joinpath(config_file).write_text(result.output) + example_project_dir.joinpath(config_file).write_text(result.stdout) # Act: Validate that the generated config is a valid configuration for PSR cli_cmd = [ @@ -93,11 +93,11 @@ def test_generate_config_json( # Evaluate: Check that the command ran successfully and that the output matches the expected configuration assert_successful_exit_code(result, cli_cmd) - assert expected_config_as_str == result.output.strip() + assert expected_config_as_str == result.stdout.strip() # Setup: Write the generated configuration to a file config_file = "releaserc.json" - example_project_dir.joinpath(config_file).write_text(result.output) + example_project_dir.joinpath(config_file).write_text(result.stdout) # Act: Validate that the generated config is a valid configuration for PSR cli_cmd = [ @@ -144,7 +144,7 @@ def test_generate_config_pyproject_toml( # Evaluate: Check that the command ran successfully and that the output matches the expected configuration assert_successful_exit_code(result, cli_cmd) - assert expected_config_as_str == result.output.strip() + assert expected_config_as_str == result.stdout.strip() # Setup: Write the generated configuration to a file example_pyproject_toml.write_text( @@ -152,7 +152,7 @@ def test_generate_config_pyproject_toml( "\n\n", [ example_pyproject_toml.read_text(encoding="utf-8").strip(), - result.output, + result.stdout, ], ) ) diff --git a/tests/e2e/test_main.py b/tests/e2e/test_main.py index 4e1df87e0..86d60b57b 100644 --- a/tests/e2e/test_main.py +++ b/tests/e2e/test_main.py @@ -9,6 +9,7 @@ import git import pytest +from click import __version__ as click_version from click.testing import CliRunner from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture @@ -65,7 +66,12 @@ def test_main_no_args_passes_w_help_text(): cli_cmd = [MAIN_PROG_NAME] result = CliRunner().invoke(main, prog_name=cli_cmd[0]) - assert_successful_exit_code(result, cli_cmd) + + if tuple(map(int, click_version.split("."))) >= (8, 2, 0): + assert_exit_code(2, result, cli_cmd) + else: + assert_successful_exit_code(result, cli_cmd) + assert "Usage: " in result.output From 725e16b3be0f4a537cfbfac6dc5d5219fc2010d6 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 4 Jul 2026 23:06:22 -0600 Subject: [PATCH 6/9] ci(validate): adjust lint job to use Python 3.8 instead --- .github/workflows/validate.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 198fd2c3a..b0e966d16 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -451,10 +451,10 @@ jobs: ref: ${{ github.sha }} fetch-depth: 1 - - name: Setup | Install Python ${{ env.COMMON_PYTHON_VERSION }} + - name: Setup | Install Python ${{ env.LOWEST_PYTHON_VERSION }} uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: - python-version: ${{ env.COMMON_PYTHON_VERSION }} + python-version: ${{ env.LOWEST_PYTHON_VERSION }} cache: 'pip' - name: Setup | Install dependencies From ceb4e976c3c75c7b23d8788a3f56d201480e6664 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 4 Jul 2026 23:19:44 -0600 Subject: [PATCH 7/9] style(mypy-config): remove specified 3.8 compiler from config - defaults to autodetect --- pyproject.toml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2faba0697..e20798ff5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,12 +84,12 @@ test = [ "requests-mock ~= 1.10", ] dev = [ - "pre-commit ~= 4.3", + "pre-commit >= 3.5.0, < 5.0.0", "tox ~= 4.11", "ruff == 0.6.1" ] mypy = [ - "mypy == 1.16.1", + "mypy >= 1.14.1, < 2.0.0", "types-Deprecated ~= 1.2", "types-requests ~= 2.32.0", "types-pyyaml ~= 6.0", @@ -178,7 +178,6 @@ commands = [tool.mypy] -python_version = "3.8" show_column_numbers = true show_error_context = true pretty = true From 0ef2d0c36a056386284ac02b4c82d44033a57b47 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 4 Jul 2026 17:02:58 +0000 Subject: [PATCH 8/9] build(deps-dev): expand `pytest` dependency range to include `v9.0.0` Signed-off-by: dependabot[bot] Signed-off-by: codejedi365 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e20798ff5..d688ee0e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,7 +71,7 @@ test = [ "flatdict ~= 4.0", "freezegun ~= 1.5", "pyyaml ~= 6.0", - "pytest ~= 8.3", + "pytest >= 8.3, < 10.0", "pytest-clarity ~= 1.0", "pytest-cov >= 5.0.0, < 8.0.0", "pytest-env ~= 1.0", From 45f0f27545e4a0a0627f4677040a05d16f214f0e Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 5 Jul 2026 00:00:04 -0600 Subject: [PATCH 9/9] test: adjust style to match expected pytest environment & type-checker --- tests/e2e/cmd_version/test_version_build.py | 40 +++++++++++---------- tests/util.py | 4 +-- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/tests/e2e/cmd_version/test_version_build.py b/tests/e2e/cmd_version/test_version_build.py index 51022da87..40ac74388 100644 --- a/tests/e2e/cmd_version/test_version_build.py +++ b/tests/e2e/cmd_version/test_version_build.py @@ -26,27 +26,29 @@ @pytest.mark.skipif(sys.platform == "win32", reason="Unix only") @pytest.mark.parametrize( "shell", - filter( - None, - [ - # because we will actually run the build command in this shell, we must ensure it exists - "bash" - if list( - filter( - lambda sh_exe: Path(sh_exe).exists(), - ("/bin/bash", "/usr/bin/bash", "/usr/local/bin/bash"), + list( + filter( + None, + [ + # because we will actually run the build command in this shell, we must ensure it exists + "bash" + if list( + filter( + lambda sh_exe: Path(sh_exe).exists(), + ("/bin/bash", "/usr/bin/bash", "/usr/local/bin/bash"), + ) ) - ) - else "", - "zsh" - if list( - filter( - lambda sh_exe: Path(sh_exe).exists(), - ("/bin/zsh", "/usr/bin/zsh", "/usr/local/bin/zsh"), + else "", + "zsh" + if list( + filter( + lambda sh_exe: Path(sh_exe).exists(), + ("/bin/zsh", "/usr/bin/zsh", "/usr/local/bin/zsh"), + ) ) - ) - else "", - ], + else "", + ], + ) ) or ["sh"], ) diff --git a/tests/util.py b/tests/util.py index d97e04845..8ddff0a83 100644 --- a/tests/util.py +++ b/tests/util.py @@ -51,7 +51,7 @@ GitCommandWrapperType: TypeAlias = Git -def get_func_qual_name(func: Callable) -> str: +def get_func_qual_name(func: Callable[[Any], Any]) -> str: return str.join(".", filter(None, [func.__module__, func.__qualname__])) @@ -79,7 +79,7 @@ def assert_successful_exit_code(result: ClickInvokeResult, cli_cmd: list[str]) - return assert_exit_code(SUCCESS_EXIT_CODE, result, cli_cmd) -def get_full_qualname(callable_obj: Callable) -> str: +def get_full_qualname(callable_obj: Callable[[Any], Any]) -> str: parts = filter( None, [