From 5a2b5e5c6c2eee858255600bc0e5a32e8d150a89 Mon Sep 17 00:00:00 2001 From: shenxianpeng Date: Mon, 7 Jul 2025 12:30:44 +0300 Subject: [PATCH 1/6] feat: support codspeed (#245) * feat: support codspeed * Potential fix for code scanning alert no. 46: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/codspeed.yml | 44 ++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- tests/author_test.py | 12 ++++++++++ tests/branch_test.py | 11 ++++++++- tests/commit_test.py | 12 ++++++++++ tests/error_test.py | 5 ++++ tests/main_test.py | 5 ++++ tests/util_test.py | 17 +++++++++++++ 8 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/codspeed.yml diff --git a/.github/workflows/codspeed.yml b/.github/workflows/codspeed.yml new file mode 100644 index 0000000..f4bbb59 --- /dev/null +++ b/.github/workflows/codspeed.yml @@ -0,0 +1,44 @@ +name: CodSpeed + +permissions: + contents: read + +on: + push: + branches: + - "main" + paths: + - "commit_check/**" + - "tests/**" + - ".github/workflows/codspeed.yml" + - "pyproject.toml" + pull_request: + branches: + - "main" + paths: + - "commit_check/**" + - "tests/**" + - ".github/workflows/codspeed.yml" + - "pyproject.toml" + # `workflow_dispatch` allows CodSpeed to trigger backtest + # performance analysis in order to generate initial data. + workflow_dispatch: + +jobs: + benchmarks: + name: Run benchmarks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install dependencies + run: pip install -e .[test] + + - name: Run benchmarks + uses: CodSpeedHQ/action@v3 + with: + token: ${{ secrets.CODSPEED_TOKEN }} + run: pytest tests/ --codspeed diff --git a/pyproject.toml b/pyproject.toml index 3f0759e..8c0db4c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ tracker = "https://github.com/commit-check/commit-check/issues" [project.optional-dependencies] dev = ['nox'] -test = ['coverage', 'pytest', 'pytest-mock'] +test = ['coverage', 'pytest', 'pytest-mock', 'pytest-codspeed'] docs = ['sphinx-immaterial', 'sphinx-autobuild'] [tool.setuptools] diff --git a/tests/author_test.py b/tests/author_test.py index 8936223..fcc4939 100644 --- a/tests/author_test.py +++ b/tests/author_test.py @@ -1,3 +1,4 @@ +import pytest from commit_check import PASS, FAIL from commit_check.author import check_author @@ -11,6 +12,7 @@ class TestAuthorName: fake_author_value_an = "fake_author_name" fake_accented_author_value_an = "fáké_áúthór_námé" + @pytest.mark.benchmark def test_check_author(self, mocker): # Must call get_commit_info, re.match. checks = [{ @@ -30,6 +32,7 @@ def test_check_author(self, mocker): assert m_get_commit_info.call_count == 1 assert m_re_match.call_count == 1 + @pytest.mark.benchmark def test_check_author_with_accented_letters(self, mocker): # Must call get_commit_info, re.match. checks = [{ @@ -49,6 +52,7 @@ def test_check_author_with_accented_letters(self, mocker): assert m_get_commit_info.call_count == 1 assert m_re_match.call_count == 1 + @pytest.mark.benchmark def test_check_author_with_empty_checks(self, mocker): # Must NOT call get_commit_info, re.match. with `checks` param with length 0. checks = [] @@ -65,6 +69,7 @@ def test_check_author_with_empty_checks(self, mocker): assert m_get_commit_info.call_count == 0 assert m_re_match.call_count == 0 + @pytest.mark.benchmark def test_check_author_with_different_check(self, mocker): # Must NOT call get_commit_info, re.match with not `author_name`. checks = [{ @@ -84,6 +89,7 @@ def test_check_author_with_different_check(self, mocker): assert m_get_commit_info.call_count == 0 assert m_re_match.call_count == 0 + @pytest.mark.benchmark def test_check_author_with_len0_regex(self, mocker, capfd): # Must NOT call get_commit_info, re.match with `regex` with length 0. checks = [ @@ -107,6 +113,7 @@ def test_check_author_with_len0_regex(self, mocker, capfd): out, _ = capfd.readouterr() assert "Not found regex for author_name." in out + @pytest.mark.benchmark def test_check_author_with_result_none(self, mocker): # Must call print_error_message, print_suggestion when re.match returns NONE. checks = [{ @@ -140,6 +147,7 @@ class TestAuthorEmail: # used by get_commit_info mock fake_author_value_ae = "fake_author_email" + @pytest.mark.benchmark def test_check_author(self, mocker): # Must call get_commit_info, re.match. checks = [{ @@ -159,6 +167,7 @@ def test_check_author(self, mocker): assert m_get_commit_info.call_count == 1 assert m_re_match.call_count == 1 + @pytest.mark.benchmark def test_check_author_with_empty_checks(self, mocker): # Must NOT call get_commit_info, re.match. with `checks` param with length 0. checks = [] @@ -175,6 +184,7 @@ def test_check_author_with_empty_checks(self, mocker): assert m_get_commit_info.call_count == 0 assert m_re_match.call_count == 0 + @pytest.mark.benchmark def test_check_author_with_different_check(self, mocker): # Must NOT call get_commit_info, re.match with not `author_email`. checks = [{ @@ -194,6 +204,7 @@ def test_check_author_with_different_check(self, mocker): assert m_get_commit_info.call_count == 0 assert m_re_match.call_count == 0 + @pytest.mark.benchmark def test_check_author_with_len0_regex(self, mocker, capfd): # Must NOT call get_commit_info, re.match with `regex` with length 0. checks = [ @@ -217,6 +228,7 @@ def test_check_author_with_len0_regex(self, mocker, capfd): out, _ = capfd.readouterr() assert "Not found regex for author_email." in out + @pytest.mark.benchmark def test_check_author_with_result_none(self, mocker): # Must call print_error_message, print_suggestion when re.match returns NONE. checks = [{ diff --git a/tests/branch_test.py b/tests/branch_test.py index 083c5bd..34e3a3b 100644 --- a/tests/branch_test.py +++ b/tests/branch_test.py @@ -1,3 +1,4 @@ +import pytest from commit_check import PASS, FAIL from commit_check.branch import check_branch, check_merge_base @@ -7,6 +8,7 @@ class TestCheckBranch: + @pytest.mark.benchmark def test_check_branch(self, mocker): # Must call get_branch_name, re.match at once. checks = [{ @@ -26,6 +28,7 @@ def test_check_branch(self, mocker): assert m_get_branch_name.call_count == 1 assert m_re_match.call_count == 1 + @pytest.mark.benchmark def test_check_branch_with_empty_checks(self, mocker): # Must NOT call get_branch_name, re.match with `checks` param with length 0. checks = [] @@ -42,6 +45,7 @@ def test_check_branch_with_empty_checks(self, mocker): assert m_get_branch_name.call_count == 0 assert m_re_match.call_count == 0 + @pytest.mark.benchmark def test_check_branch_with_different_check(self, mocker): # Must NOT call get_branch_name, re.match with not `branch`. checks = [{ @@ -61,6 +65,7 @@ def test_check_branch_with_different_check(self, mocker): assert m_get_branch_name.call_count == 0 assert m_re_match.call_count == 0 + @pytest.mark.benchmark def test_check_branch_with_len0_regex(self, mocker, capfd): # Must NOT call get_branch_name, re.match with `regex` with length 0. checks = [ @@ -84,6 +89,7 @@ def test_check_branch_with_len0_regex(self, mocker, capfd): out, _ = capfd.readouterr() assert "Not found regex for branch naming." in out + @pytest.mark.benchmark def test_check_branch_with_result_none(self, mocker): # Must call print_error_message, print_suggestion when re.match returns NONE. checks = [{ @@ -115,6 +121,7 @@ def test_check_branch_with_result_none(self, mocker): class TestCheckMergeBase: + @pytest.mark.benchmark def test_check_merge_base_with_empty_checks(self, mocker): checks = [] m_check_merge = mocker.patch(f"{LOCATION}.check_merge_base") @@ -122,7 +129,7 @@ def test_check_merge_base_with_empty_checks(self, mocker): assert retval == PASS assert m_check_merge.call_count == 0 - + @pytest.mark.benchmark def test_check_merge_base_with_empty_regex(self, mocker): checks = [{ "check": "merge_base", @@ -133,6 +140,7 @@ def test_check_merge_base_with_empty_regex(self, mocker): assert retval == PASS assert m_check_merge.call_count == 0 + @pytest.mark.benchmark def test_check_merge_base_with_different_check(self, mocker): checks = [{ "check": "branch", @@ -143,6 +151,7 @@ def test_check_merge_base_with_different_check(self, mocker): assert retval == PASS assert m_check_merge.call_count == 0 + @pytest.mark.benchmark def test_check_merge_base_fail_with_messages(self, mocker, capfd): checks = [{ "check": "merge_base", diff --git a/tests/commit_test.py b/tests/commit_test.py index a458636..c733968 100644 --- a/tests/commit_test.py +++ b/tests/commit_test.py @@ -1,3 +1,4 @@ +import pytest from commit_check import PASS, FAIL from commit_check.commit import check_commit_msg, get_default_commit_msg_file, read_commit_msg, check_commit_signoff @@ -9,11 +10,13 @@ MSG_FILE = '.git/COMMIT_EDITMSG' +@pytest.mark.benchmark def test_get_default_commit_msg_file(mocker): retval = get_default_commit_msg_file() assert retval == ".git/COMMIT_EDITMSG" +@pytest.mark.benchmark def test_read_commit_msg_from_existing_file(tmp_path): # Create a temporary file with a known content commit_msg_content = "Test commit message content." @@ -24,12 +27,14 @@ def test_read_commit_msg_from_existing_file(tmp_path): assert result == commit_msg_content +@pytest.mark.benchmark def test_read_commit_msg_file_not_found(mocker): m_commits_info = mocker.patch('commit_check.util.get_commit_info', return_value='mocked_commits_info') read_commit_msg("non_existent_file.txt") assert m_commits_info.call_count == 0 +@pytest.mark.benchmark def test_check_commit_msg_no_commit_msg_file(mocker): mock_get_default_commit_msg_file = mocker.patch( "commit_check.commit.get_default_commit_msg_file", @@ -49,6 +54,7 @@ def test_check_commit_msg_no_commit_msg_file(mocker): assert result == 0 +@pytest.mark.benchmark def test_check_commit_with_empty_checks(mocker): checks = [] m_re_match = mocker.patch( @@ -60,6 +66,7 @@ def test_check_commit_with_empty_checks(mocker): assert m_re_match.call_count == 0 +@pytest.mark.benchmark def test_check_commit_with_different_check(mocker): checks = [{ "check": "branch", @@ -74,6 +81,7 @@ def test_check_commit_with_different_check(mocker): assert m_re_match.call_count == 0 +@pytest.mark.benchmark def test_check_commit_with_len0_regex(mocker, capfd): checks = [ { @@ -92,6 +100,7 @@ def test_check_commit_with_len0_regex(mocker, capfd): assert "Not found regex for commit message." in out +@pytest.mark.benchmark def test_check_commit_with_result_none(mocker): checks = [{ "check": "message", @@ -116,6 +125,7 @@ def test_check_commit_with_result_none(mocker): assert m_print_suggestion.call_count == 1 +@pytest.mark.benchmark def test_check_commit_signoff(mocker): checks = [{ "check": "commit_signoff", @@ -140,6 +150,7 @@ def test_check_commit_signoff(mocker): assert m_print_suggestion.call_count == 1 +@pytest.mark.benchmark def test_check_commit_signoff_with_empty_regex(mocker): checks = [{ "check": "commit_signoff", @@ -156,6 +167,7 @@ def test_check_commit_signoff_with_empty_regex(mocker): assert m_re_match.call_count == 0 +@pytest.mark.benchmark def test_check_commit_signoff_with_empty_checks(mocker): checks = [] m_re_match = mocker.patch( diff --git a/tests/error_test.py b/tests/error_test.py index d0a2880..ab9e948 100644 --- a/tests/error_test.py +++ b/tests/error_test.py @@ -3,6 +3,7 @@ from commit_check.error import error_handler, log_and_exit +@pytest.mark.benchmark def test_error_handler_RuntimeError(): with pytest.raises(SystemExit) as exit_info: with error_handler(): @@ -10,6 +11,7 @@ def test_error_handler_RuntimeError(): assert exit_info.value.code == 1 +@pytest.mark.benchmark def test_error_handler_KeyboardInterrupt(): with pytest.raises(SystemExit) as exit_info: with error_handler(): @@ -17,6 +19,7 @@ def test_error_handler_KeyboardInterrupt(): assert exit_info.value.code == 130 +@pytest.mark.benchmark def test_error_handler_unexpected_error(): with pytest.raises(SystemExit) as exit_info: with error_handler(): @@ -24,6 +27,7 @@ def test_error_handler_unexpected_error(): assert exit_info.value.code == 3 +@pytest.mark.benchmark def test_error_handler_cannot_access(mocker): with pytest.raises(SystemExit): store_dir = "/fake/commit-check" @@ -50,6 +54,7 @@ def test_error_handler_cannot_access(mocker): mock_open().write.assert_any_call(f"Failed to write to log at {log_path}\n") +@pytest.mark.benchmark @pytest.mark.xfail def test_log_and_exit(monkeypatch): monkeypatch.setenv("COMMIT_CHECK_HOME", "") diff --git a/tests/main_test.py b/tests/main_test.py index ca77032..c232efd 100644 --- a/tests/main_test.py +++ b/tests/main_test.py @@ -7,6 +7,7 @@ class TestMain: + @pytest.mark.benchmark @pytest.mark.parametrize("argv, check_commit_call_count, check_branch_call_count, check_author_call_count, check_commit_signoff_call_count, check_merge_base_call_count", [ ([CMD, "--message"], 1, 0, 0, 0, 0), ([CMD, "--branch"], 0, 1, 0, 0, 0), @@ -53,6 +54,7 @@ def test_main( assert m_check_commit_signoff.call_count == check_commit_signoff_call_count assert m_check_merge_base.call_count == check_merge_base_call_count + @pytest.mark.benchmark def test_main_help(self, mocker, capfd): mocker.patch( "commit_check.main.validate_config", @@ -78,6 +80,7 @@ def test_main_help(self, mocker, capfd): stdout, _ = capfd.readouterr() assert "usage: " in stdout + @pytest.mark.benchmark def test_main_version(self, mocker): mocker.patch( "commit_check.main.validate_config", @@ -101,6 +104,7 @@ def test_main_version(self, mocker): assert m_check_commit_signoff.call_count == 0 assert m_check_merge_base.call_count == 0 + @pytest.mark.benchmark def test_main_validate_config_ret_none(self, mocker): mocker.patch( "commit_check.main.validate_config", @@ -116,6 +120,7 @@ def test_main_validate_config_ret_none(self, mocker): assert m_check_commit.call_count == 1 assert m_check_commit.call_args[0][0] == DEFAULT_CONFIG["checks"] + @pytest.mark.benchmark @pytest.mark.parametrize( "argv, message_result, branch_result, author_name_result, author_email_result, commit_signoff_result, merge_base_result, final_result", [ diff --git a/tests/util_test.py b/tests/util_test.py index e42ba24..c426831 100644 --- a/tests/util_test.py +++ b/tests/util_test.py @@ -15,6 +15,7 @@ class TestUtil: class TestGetBranchName: + @pytest.mark.benchmark def test_get_branch_name(self, mocker): # Must call cmd_output with given argument. m_cmd_output = mocker.patch( @@ -28,6 +29,7 @@ def test_get_branch_name(self, mocker): ] assert retval == "fake_branch_name" + @pytest.mark.benchmark def test_get_branch_name_with_exception(self, mocker): # Must return empty string when exception raises in cmd_output. m_cmd_output = mocker.patch( @@ -48,6 +50,7 @@ def test_get_branch_name_with_exception(self, mocker): assert retval == "" class TestHasCommits: + @pytest.mark.benchmark def test_has_commits_true(self, mocker): # Must return True when git rev-parse HEAD succeeds m_subprocess_run = mocker.patch( @@ -66,6 +69,7 @@ def test_has_commits_true(self, mocker): } assert retval is True + @pytest.mark.benchmark def test_has_commits_false(self, mocker): # Must return False when git rev-parse HEAD fails m_subprocess_run = mocker.patch( @@ -85,6 +89,7 @@ def test_has_commits_false(self, mocker): assert retval is False class TestGitMergeBase: + @pytest.mark.benchmark @pytest.mark.parametrize("returncode,expected", [ (0, 0), # ancestor exists (1, 1), # no ancestor @@ -109,6 +114,7 @@ def test_git_merge_base(self, mocker, returncode, expected): assert result == expected class TestGetCommitInfo: + @pytest.mark.benchmark @pytest.mark.parametrize("format_string", [ ("s"), ("an"), @@ -133,6 +139,7 @@ def test_get_commit_info(self, mocker, format_string): ] assert retval == " fake commit message " + @pytest.mark.benchmark def test_get_commit_info_no_commits(self, mocker): # Must return 'Repo has no commits yet.' when there are no commits. m_has_commits = mocker.patch( @@ -149,6 +156,7 @@ def test_get_commit_info_no_commits(self, mocker): assert m_cmd_output.call_count == 0 # Should not call cmd_output assert retval == "Repo has no commits yet." + @pytest.mark.benchmark def test_get_commit_info_with_exception(self, mocker): # Must return empty string when exception raises in cmd_output. m_has_commits = mocker.patch( @@ -182,6 +190,7 @@ def __init__(self, returncode, stdout, stderr): self.stdout = stdout self.stderr = stderr + @pytest.mark.benchmark def test_cmd_output(self, mocker): # Must subprocess.run with given argument. m_subprocess_run = mocker.patch( @@ -192,6 +201,7 @@ def test_cmd_output(self, mocker): assert m_subprocess_run.call_count == 1 assert retval == "ok" + @pytest.mark.benchmark @pytest.mark.parametrize("returncode, stdout, stderr", [ (1, "ok", "err"), (0, None, "err"), @@ -216,6 +226,7 @@ def test_cmd_output_err(self, mocker, returncode, stdout, stderr): "stdout": PIPE } + @pytest.mark.benchmark @pytest.mark.parametrize("returncode, stdout, stderr", [ (1, "ok", ""), (0, None, ""), @@ -241,6 +252,7 @@ def test_cmd_output_err_with_len0_stderr(self, mocker, returncode, stdout, stder } class TestValidateConfig: + @pytest.mark.benchmark def test_validate_config(self, mocker): # Must call yaml.safe_load. mocker.patch("builtins.open") @@ -253,6 +265,7 @@ def test_validate_config(self, mocker): assert m_yaml_safe_load.call_count == 1 assert retval == dummy_resp + @pytest.mark.benchmark def test_validate_config_file_not_found(self, mocker): # Must return empty dictionary when FileNotFoundError raises in built-in open. mocker.patch("builtins.open").side_effect = FileNotFoundError @@ -262,6 +275,7 @@ def test_validate_config_file_not_found(self, mocker): assert retval == {} class TestPrintErrorMessage: + @pytest.mark.benchmark def test_print_error_header(self, capfd): # Must print on stdout with given argument. print_error_header() @@ -269,6 +283,7 @@ def test_print_error_header(self, capfd): assert "Commit rejected by Commit-Check" in stdout assert "Commit rejected." in stdout + @pytest.mark.benchmark @pytest.mark.parametrize("check_type, type_failed_msg", [ ("message", "check failed =>"), ("branch", "check failed =>"), @@ -294,12 +309,14 @@ def test_print_error_message(self, capfd, check_type, type_failed_msg): assert dummy_error in stdout class TestPrintSuggestion: + @pytest.mark.benchmark def test_print_suggestion(self, capfd): # Must print on stdout with given argument. print_suggestion("dummy suggest") stdout, _ = capfd.readouterr() assert "Suggest:" in stdout + @pytest.mark.benchmark def test_print_suggestion_exit1(self, capfd): # Must exit with 1 when "" passed with pytest.raises(SystemExit) as e: From 9ac3f4ce141c8efd33b4a3353755ace05c16a04a Mon Sep 17 00:00:00 2001 From: shenxianpeng Date: Mon, 7 Jul 2025 13:59:32 +0300 Subject: [PATCH 2/6] chore: delete .github/ISSUE_TEMPLATE/config.yml (#246) --- .github/ISSUE_TEMPLATE/config.yml | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 375a8f2..0000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,8 +0,0 @@ -# Documentation for this file can be found at: -# https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository - -blank_issues_enabled: false -contact_links: - - name: "(maintainers only) Blank issue" - url: https://github.com/commit-check/commit-check/issues/new/ - about: For maintainers only. From 54738e64db30c0ea54bc077ef6e524637c841049 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 21:28:24 +0300 Subject: [PATCH 3/6] ci: pre-commit autoupdate (#248) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d289da2..8f6f962 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,13 +19,13 @@ repos: - id: requirements-txt-fixer - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.11.4 + rev: v0.12.2 hooks: # Run the linter. - id: ruff args: [ --fix ] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.15.0 + rev: v1.16.1 hooks: - id: mypy additional_dependencies: [types-PyYAML] @@ -35,7 +35,7 @@ repos: hooks: - id: codespell - repo: https://github.com/commit-check/commit-check - rev: v0.9.5 + rev: v0.9.8 hooks: - id: check-message # - id: check-branch # uncomment if you need. From cf9d2454133417b01c155ffb4ba30648911d3ea0 Mon Sep 17 00:00:00 2001 From: shenxianpeng Date: Wed, 9 Jul 2025 23:55:30 +0300 Subject: [PATCH 4/6] fix: use `git branch --show-current` to get branch name (#252) * fix: use git branch --show-current to get branch name * fix: set branch name to HEAD if empty --- commit_check/util.py | 5 +++-- tests/util_test.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/commit_check/util.py b/commit_check/util.py index 52b4eaa..fe8211f 100644 --- a/commit_check/util.py +++ b/commit_check/util.py @@ -22,8 +22,9 @@ def get_branch_name() -> str: :returns: A `str` describing the current branch name. """ try: - commands = ['git', 'rev-parse', '--abbrev-ref', 'HEAD'] - branch_name = cmd_output(commands) + # Git 2.22 and above supports `git branch --show-current` + commands = ['git', 'branch', '--show-current'] + branch_name = cmd_output(commands) or "HEAD" except CalledProcessError: branch_name = '' return branch_name.strip() diff --git a/tests/util_test.py b/tests/util_test.py index c426831..a9d9f51 100644 --- a/tests/util_test.py +++ b/tests/util_test.py @@ -25,7 +25,7 @@ def test_get_branch_name(self, mocker): retval = get_branch_name() assert m_cmd_output.call_count == 1 assert m_cmd_output.call_args[0][0] == [ - "git", "rev-parse", "--abbrev-ref", "HEAD" + "git", "branch", "--show-current" ] assert retval == "fake_branch_name" @@ -45,7 +45,7 @@ def test_get_branch_name_with_exception(self, mocker): retval = get_branch_name() assert m_cmd_output.call_count == 1 assert m_cmd_output.call_args[0][0] == [ - "git", "rev-parse", "--abbrev-ref", "HEAD" + "git", "branch", "--show-current" ] assert retval == "" From 55b000f5992da397487eacc5bbf39bf53d94fd5f Mon Sep 17 00:00:00 2001 From: shenxianpeng Date: Thu, 10 Jul 2025 01:08:48 +0300 Subject: [PATCH 5/6] fix: make sure git repo has commit before checking (#253) * fix: make sure git repo has commit before checking * chore: remove print * chore: add # pragma: no cover * chore: add # pragma: no cover --- commit_check/author.py | 5 ++++- commit_check/branch.py | 5 ++++- commit_check/commit.py | 9 ++++++++- commit_check/util.py | 2 -- tests/util_test.py | 15 ++++++--------- 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/commit_check/author.py b/commit_check/author.py index a68397e..e3cfd20 100644 --- a/commit_check/author.py +++ b/commit_check/author.py @@ -1,10 +1,13 @@ """Check git author name and email""" import re from commit_check import YELLOW, RESET_COLOR, PASS, FAIL -from commit_check.util import get_commit_info, print_error_header, print_error_message, print_suggestion +from commit_check.util import get_commit_info, has_commits, print_error_header, print_error_message, print_suggestion def check_author(checks: list, check_type: str) -> int: + if has_commits() is False: + return PASS # pragma: no cover + for check in checks: if check['check'] == check_type: if check['regex'] == "": diff --git a/commit_check/branch.py b/commit_check/branch.py index 1cc8a35..b7446cc 100644 --- a/commit_check/branch.py +++ b/commit_check/branch.py @@ -1,7 +1,7 @@ """Check git branch naming convention.""" import re from commit_check import YELLOW, RESET_COLOR, PASS, FAIL -from commit_check.util import get_branch_name, git_merge_base, print_error_header, print_error_message, print_suggestion +from commit_check.util import get_branch_name, git_merge_base, print_error_header, print_error_message, print_suggestion, has_commits def check_branch(checks: list) -> int: @@ -33,6 +33,9 @@ def check_merge_base(checks: list) -> int: :returns PASS(0) if merge base check succeeds, FAIL(1) otherwise """ + if has_commits() is False: + return PASS # pragma: no cover + for check in checks: if check['check'] == 'merge_base': if check['regex'] == "": diff --git a/commit_check/commit.py b/commit_check/commit.py index 60b8d2f..ac1c63a 100644 --- a/commit_check/commit.py +++ b/commit_check/commit.py @@ -2,7 +2,7 @@ import re from pathlib import PurePath from commit_check import YELLOW, RESET_COLOR, PASS, FAIL -from commit_check.util import cmd_output, get_commit_info, print_error_header, print_error_message, print_suggestion +from commit_check.util import cmd_output, get_commit_info, print_error_header, print_error_message, print_suggestion, has_commits def get_default_commit_msg_file() -> str: @@ -22,6 +22,10 @@ def read_commit_msg(commit_msg_file) -> str: def check_commit_msg(checks: list, commit_msg_file: str = "") -> int: + """Check commit message against the provided checks.""" + if has_commits() is False: + return PASS # pragma: no cover + if commit_msg_file is None or commit_msg_file == "": commit_msg_file = get_default_commit_msg_file() @@ -51,6 +55,9 @@ def check_commit_msg(checks: list, commit_msg_file: str = "") -> int: def check_commit_signoff(checks: list, commit_msg_file: str = "") -> int: + if has_commits() is False: + return PASS # pragma: no cover + if commit_msg_file is None or commit_msg_file == "": commit_msg_file = get_default_commit_msg_file() diff --git a/commit_check/util.py b/commit_check/util.py index fe8211f..6d3c33b 100644 --- a/commit_check/util.py +++ b/commit_check/util.py @@ -57,8 +57,6 @@ def get_commit_info(format_string: str, sha: str = "HEAD") -> str: :returns: A `str`. """ - if has_commits() is False: - return 'Repo has no commits yet.' try: commands = [ 'git', 'log', '-n', '1', f"--pretty=format:%{format_string}", f"{sha}", diff --git a/tests/util_test.py b/tests/util_test.py index a9d9f51..8937005 100644 --- a/tests/util_test.py +++ b/tests/util_test.py @@ -123,7 +123,7 @@ class TestGetCommitInfo: ) def test_get_commit_info(self, mocker, format_string): # Must call get_commit_info with given argument when there are commits. - m_has_commits = mocker.patch( + mocker.patch( "commit_check.util.has_commits", return_value=True ) @@ -132,7 +132,6 @@ def test_get_commit_info(self, mocker, format_string): return_value=" fake commit message " ) retval = get_commit_info(format_string) - assert m_has_commits.call_count == 1 assert m_cmd_output.call_count == 1 assert m_cmd_output.call_args[0][0] == [ "git", "log", "-n", "1", f"--pretty=format:%{format_string}", "HEAD" @@ -142,24 +141,23 @@ def test_get_commit_info(self, mocker, format_string): @pytest.mark.benchmark def test_get_commit_info_no_commits(self, mocker): # Must return 'Repo has no commits yet.' when there are no commits. - m_has_commits = mocker.patch( + mocker.patch( "commit_check.util.has_commits", return_value=False ) - m_cmd_output = mocker.patch( + mocker.patch( "commit_check.util.cmd_output", return_value=" fake commit message " ) format_string = "s" retval = get_commit_info(format_string) - assert m_has_commits.call_count == 1 - assert m_cmd_output.call_count == 0 # Should not call cmd_output - assert retval == "Repo has no commits yet." + assert retval == " fake commit message " + @pytest.mark.benchmark def test_get_commit_info_with_exception(self, mocker): # Must return empty string when exception raises in cmd_output. - m_has_commits = mocker.patch( + mocker.patch( "commit_check.util.has_commits", return_value=True ) @@ -175,7 +173,6 @@ def test_get_commit_info_with_exception(self, mocker): ) format_string = "s" retval = get_commit_info(format_string) - assert m_has_commits.call_count == 1 assert m_cmd_output.call_count == 1 assert m_cmd_output.call_args[0][0] == [ "git", "log", "-n", "1", f"--pretty=format:%{format_string}", "HEAD" From 9d219e981ec71a9b5865dfa7539609d1384ee48d Mon Sep 17 00:00:00 2001 From: shenxianpeng Date: Thu, 10 Jul 2025 01:49:31 +0300 Subject: [PATCH 6/6] fix: add package write permission (#255) --- .github/workflows/publish-image.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish-image.yml b/.github/workflows/publish-image.yml index 9c148f6..6e4fe40 100644 --- a/.github/workflows/publish-image.yml +++ b/.github/workflows/publish-image.yml @@ -2,6 +2,7 @@ name: publish image permissions: contents: read + packages: write on: push: pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy