From 2a26fb6644437dddd8ab119bacfc3d8f8ef5a116 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 14 Jan 2026 12:26:11 +0800 Subject: [PATCH 1/6] feat(cli): add cz --version back and add cz --report to separate them from cz version --- commitizen/cli.py | 24 +++++++++++++++++++ tests/test_cli.py | 19 +++++++++++++++ ...invalid_command_py_3_10___invalid_arg_.txt | 3 ++- ...nvalid_command_py_3_10_invalidCommand_.txt | 3 ++- ...invalid_command_py_3_11___invalid_arg_.txt | 3 ++- ...nvalid_command_py_3_11_invalidCommand_.txt | 3 ++- ...invalid_command_py_3_12___invalid_arg_.txt | 3 ++- ...nvalid_command_py_3_12_invalidCommand_.txt | 3 ++- ...invalid_command_py_3_13___invalid_arg_.txt | 3 ++- ...nvalid_command_py_3_13_invalidCommand_.txt | 3 ++- ...invalid_command_py_3_14___invalid_arg_.txt | 3 ++- ...nvalid_command_py_3_14_invalidCommand_.txt | 3 ++- tests/test_cli/test_no_argv_py_3_10_.txt | 5 +++- tests/test_cli/test_no_argv_py_3_11_.txt | 5 +++- tests/test_cli/test_no_argv_py_3_12_.txt | 5 +++- tests/test_cli/test_no_argv_py_3_13_.txt | 5 +++- tests/test_cli/test_no_argv_py_3_14_.txt | 5 +++- 17 files changed, 83 insertions(+), 15 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index 177099480e..049e37d192 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -2,6 +2,7 @@ import argparse import logging +import platform import sys from copy import deepcopy from functools import partial @@ -13,6 +14,7 @@ from decli import cli from commitizen import commands, config, out, version_schemes +from commitizen.__version__ import __version__ from commitizen.defaults import DEFAULT_SETTINGS from commitizen.exceptions import ( CommitizenException, @@ -104,6 +106,16 @@ def __call__( "required": False, "help": "Comma-separated error codes that won't raise error, e.g., cz -nr 1,2,3 bump. See codes at https://commitizen-tools.github.io/commitizen/exit_codes/", }, + { + "name": ["-v", "--version"], + "action": "store_true", + "help": "Show the version of the installed commitizen", + }, + { + "name": ["--report"], + "action": "store_true", + "help": "Show system information for reporting bugs", + }, ], "subcommands": { "title": "commands", @@ -677,6 +689,18 @@ def main() -> None: parser.print_help(sys.stderr) raise ExpectedExit() + # TODO(bearomorphism): mark `cz version --commitizen` as deprecated after `cz version` feature is stable + if "--version" in sys.argv: + out.write(__version__) + raise ExpectedExit() + + # TODO(bearomorphism): mark `cz version --report` as deprecated after `cz version` feature is stable + if "--report" in sys.argv: + out.write(f"Commitizen Version: {__version__}") + out.write(f"Python Version: {sys.version}") + out.write(f"Operating System: {platform.system()}") + raise ExpectedExit() + # This is for the command required constraint in 2.0 try: args, unknown_args = parser.parse_known_args() diff --git a/tests/test_cli.py b/tests/test_cli.py index c1f6d5beda..15e5bcb55c 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -8,6 +8,7 @@ from pytest_mock import MockFixture from commitizen import cli +from commitizen.__version__ import __version__ from commitizen.exceptions import ( ConfigFileNotFound, ExpectedExit, @@ -55,6 +56,24 @@ def test_cz_with_arg_but_without_command(util: UtilFixture): assert "Command is required" in str(excinfo.value) +def test_cz_with_version_arg(util: UtilFixture, capsys): + """Test that cz shows the version when --version is used.""" + with pytest.raises(ExpectedExit): + util.run_cli("--version") + out, _ = capsys.readouterr() + assert __version__ in out + + +def test_cz_with_report_arg(util: UtilFixture, capsys): + """Test that cz shows the report when --report is used.""" + with pytest.raises(ExpectedExit): + util.run_cli("--report") + out, _ = capsys.readouterr() + assert "Commitizen Version:" in out + assert "Python Version:" in out + assert "Operating System:" in out + + def test_name(util: UtilFixture, capsys): util.run_cli("-n", "cz_jira", "example") out, _ = capsys.readouterr() diff --git a/tests/test_cli/test_invalid_command_py_3_10___invalid_arg_.txt b/tests/test_cli/test_invalid_command_py_3_10___invalid_arg_.txt index 148b4eacdb..33ae4930ed 100644 --- a/tests/test_cli/test_invalid_command_py_3_10___invalid_arg_.txt +++ b/tests/test_cli/test_invalid_command_py_3_10___invalid_arg_.txt @@ -1,4 +1,5 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] +usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] + [--report] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... cz: error: the following arguments are required: {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} diff --git a/tests/test_cli/test_invalid_command_py_3_10_invalidCommand_.txt b/tests/test_cli/test_invalid_command_py_3_10_invalidCommand_.txt index e2d4416b81..ac64f94f97 100644 --- a/tests/test_cli/test_invalid_command_py_3_10_invalidCommand_.txt +++ b/tests/test_cli/test_invalid_command_py_3_10_invalidCommand_.txt @@ -1,4 +1,5 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] +usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] + [--report] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... cz: error: argument {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version}: invalid choice: 'invalidCommand' (choose from 'init', 'commit', 'c', 'ls', 'example', 'info', 'schema', 'bump', 'changelog', 'ch', 'check', 'version') diff --git a/tests/test_cli/test_invalid_command_py_3_11___invalid_arg_.txt b/tests/test_cli/test_invalid_command_py_3_11___invalid_arg_.txt index 148b4eacdb..33ae4930ed 100644 --- a/tests/test_cli/test_invalid_command_py_3_11___invalid_arg_.txt +++ b/tests/test_cli/test_invalid_command_py_3_11___invalid_arg_.txt @@ -1,4 +1,5 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] +usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] + [--report] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... cz: error: the following arguments are required: {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} diff --git a/tests/test_cli/test_invalid_command_py_3_11_invalidCommand_.txt b/tests/test_cli/test_invalid_command_py_3_11_invalidCommand_.txt index e2d4416b81..ac64f94f97 100644 --- a/tests/test_cli/test_invalid_command_py_3_11_invalidCommand_.txt +++ b/tests/test_cli/test_invalid_command_py_3_11_invalidCommand_.txt @@ -1,4 +1,5 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] +usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] + [--report] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... cz: error: argument {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version}: invalid choice: 'invalidCommand' (choose from 'init', 'commit', 'c', 'ls', 'example', 'info', 'schema', 'bump', 'changelog', 'ch', 'check', 'version') diff --git a/tests/test_cli/test_invalid_command_py_3_12___invalid_arg_.txt b/tests/test_cli/test_invalid_command_py_3_12___invalid_arg_.txt index 148b4eacdb..33ae4930ed 100644 --- a/tests/test_cli/test_invalid_command_py_3_12___invalid_arg_.txt +++ b/tests/test_cli/test_invalid_command_py_3_12___invalid_arg_.txt @@ -1,4 +1,5 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] +usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] + [--report] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... cz: error: the following arguments are required: {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} diff --git a/tests/test_cli/test_invalid_command_py_3_12_invalidCommand_.txt b/tests/test_cli/test_invalid_command_py_3_12_invalidCommand_.txt index c92220c4dc..e3b00a23b2 100644 --- a/tests/test_cli/test_invalid_command_py_3_12_invalidCommand_.txt +++ b/tests/test_cli/test_invalid_command_py_3_12_invalidCommand_.txt @@ -1,4 +1,5 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] +usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] + [--report] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... cz: error: argument {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version}: invalid choice: 'invalidCommand' (choose from init, commit, c, ls, example, info, schema, bump, changelog, ch, check, version) diff --git a/tests/test_cli/test_invalid_command_py_3_13___invalid_arg_.txt b/tests/test_cli/test_invalid_command_py_3_13___invalid_arg_.txt index 4f0ba2b148..04a06a96a7 100644 --- a/tests/test_cli/test_invalid_command_py_3_13___invalid_arg_.txt +++ b/tests/test_cli/test_invalid_command_py_3_13___invalid_arg_.txt @@ -1,3 +1,4 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] +usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] + [--report] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... cz: error: the following arguments are required: {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} diff --git a/tests/test_cli/test_invalid_command_py_3_13_invalidCommand_.txt b/tests/test_cli/test_invalid_command_py_3_13_invalidCommand_.txt index 749066c556..fa8e3d893f 100644 --- a/tests/test_cli/test_invalid_command_py_3_13_invalidCommand_.txt +++ b/tests/test_cli/test_invalid_command_py_3_13_invalidCommand_.txt @@ -1,3 +1,4 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] +usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] + [--report] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... cz: error: argument {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version}: invalid choice: 'invalidCommand' (choose from init, commit, c, ls, example, info, schema, bump, changelog, ch, check, version) diff --git a/tests/test_cli/test_invalid_command_py_3_14___invalid_arg_.txt b/tests/test_cli/test_invalid_command_py_3_14___invalid_arg_.txt index 4f0ba2b148..04a06a96a7 100644 --- a/tests/test_cli/test_invalid_command_py_3_14___invalid_arg_.txt +++ b/tests/test_cli/test_invalid_command_py_3_14___invalid_arg_.txt @@ -1,3 +1,4 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] +usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] + [--report] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... cz: error: the following arguments are required: {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} diff --git a/tests/test_cli/test_invalid_command_py_3_14_invalidCommand_.txt b/tests/test_cli/test_invalid_command_py_3_14_invalidCommand_.txt index 749066c556..fa8e3d893f 100644 --- a/tests/test_cli/test_invalid_command_py_3_14_invalidCommand_.txt +++ b/tests/test_cli/test_invalid_command_py_3_14_invalidCommand_.txt @@ -1,3 +1,4 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] +usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] + [--report] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... cz: error: argument {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version}: invalid choice: 'invalidCommand' (choose from init, commit, c, ls, example, info, schema, bump, changelog, ch, check, version) diff --git a/tests/test_cli/test_no_argv_py_3_10_.txt b/tests/test_cli/test_no_argv_py_3_10_.txt index 69f410e96d..aba56be445 100644 --- a/tests/test_cli/test_no_argv_py_3_10_.txt +++ b/tests/test_cli/test_no_argv_py_3_10_.txt @@ -1,4 +1,5 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] +usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] + [--report] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... @@ -16,6 +17,8 @@ options: e.g., cz -nr 1,2,3 bump. See codes at https://commitizen- tools.github.io/commitizen/exit_codes/ + -v, --version Show the version of the installed commitizen + --report Show system information for reporting bugs commands: {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} diff --git a/tests/test_cli/test_no_argv_py_3_11_.txt b/tests/test_cli/test_no_argv_py_3_11_.txt index 69f410e96d..aba56be445 100644 --- a/tests/test_cli/test_no_argv_py_3_11_.txt +++ b/tests/test_cli/test_no_argv_py_3_11_.txt @@ -1,4 +1,5 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] +usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] + [--report] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... @@ -16,6 +17,8 @@ options: e.g., cz -nr 1,2,3 bump. See codes at https://commitizen- tools.github.io/commitizen/exit_codes/ + -v, --version Show the version of the installed commitizen + --report Show system information for reporting bugs commands: {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} diff --git a/tests/test_cli/test_no_argv_py_3_12_.txt b/tests/test_cli/test_no_argv_py_3_12_.txt index 69f410e96d..aba56be445 100644 --- a/tests/test_cli/test_no_argv_py_3_12_.txt +++ b/tests/test_cli/test_no_argv_py_3_12_.txt @@ -1,4 +1,5 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] +usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] + [--report] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... @@ -16,6 +17,8 @@ options: e.g., cz -nr 1,2,3 bump. See codes at https://commitizen- tools.github.io/commitizen/exit_codes/ + -v, --version Show the version of the installed commitizen + --report Show system information for reporting bugs commands: {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} diff --git a/tests/test_cli/test_no_argv_py_3_13_.txt b/tests/test_cli/test_no_argv_py_3_13_.txt index b47528ec3e..b90fc6834b 100644 --- a/tests/test_cli/test_no_argv_py_3_13_.txt +++ b/tests/test_cli/test_no_argv_py_3_13_.txt @@ -1,4 +1,5 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] +usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] + [--report] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... Commitizen is a powerful release management tool that helps teams maintain consistent and meaningful commit messages while automating version management. @@ -15,6 +16,8 @@ options: e.g., cz -nr 1,2,3 bump. See codes at https://commitizen- tools.github.io/commitizen/exit_codes/ + -v, --version Show the version of the installed commitizen + --report Show system information for reporting bugs commands: {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} diff --git a/tests/test_cli/test_no_argv_py_3_14_.txt b/tests/test_cli/test_no_argv_py_3_14_.txt index b47528ec3e..b90fc6834b 100644 --- a/tests/test_cli/test_no_argv_py_3_14_.txt +++ b/tests/test_cli/test_no_argv_py_3_14_.txt @@ -1,4 +1,5 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] +usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] + [--report] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... Commitizen is a powerful release management tool that helps teams maintain consistent and meaningful commit messages while automating version management. @@ -15,6 +16,8 @@ options: e.g., cz -nr 1,2,3 bump. See codes at https://commitizen- tools.github.io/commitizen/exit_codes/ + -v, --version Show the version of the installed commitizen + --report Show system information for reporting bugs commands: {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} From 0d600844cfe539a7b724af22855c2815262de0dc Mon Sep 17 00:00:00 2001 From: Tim Hsiung Date: Mon, 4 May 2026 11:56:41 +0800 Subject: [PATCH 2/6] fix(cli): use argparse for top-level --version/-v and --report flags Replace raw sys.argv checks with proper argparse-based handling by making subcommands optional. This fixes three issues: - cz version --report was intercepted by the top-level handler instead of routing to the Version command - cz -v did not work despite being registered as a shorthand for --version - cz version --report and cz version --commitizen now emit deprecation warnings directing users to use cz --report and cz --version instead Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- commitizen/cli.py | 25 +++++++++---------- commitizen/commands/version.py | 13 ++++++++++ tests/commands/test_version_command.py | 20 +++++++++++++++ tests/test_cli.py | 8 ++++++ ...invalid_command_py_3_10___invalid_arg_.txt | 5 ---- ...invalid_command_py_3_11___invalid_arg_.txt | 5 ---- ...invalid_command_py_3_12___invalid_arg_.txt | 5 ---- ...invalid_command_py_3_13___invalid_arg_.txt | 4 --- ...invalid_command_py_3_14___invalid_arg_.txt | 4 --- 9 files changed, 53 insertions(+), 36 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index 049e37d192..fcd5c66871 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -119,7 +119,7 @@ def __call__( ], "subcommands": { "title": "commands", - "required": True, + "required": False, "commands": [ { "name": ["init"], @@ -689,18 +689,6 @@ def main() -> None: parser.print_help(sys.stderr) raise ExpectedExit() - # TODO(bearomorphism): mark `cz version --commitizen` as deprecated after `cz version` feature is stable - if "--version" in sys.argv: - out.write(__version__) - raise ExpectedExit() - - # TODO(bearomorphism): mark `cz version --report` as deprecated after `cz version` feature is stable - if "--report" in sys.argv: - out.write(f"Commitizen Version: {__version__}") - out.write(f"Python Version: {sys.version}") - out.write(f"Operating System: {platform.system()}") - raise ExpectedExit() - # This is for the command required constraint in 2.0 try: args, unknown_args = parser.parse_known_args() @@ -709,6 +697,17 @@ def main() -> None: raise NoCommandFoundError() raise e + if not hasattr(args, "func"): + if getattr(args, "version", False): + out.write(__version__) + raise ExpectedExit() + if getattr(args, "report", False): + out.write(f"Commitizen Version: {__version__}") + out.write(f"Python Version: {sys.version}") + out.write(f"Operating System: {platform.system()}") + raise ExpectedExit() + raise NoCommandFoundError() + arguments = vars(args) if unknown_args: # Raise error for extra-args without -- separation diff --git a/commitizen/commands/version.py b/commitizen/commands/version.py index 2a4bcea88a..2376589d48 100644 --- a/commitizen/commands/version.py +++ b/commitizen/commands/version.py @@ -1,5 +1,6 @@ import platform import sys +import warnings from typing import TypedDict from packaging.version import InvalidVersion @@ -45,6 +46,12 @@ def __init__(self, config: BaseConfig, arguments: VersionArgs) -> None: def __call__(self) -> None: if self.arguments.get("report"): + warnings.warn( + "`cz version --report` is deprecated and will be removed in v5. " + "Use `cz --report` instead.", + DeprecationWarning, + stacklevel=2, + ) out.write(f"Commitizen Version: {__version__}") out.write(f"Python Version: {sys.version}") out.write(f"Operating System: {platform.system()}") @@ -54,6 +61,12 @@ def __call__(self) -> None: out.write(f"Installed Commitizen Version: {__version__}") if self.arguments.get("commitizen"): + warnings.warn( + "`cz version --commitizen` is deprecated and will be removed in v5. " + "Use `cz --version` instead.", + DeprecationWarning, + stacklevel=2, + ) out.write(__version__) return diff --git a/tests/commands/test_version_command.py b/tests/commands/test_version_command.py index 099e7110e7..bbca193c70 100644 --- a/tests/commands/test_version_command.py +++ b/tests/commands/test_version_command.py @@ -296,3 +296,23 @@ def test_version_no_arguments_shows_commitizen_version(config, capsys): commands.Version(config, {})() captured = capsys.readouterr() assert captured.out.strip() == __version__ + + +def test_version_report_emits_deprecation_warning(config, capsys): + with pytest.warns( + DeprecationWarning, + match=r"`cz version --report` is deprecated.*Use `cz --report` instead", + ): + commands.Version(config, {"report": True})() + captured = capsys.readouterr() + assert f"Commitizen Version: {__version__}" in captured.out + + +def test_version_commitizen_emits_deprecation_warning(config, capsys): + with pytest.warns( + DeprecationWarning, + match=r"`cz version --commitizen` is deprecated.*Use `cz --version` instead", + ): + commands.Version(config, {"commitizen": True})() + captured = capsys.readouterr() + assert __version__ in captured.out diff --git a/tests/test_cli.py b/tests/test_cli.py index 15e5bcb55c..878f6783ab 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -64,6 +64,14 @@ def test_cz_with_version_arg(util: UtilFixture, capsys): assert __version__ in out +def test_cz_with_version_short_arg(util: UtilFixture, capsys): + """Test that cz shows the version when -v is used.""" + with pytest.raises(ExpectedExit): + util.run_cli("-v") + out, _ = capsys.readouterr() + assert __version__ in out + + def test_cz_with_report_arg(util: UtilFixture, capsys): """Test that cz shows the report when --report is used.""" with pytest.raises(ExpectedExit): diff --git a/tests/test_cli/test_invalid_command_py_3_10___invalid_arg_.txt b/tests/test_cli/test_invalid_command_py_3_10___invalid_arg_.txt index 33ae4930ed..e69de29bb2 100644 --- a/tests/test_cli/test_invalid_command_py_3_10___invalid_arg_.txt +++ b/tests/test_cli/test_invalid_command_py_3_10___invalid_arg_.txt @@ -1,5 +0,0 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] - [--report] - {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} - ... -cz: error: the following arguments are required: {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} diff --git a/tests/test_cli/test_invalid_command_py_3_11___invalid_arg_.txt b/tests/test_cli/test_invalid_command_py_3_11___invalid_arg_.txt index 33ae4930ed..e69de29bb2 100644 --- a/tests/test_cli/test_invalid_command_py_3_11___invalid_arg_.txt +++ b/tests/test_cli/test_invalid_command_py_3_11___invalid_arg_.txt @@ -1,5 +0,0 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] - [--report] - {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} - ... -cz: error: the following arguments are required: {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} diff --git a/tests/test_cli/test_invalid_command_py_3_12___invalid_arg_.txt b/tests/test_cli/test_invalid_command_py_3_12___invalid_arg_.txt index 33ae4930ed..e69de29bb2 100644 --- a/tests/test_cli/test_invalid_command_py_3_12___invalid_arg_.txt +++ b/tests/test_cli/test_invalid_command_py_3_12___invalid_arg_.txt @@ -1,5 +0,0 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] - [--report] - {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} - ... -cz: error: the following arguments are required: {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} diff --git a/tests/test_cli/test_invalid_command_py_3_13___invalid_arg_.txt b/tests/test_cli/test_invalid_command_py_3_13___invalid_arg_.txt index 04a06a96a7..e69de29bb2 100644 --- a/tests/test_cli/test_invalid_command_py_3_13___invalid_arg_.txt +++ b/tests/test_cli/test_invalid_command_py_3_13___invalid_arg_.txt @@ -1,4 +0,0 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] - [--report] - {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... -cz: error: the following arguments are required: {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} diff --git a/tests/test_cli/test_invalid_command_py_3_14___invalid_arg_.txt b/tests/test_cli/test_invalid_command_py_3_14___invalid_arg_.txt index 04a06a96a7..e69de29bb2 100644 --- a/tests/test_cli/test_invalid_command_py_3_14___invalid_arg_.txt +++ b/tests/test_cli/test_invalid_command_py_3_14___invalid_arg_.txt @@ -1,4 +0,0 @@ -usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] [-v] - [--report] - {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... -cz: error: the following arguments are required: {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} From 1193e8b04cebffd98f0c586a476d4df0fa0b49ce Mon Sep 17 00:00:00 2001 From: Tim Hsiung Date: Mon, 4 May 2026 12:05:10 +0800 Subject: [PATCH 3/6] fix(cli): make --version and --report take precedence over subcommands Top-level meta flags like --version and --report now take priority even when a subcommand is also provided (e.g., cz --version bump shows the version instead of running bump). The --report flag correctly defers to the version subcommand when used as cz version --report. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- commitizen/cli.py | 20 ++++++++++++-------- tests/test_cli.py | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index fcd5c66871..b05931fc61 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -697,15 +697,19 @@ def main() -> None: raise NoCommandFoundError() raise e + if getattr(args, "version", False): + out.write(__version__) + raise ExpectedExit() + + if getattr(args, "report", False) and ( + not hasattr(args, "func") or args.func is not commands.Version + ): + out.write(f"Commitizen Version: {__version__}") + out.write(f"Python Version: {sys.version}") + out.write(f"Operating System: {platform.system()}") + raise ExpectedExit() + if not hasattr(args, "func"): - if getattr(args, "version", False): - out.write(__version__) - raise ExpectedExit() - if getattr(args, "report", False): - out.write(f"Commitizen Version: {__version__}") - out.write(f"Python Version: {sys.version}") - out.write(f"Operating System: {platform.system()}") - raise ExpectedExit() raise NoCommandFoundError() arguments = vars(args) diff --git a/tests/test_cli.py b/tests/test_cli.py index 878f6783ab..1d08df2a2d 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -72,6 +72,14 @@ def test_cz_with_version_short_arg(util: UtilFixture, capsys): assert __version__ in out +def test_cz_version_flag_takes_precedence_over_subcommand(util: UtilFixture, capsys): + """Test that --version takes precedence even when a subcommand is given.""" + with pytest.raises(ExpectedExit): + util.run_cli("--version", "bump") + out, _ = capsys.readouterr() + assert __version__ in out + + def test_cz_with_report_arg(util: UtilFixture, capsys): """Test that cz shows the report when --report is used.""" with pytest.raises(ExpectedExit): @@ -82,6 +90,14 @@ def test_cz_with_report_arg(util: UtilFixture, capsys): assert "Operating System:" in out +def test_cz_report_flag_takes_precedence_over_subcommand(util: UtilFixture, capsys): + """Test that --report takes precedence over non-version subcommands.""" + with pytest.raises(ExpectedExit): + util.run_cli("--report", "bump") + out, _ = capsys.readouterr() + assert "Commitizen Version:" in out + + def test_name(util: UtilFixture, capsys): util.run_cli("-n", "cz_jira", "example") out, _ = capsys.readouterr() From 9cab29370dd71cb87ad29cb3f7568f5e613f26be Mon Sep 17 00:00:00 2001 From: Tim Hsiung Date: Wed, 6 May 2026 12:22:48 +0800 Subject: [PATCH 4/6] fix(cli): use distinct dest for top-level --report to avoid subcommand conflict Use dest='global_report' for the top-level --report flag so it does not share the same attribute as the version subcommand's --report. This ensures 'cz --report version' behaves as a top-level report instead of routing through the version subcommand with a deprecation warning. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- commitizen/cli.py | 6 +++--- tests/test_cli.py | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index b05931fc61..7f6ba25d2e 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -114,6 +114,7 @@ def __call__( { "name": ["--report"], "action": "store_true", + "dest": "global_report", "help": "Show system information for reporting bugs", }, ], @@ -662,6 +663,7 @@ class Args(argparse.Namespace): name: str | None = None no_raise: str | None = None # comma-separated string, later parsed as list[int] report: bool = False + global_report: bool = False project: bool = False commitizen: bool = False verbose: bool = False @@ -701,9 +703,7 @@ def main() -> None: out.write(__version__) raise ExpectedExit() - if getattr(args, "report", False) and ( - not hasattr(args, "func") or args.func is not commands.Version - ): + if getattr(args, "global_report", False): out.write(f"Commitizen Version: {__version__}") out.write(f"Python Version: {sys.version}") out.write(f"Operating System: {platform.system()}") diff --git a/tests/test_cli.py b/tests/test_cli.py index 1d08df2a2d..cdcb9434dd 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -98,6 +98,16 @@ def test_cz_report_flag_takes_precedence_over_subcommand(util: UtilFixture, caps assert "Commitizen Version:" in out +def test_cz_report_flag_takes_precedence_over_version_subcommand( + util: UtilFixture, capsys +): + """Test that top-level --report takes precedence over version subcommand.""" + with pytest.raises(ExpectedExit): + util.run_cli("--report", "version") + out, _ = capsys.readouterr() + assert "Commitizen Version:" in out + + def test_name(util: UtilFixture, capsys): util.run_cli("-n", "cz_jira", "example") out, _ = capsys.readouterr() From 12584e3178f7816bc2b441150fe33369cf00f08f Mon Sep 17 00:00:00 2001 From: Tim Hsiung Date: Wed, 6 May 2026 12:29:30 +0800 Subject: [PATCH 5/6] refactor(cli): simplify --report handling and update docs Revert the global_report dest approach. Keep --report as a simple top-level flag that works only without a subcommand, matching the original intent. Update version.md and README.md to document the new cz --version/-v and cz --report top-level flags. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- commitizen/cli.py | 13 +++++-------- docs/README.md | 2 ++ docs/commands/version.md | 9 +++++++++ tests/test_cli.py | 18 ------------------ 4 files changed, 16 insertions(+), 26 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index 7f6ba25d2e..e34291f891 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -114,7 +114,6 @@ def __call__( { "name": ["--report"], "action": "store_true", - "dest": "global_report", "help": "Show system information for reporting bugs", }, ], @@ -663,7 +662,6 @@ class Args(argparse.Namespace): name: str | None = None no_raise: str | None = None # comma-separated string, later parsed as list[int] report: bool = False - global_report: bool = False project: bool = False commitizen: bool = False verbose: bool = False @@ -703,13 +701,12 @@ def main() -> None: out.write(__version__) raise ExpectedExit() - if getattr(args, "global_report", False): - out.write(f"Commitizen Version: {__version__}") - out.write(f"Python Version: {sys.version}") - out.write(f"Operating System: {platform.system()}") - raise ExpectedExit() - if not hasattr(args, "func"): + if getattr(args, "report", False): + out.write(f"Commitizen Version: {__version__}") + out.write(f"Python Version: {sys.version}") + out.write(f"Operating System: {platform.system()}") + raise ExpectedExit() raise NoCommandFoundError() arguments = vars(args) diff --git a/docs/README.md b/docs/README.md index b4b97884ab..865fe6e8d3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -227,6 +227,8 @@ Commitizen provides a comprehensive CLI with various commands. Here's the comple | Command | Description | Alias | |---------|-------------|-------| +| `cz --version` | Show installed Commitizen version | `cz -v` | +| `cz --report` | Show system information for bug reports | - | | `cz init` | Initialize Commitizen configuration | - | | `cz commit` | Create a new commit | `cz c` | | `cz bump` | Bump version and update changelog | - | diff --git a/docs/commands/version.md b/docs/commands/version.md index 198df5b480..ee68a08622 100644 --- a/docs/commands/version.md +++ b/docs/commands/version.md @@ -1,5 +1,12 @@ Get the version of the installed Commitizen or the current project (default: installed commitizen). +## Quick version and report + +The following top-level flags are available for convenience: + +- **`cz --version`** (or **`cz -v`**): print the installed Commitizen version and exit. +- **`cz --report`**: print system information (Commitizen version, Python version, operating system) useful for bug reports, and exit. + ## Usage ![cz version --help](../images/cli_help/cz_version___help.svg) @@ -20,6 +27,8 @@ Get the version of the installed Commitizen or the current project (default: ins ## Examples ```bash +cz --version # quick: installed commitizen version +cz --report # quick: system info for bug reports cz version --project cz version 2.0.0 --next MAJOR cz version --project --major diff --git a/tests/test_cli.py b/tests/test_cli.py index cdcb9434dd..93af56861c 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -90,24 +90,6 @@ def test_cz_with_report_arg(util: UtilFixture, capsys): assert "Operating System:" in out -def test_cz_report_flag_takes_precedence_over_subcommand(util: UtilFixture, capsys): - """Test that --report takes precedence over non-version subcommands.""" - with pytest.raises(ExpectedExit): - util.run_cli("--report", "bump") - out, _ = capsys.readouterr() - assert "Commitizen Version:" in out - - -def test_cz_report_flag_takes_precedence_over_version_subcommand( - util: UtilFixture, capsys -): - """Test that top-level --report takes precedence over version subcommand.""" - with pytest.raises(ExpectedExit): - util.run_cli("--report", "version") - out, _ = capsys.readouterr() - assert "Commitizen Version:" in out - - def test_name(util: UtilFixture, capsys): util.run_cli("-n", "cz_jira", "example") out, _ = capsys.readouterr() From 549fbcfe44d6af4627abdd2c2e69ac9545d82fdb Mon Sep 17 00:00:00 2001 From: Tim Hsiung Date: Wed, 6 May 2026 14:15:34 +0800 Subject: [PATCH 6/6] test: deduplicate version and report tests Consolidate three separate version flag tests into a single parametrized test. Merge standalone deprecation warning tests into the existing test_version_for_showing_commitizen_version and test_version_for_showing_commitizen_system_info tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/commands/test_version_command.py | 44 ++++++++++---------------- tests/test_cli.py | 31 +++++++----------- 2 files changed, 28 insertions(+), 47 deletions(-) diff --git a/tests/commands/test_version_command.py b/tests/commands/test_version_command.py index bbca193c70..b423f13f99 100644 --- a/tests/commands/test_version_command.py +++ b/tests/commands/test_version_command.py @@ -31,10 +31,14 @@ def test_version_for_showing_project_version(config, capsys): @pytest.mark.parametrize("project", [True, False]) def test_version_for_showing_commitizen_version(config, capsys, project: bool): - commands.Version( - config, - {"project": project, "commitizen": True}, - )() + with pytest.warns( + DeprecationWarning, + match=r"`cz version --commitizen` is deprecated.*Use `cz --version` instead", + ): + commands.Version( + config, + {"project": project, "commitizen": True}, + )() captured = capsys.readouterr() assert f"{__version__}" in captured.out @@ -63,10 +67,14 @@ def test_version_for_showing_both_versions(config, capsys): def test_version_for_showing_commitizen_system_info(config, capsys): - commands.Version( - config, - {"report": True}, - )() + with pytest.warns( + DeprecationWarning, + match=r"`cz version --report` is deprecated.*Use `cz --report` instead", + ): + commands.Version( + config, + {"report": True}, + )() captured = capsys.readouterr() assert f"Commitizen Version: {__version__}" in captured.out assert f"Python Version: {sys.version}" in captured.out @@ -296,23 +304,3 @@ def test_version_no_arguments_shows_commitizen_version(config, capsys): commands.Version(config, {})() captured = capsys.readouterr() assert captured.out.strip() == __version__ - - -def test_version_report_emits_deprecation_warning(config, capsys): - with pytest.warns( - DeprecationWarning, - match=r"`cz version --report` is deprecated.*Use `cz --report` instead", - ): - commands.Version(config, {"report": True})() - captured = capsys.readouterr() - assert f"Commitizen Version: {__version__}" in captured.out - - -def test_version_commitizen_emits_deprecation_warning(config, capsys): - with pytest.warns( - DeprecationWarning, - match=r"`cz version --commitizen` is deprecated.*Use `cz --version` instead", - ): - commands.Version(config, {"commitizen": True})() - captured = capsys.readouterr() - assert __version__ in captured.out diff --git a/tests/test_cli.py b/tests/test_cli.py index 93af56861c..c0a8fc2450 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -56,26 +56,19 @@ def test_cz_with_arg_but_without_command(util: UtilFixture): assert "Command is required" in str(excinfo.value) -def test_cz_with_version_arg(util: UtilFixture, capsys): - """Test that cz shows the version when --version is used.""" - with pytest.raises(ExpectedExit): - util.run_cli("--version") - out, _ = capsys.readouterr() - assert __version__ in out - - -def test_cz_with_version_short_arg(util: UtilFixture, capsys): - """Test that cz shows the version when -v is used.""" - with pytest.raises(ExpectedExit): - util.run_cli("-v") - out, _ = capsys.readouterr() - assert __version__ in out - - -def test_cz_version_flag_takes_precedence_over_subcommand(util: UtilFixture, capsys): - """Test that --version takes precedence even when a subcommand is given.""" +@pytest.mark.parametrize( + "args", + [ + ("--version",), + ("-v",), + ("--version", "bump"), + ], + ids=["long-flag", "short-flag", "precedence-over-subcommand"], +) +def test_cz_with_version_arg(util: UtilFixture, capsys, args): + """Test that cz --version / -v shows the version and takes precedence.""" with pytest.raises(ExpectedExit): - util.run_cli("--version", "bump") + util.run_cli(*args) out, _ = capsys.readouterr() assert __version__ in out