diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 0000000..52689d6 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,10 @@ +name: PR Autolabeler + +on: + # pull_request event is required for autolabeler + pull_request: + types: [opened, reopened, synchronize] + +jobs: + draft-release: + uses: commit-check/.github/.github/workflows/release-drafter.yml@main diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3737c30..9106468 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,41 +15,33 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.x' - - - name: Install dependencies - run: | - pip install -r requirements-dev.txt - pip install -e . + - run: pip install -r requirements-dev.txt - name: Run pre-commit run: | - pre-commit run --all-files - pre-commit try-repo . + nox -s lint + nox -s test-hook - name: Build wheel - run: python3 -m pip wheel --no-deps -w dist . + run: nox -s build - name: Upload wheel as artifact uses: actions/upload-artifact@v4 with: name: commit-check_wheel path: ${{ github.workspace }}/dist/*.whl - name: Run commit-check - run: | - python3 -m pip install dist/*.whl - commit-check -h - commit-check --message --branch --author-email + run: nox -s commit-check - name: Collect Coverage - run: | - coverage run --source commit_check -m pytest - coverage report && coverage xml - - uses: codecov/codecov-action@v4.6.0 + run: nox -s coverage + + - uses: codecov/codecov-action@v5.0.2 with: token: ${{ secrets.CODECOV_TOKEN }} files: ./coverage.xml @@ -62,13 +54,16 @@ jobs: fail-fast: false matrix: py: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] - os: ['windows-latest', 'ubuntu-latest', 'macos-latest'] + os: ['windows-latest', 'ubuntu-24.04', 'macos-latest'] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: ${{ matrix.py }} + - run: | + pip install --upgrade pip + pip install -r requirements-dev.txt - name: Download wheel artifact uses: actions/download-artifact@v4 @@ -79,26 +74,27 @@ jobs: - name: Install test # using a wildcard as filename on Windows requires a bash shell shell: bash - run: python3 -m pip install dist/*.whl + run: nox -s install-wheel docs: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} # get current branch name - uses: actions/setup-python@v5 with: python-version: "3.10" - - run: python -m pip install . -r docs/requirements.txt + - run: pip install -r requirements-dev.txt - name: Build docs - working-directory: docs - run: sphinx-build -E -W -b html . _build/html + run: nox -s docs - name: Save built docs as artifact uses: actions/upload-artifact@v4 with: name: "commit-check_docs" - path: ${{ github.workspace }}/docs/_build/html + path: ${{ github.workspace }}/_build/html - name: Upload docs to github pages # only publish doc changes from main branch @@ -106,4 +102,4 @@ jobs: uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./docs/_build/html + publish_dir: ./_build/html diff --git a/.github/workflows/publish-image.yml b/.github/workflows/publish-image.yml index 53649bc..39dd16e 100644 --- a/.github/workflows/publish-image.yml +++ b/.github/workflows/publish-image.yml @@ -13,7 +13,7 @@ on: jobs: publish: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Build to check Dockerfile diff --git a/.github/workflows/publish-package.yml b/.github/workflows/publish-package.yml index 77c5179..9dcac79 100644 --- a/.github/workflows/publish-package.yml +++ b/.github/workflows/publish-package.yml @@ -12,7 +12,7 @@ permissions: jobs: publish: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 # use fetch --all for setuptools_scm to work @@ -33,7 +33,7 @@ jobs: twine check dist/commit_check* - name: Create attestations - uses: actions/attest-build-provenance@v1 + uses: actions/attest-build-provenance@v2 with: subject-path: "dist/commit_check*" diff --git a/.gitpod.yml b/.gitpod.yml index e6de223..8123636 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -3,6 +3,6 @@ # and commit this file to your remote git repository to share the goodness with others. tasks: - - before: pip install -r requirements-dev.txt + - before: pip install --upgrade pip && pip install -r requirements-dev.txt init: pre-commit install command: pip install -e . diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d65d917..e1d96d8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,28 +15,29 @@ repos: - id: trailing-whitespace - id: name-tests-test - id: requirements-txt-fixer -- repo: https://github.com/PyCQA/flake8 - rev: 7.1.1 +- repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.9.3 hooks: - - id: flake8 - args: [--max-line-length=100, --ignore=E501] - exclude: ^commit_check/__init__.py + # Run the linter. + - id: ruff + args: [ --fix ] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.13.0 + rev: v1.14.1 hooks: - id: mypy additional_dependencies: [types-PyYAML] exclude: ^testing/resources/ - repo: https://github.com/codespell-project/codespell - rev: v2.3.0 + rev: v2.4.0 hooks: - id: codespell - repo: https://github.com/commit-check/commit-check - rev: v0.9.1 + rev: v0.9.2 hooks: - id: check-message # - id: check-branch # uncomment if you need. - id: check-author-name # uncomment if you need. - id: check-author-email # uncomment if you need. # - id: commit-signoff # uncomment if you need. - # - id: check-merge-base # uncomment if you need. + # - id: check-merge-base # requires download all git history diff --git a/README.rst b/README.rst index 09d71eb..2f19257 100644 --- a/README.rst +++ b/README.rst @@ -30,9 +30,11 @@ Commit Check Overview -------- -**Commit Check** is a free, powerful tool that enforces commit metadata standards, including commit message, branch naming, committer name/email, and commit signoff. Fully customizable with error messages and suggested commands, it ensures compliance across teams. +**Commit Check** is a free, powerful tool that enforces commit metadata standards, including commit message, branch naming, committer name/email, commit signoff and more. -As an alternative to GitHub Enterprise `Metadata restrictions `_ and Bitbucket's paid puglin `Yet Another Commit Checker `_, Commit Check stands out by integrating DevOps principles and Infrastructure as Code (IaC). +Fully customizable with error messages and suggested commands, it ensures compliance across teams. + +As an alternative to GitHub Enterprise `Metadata restrictions `_ and Bitbucket's paid plugin `Yet Another Commit Checker `_, Commit Check stands out by integrating DevOps principles and Infrastructure as Code (IaC). Configuration ------------- @@ -70,35 +72,35 @@ Running as pre-commit hook - repo: https://github.com/commit-check/commit-check rev: the tag or revision hooks: # support hooks - - id: check-message # it requires hook prepare-commit-msg + - id: check-message # requires prepare-commit-msg hook - id: check-branch - id: check-author-name - id: check-author-email - id: check-commit-signoff - - id: check-merge-base + - id: check-merge-base # requires download all git history Running as CLI ~~~~~~~~~~~~~~ -Global Installation +Install globally .. code-block:: bash sudo pip3 install -U commit-check -User Installation +Install locally .. code-block:: bash pip install -U commit-check -Install from Git Repo +Install from source code .. code-block:: bash pip install git+https://github.com/commit-check/commit-check.git@main -Then, run ``commit-check`` from the command line. For more information, see the `docs `_. +Then, run ``commit-check --help`` from the command line. For more information, see the `docs `_. Running as Git Hooks ~~~~~~~~~~~~~~~~~~~~ @@ -118,8 +120,8 @@ Save the script file as ``pre-push`` and make it executable: Now, ``git push`` will trigger this hook automatically. -Example -------- +Examples +-------- Check Commit Message Failed @@ -155,24 +157,47 @@ Check Branch Naming Failed Commit rejected by Commit-Check. - (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) - / ._. \ / ._. \ / ._. \ / ._. \ / ._. \ - __\( C )/__ __\( H )/__ __\( E )/__ __\( C )/__ __\( K )/__ + (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) + / ._. \ / ._. \ / ._. \ / ._. \ / ._. \ + __\( C )/__ __\( H )/__ __\( E )/__ __\( C )/__ __\( K )/__ (_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._) - || E || || R || || R || || O || || R || - _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._ + || E || || R || || R || || O || || R || + _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._ (.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.) - `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ + `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ Commit rejected. - Type branch check failed => my-test-branch + Type branch check failed => patch-1 It doesn't match regex: ^(bugfix|feature|release|hotfix|task|chore)\/.+|(master)|(main)|(HEAD)|(PR-.+) - Branches must begin with these types: bugfix/ feature/ release/ hotfix/ task/ chore/ Suggest: run command `git checkout -b type/branch_name` +Check Commit Signature Failed + +.. code-block:: text + + Commit rejected by Commit-Check. + + (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) + / ._. \ / ._. \ / ._. \ / ._. \ / ._. \ + __\( C )/__ __\( H )/__ __\( E )/__ __\( C )/__ __\( K )/__ + (_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._) + || E || || R || || R || || O || || R || + _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._ + (.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.) + `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ + + Commit rejected. + + Type commit_signoff check failed => c92ce259ff041c91859c7fb61afdbb391e769d0f + It doesn't match regex: Signed-off-by:.*[A-Za-z0-9]\s+<.+@.+> + Signed-off-by not found in latest commit + Suggest: run command `git commit -m "conventional commit message" --signoff` + + + Badging your repository ----------------------- diff --git a/commit_check/branch.py b/commit_check/branch.py index 39bc7ec..1cc8a35 100644 --- a/commit_check/branch.py +++ b/commit_check/branch.py @@ -16,7 +16,7 @@ def check_branch(checks: list) -> int: result = re.match(check['regex'], branch_name) if result is None: if not print_error_header.has_been_called: - print_error_header() + print_error_header() # pragma: no cover print_error_message( check['check'], check['regex'], check['error'], branch_name, @@ -45,7 +45,7 @@ def check_merge_base(checks: list) -> int: result = git_merge_base(target_branch, current_branch) if result != 0: if not print_error_header.has_been_called: - print_error_header() + print_error_header() # pragma: no cover print_error_message( check['check'], check['regex'], check['error'], current_branch, diff --git a/commit_check/commit.py b/commit_check/commit.py index 5505e77..60b8d2f 100644 --- a/commit_check/commit.py +++ b/commit_check/commit.py @@ -38,7 +38,7 @@ def check_commit_msg(checks: list, commit_msg_file: str = "") -> int: result = re.match(check['regex'], commit_msg) if result is None: if not print_error_header.has_been_called: - print_error_header() + print_error_header() # pragma: no cover print_error_message( check['check'], check['regex'], check['error'], commit_msg, @@ -67,7 +67,7 @@ def check_commit_signoff(checks: list, commit_msg_file: str = "") -> int: result = re.search(check['regex'], commit_msg) if result is None: if not print_error_header.has_been_called: - print_error_header() + print_error_header() # pragma: no cover print_error_message( check['check'], check['regex'], check['error'], commit_hash, diff --git a/commit_check/main.py b/commit_check/main.py index 50b2350..6faa070 100644 --- a/commit_check/main.py +++ b/commit_check/main.py @@ -10,7 +10,7 @@ from commit_check import author from commit_check.util import validate_config from commit_check.error import error_handler -from . import CONFIG_FILE, DEFAULT_CONFIG, PASS, __version__ +from . import CONFIG_FILE, DEFAULT_CONFIG, PASS, FAIL, __version__ def get_parser() -> argparse.ArgumentParser: @@ -99,7 +99,11 @@ def main() -> int: """The main entrypoint of commit-check program.""" parser = get_parser() args = parser.parse_args() - retval = PASS + + if args.dry_run: + return PASS + + check_results: list[int] = [] with error_handler(): config = validate_config(args.config) if validate_config( @@ -107,21 +111,19 @@ def main() -> int: ) else DEFAULT_CONFIG checks = config['checks'] if args.message: - retval = commit.check_commit_msg(checks, args.commit_msg_file) + check_results.append(commit.check_commit_msg(checks, args.commit_msg_file)) if args.author_name: - retval = author.check_author(checks, "author_name") + check_results.append(author.check_author(checks, "author_name")) if args.author_email: - retval = author.check_author(checks, "author_email") + check_results.append(author.check_author(checks, "author_email")) if args.branch: - retval = branch.check_branch(checks) + check_results.append(branch.check_branch(checks)) if args.commit_signoff: - retval = commit.check_commit_signoff(checks) + check_results.append(commit.check_commit_signoff(checks)) if args.merge_base: - retval = branch.check_merge_base(checks) + check_results.append(branch.check_merge_base(checks)) - if args.dry_run: - retval = PASS - return retval + return PASS if all(val == PASS for val in check_results) else FAIL if __name__ == '__main__': diff --git a/docs/README.rst b/docs/README.rst index cdb46e5..d296410 100644 --- a/docs/README.rst +++ b/docs/README.rst @@ -9,15 +9,13 @@ From the root directory of the repository, do the following to steps .. code-block:: text - pip install -r docs/requirements.txt - - On Linux, you may need to use ``pip3`` instead. + pip install nox 2. Build the docs .. code-block:: text - sphinx-build docs docs/_build/html + nox -s docs - Browse the files in docs/_build/html with your internet browser to see the rendered + Browse the files in /_build/html with your internet browser to see the rendered output. diff --git a/noxfile.py b/noxfile.py index 3600177..079df8a 100644 --- a/noxfile.py +++ b/noxfile.py @@ -2,7 +2,6 @@ import glob nox.options.reuse_existing_virtualenvs = True -nox.options.reuse_venv = True nox.options.sessions = ["lint"] REQUIREMENTS = { @@ -42,23 +41,21 @@ def build(session): @nox.session(name="install-wheel", requires=["build"]) def install_wheel(session): + session.run("python3", "-m", "pip", "wheel", "--no-deps", "-w", "dist", ".") whl_file = glob.glob("dist/*.whl") session.install(str(whl_file[0])) -# @nox.session(name="commit-check", requires=["install-wheel"]) -@nox.session(name="commit-check", requires=["install-wheel"]) +@nox.session(name="commit-check") def commit_check(session): - session.run( - "commit-check", - "--message", - "--branch", - "--author-email", - ) + session.install(".") + session.run("commit-check", "--message", "--branch", "--author-email") -@nox.session(requires=["install-wheel"]) +@nox.session() def coverage(session): + session.install(".") + session.install("-r", REQUIREMENTS["dev"]) session.run("coverage", "run", "--source", "commit_check", "-m", "pytest") session.run("coverage", "report") session.run("coverage", "xml") @@ -66,6 +63,13 @@ def coverage(session): @nox.session() def docs(session): - session.install("-e", ".") + session.install(".") session.install("-r", REQUIREMENTS["docs"]) session.run("sphinx-build", "-E", "-W", "-b", "html", "docs", "_build/html") + + +@nox.session(name="docs-live") +def docs_live(session): + session.install(".") + session.install("-r", REQUIREMENTS["docs"], "sphinx-autobuild") + session.run("sphinx-autobuild", "-b", "html", "docs", "_build/html") diff --git a/pyproject.toml b/pyproject.toml index 1e028b4..2710ff6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ license = {text = "MIT License"} authors = [ { name = "Xianpeng Shen", email = "xianpeng.shen@gmail.com" }, ] +requires-python = ">=3.8" dependencies = ["pyyaml"] classifiers = [ # https://pypi.org/pypi?%3Aaction=list_classifiers diff --git a/requirements-dev.txt b/requirements-dev.txt index cb4cde7..120645f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ coverage -nox +git+https://github.com/wntrblm/nox.git@main pre-commit pytest pytest-mock diff --git a/tests/branch_test.py b/tests/branch_test.py index 64ec3a4..083c5bd 100644 --- a/tests/branch_test.py +++ b/tests/branch_test.py @@ -122,6 +122,17 @@ def test_check_merge_base_with_empty_checks(self, mocker): assert retval == PASS assert m_check_merge.call_count == 0 + + def test_check_merge_base_with_empty_regex(self, mocker): + checks = [{ + "check": "merge_base", + "regex": "" + }] + m_check_merge = mocker.patch(f"{LOCATION}.check_merge_base") + retval = check_merge_base(checks) + assert retval == PASS + assert m_check_merge.call_count == 0 + def test_check_merge_base_with_different_check(self, mocker): checks = [{ "check": "branch", diff --git a/tests/commit_test.py b/tests/commit_test.py index 5642a52..a458636 100644 --- a/tests/commit_test.py +++ b/tests/commit_test.py @@ -30,6 +30,25 @@ def test_read_commit_msg_file_not_found(mocker): assert m_commits_info.call_count == 0 +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", + return_value=".git/COMMIT_EDITMSG" + ) + mock_read_commit_msg = mocker.patch( + "commit_check.commit.read_commit_msg", + return_value="Sample commit message" + ) + + checks = [{"regex": ".*", "check": "message", "error": "Invalid", "suggest": None}] + + result = check_commit_msg(checks, commit_msg_file="") + + mock_get_default_commit_msg_file.assert_called_once() + mock_read_commit_msg.assert_called_once_with(".git/COMMIT_EDITMSG") + assert result == 0 + + def test_check_commit_with_empty_checks(mocker): checks = [] m_re_match = mocker.patch( diff --git a/tests/error_test.py b/tests/error_test.py index 188a2b1..d0a2880 100644 --- a/tests/error_test.py +++ b/tests/error_test.py @@ -24,6 +24,32 @@ def test_error_handler_unexpected_error(): assert exit_info.value.code == 3 +def test_error_handler_cannot_access(mocker): + with pytest.raises(SystemExit): + store_dir = "/fake/commit-check" + log_path = os.path.join(store_dir, "commit-check.log") + mocker.patch.dict(os.environ, {"COMMIT_CHECK_HOME": store_dir}) + mock_os_access = mocker.patch("os.access", return_value=False) + mocker.patch("os.path.exists", return_value=True) + mocker.patch("os.makedirs") + mock_open = mocker.patch("builtins.open", mocker.mock_open()) + mocker.patch("commit_check.util.cmd_output", return_value="mock_version") + mocker.patch("sys.version", "Mock Python Version") + mocker.patch("sys.executable", "/mock/path/to/python") + + from commit_check.error import log_and_exit + log_and_exit( + msg="Test error message", + ret_code=1, + exc=ValueError("Test exception"), + formatted="Mocked formatted stack trace" + ) + + mock_os_access.assert_called_once_with(store_dir, os.W_OK) + mock_open.assert_called_with(log_path, "a") + mock_open().write.assert_any_call(f"Failed to write to log at {log_path}\n") + + @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 9d26ebe..ca77032 100644 --- a/tests/main_test.py +++ b/tests/main_test.py @@ -1,7 +1,7 @@ import sys import pytest from commit_check.main import main -from commit_check import DEFAULT_CONFIG +from commit_check import DEFAULT_CONFIG, PASS, FAIL CMD = "commit-check" @@ -115,3 +115,65 @@ def test_main_validate_config_ret_none(self, mocker): main() assert m_check_commit.call_count == 1 assert m_check_commit.call_args[0][0] == DEFAULT_CONFIG["checks"] + + @pytest.mark.parametrize( + "argv, message_result, branch_result, author_name_result, author_email_result, commit_signoff_result, merge_base_result, final_result", + [ + ([CMD, "--message"], PASS, PASS, PASS, PASS, PASS, PASS, PASS), + ([CMD, "--message"], FAIL, PASS, PASS, PASS, PASS, PASS, FAIL), + ([CMD, "--message", "--commit-signoff"], FAIL, PASS, PASS, PASS, PASS, PASS, FAIL,), + ([CMD, "--message", "--commit-signoff"], PASS, PASS, PASS, PASS, FAIL, PASS, FAIL,), + ([CMD, "--message", "--author-name", "--author-email"], PASS, PASS, PASS, PASS, PASS, PASS, PASS,), + ([CMD, "--message", "--author-name", "--author-email"], FAIL, PASS, PASS, PASS, PASS, PASS, FAIL,), + ([CMD, "--message", "--author-name", "--author-email"], PASS, PASS, FAIL, PASS, PASS, PASS, FAIL,), + ([CMD, "--message", "--author-name", "--author-email"], PASS, PASS, PASS, FAIL, PASS, PASS, FAIL,), + ([CMD, "--message", "--author-name", "--author-email"], PASS, PASS, FAIL, FAIL, PASS, PASS, FAIL,), + ([CMD, "--message", "--branch", "--author-name", "--author-email", "--commit-signoff", "--merge-base", ], PASS, PASS, PASS, PASS, PASS, PASS, PASS,), + ([CMD, "--message", "--branch", "--author-name", "--author-email", "--commit-signoff", "--merge-base", ], FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,), + ([CMD, "--message", "--branch", "--author-name", "--author-email", "--commit-signoff", "--merge-base", ], FAIL, PASS, PASS, PASS, PASS, PASS, FAIL,), + ([CMD, "--dry-run"], FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, PASS), + ], + ) + def test_main_multiple_checks( + self, + mocker, + argv, + message_result, + branch_result, + author_name_result, + author_email_result, + commit_signoff_result, + merge_base_result, + final_result, + ): + mocker.patch( + "commit_check.main.validate_config", + return_value={}, + ) + + mocker.patch( + "commit_check.commit.check_commit_msg", return_value=message_result + ) + mocker.patch( + "commit_check.commit.check_commit_signoff", + return_value=commit_signoff_result, + ) + + mocker.patch("commit_check.branch.check_branch", return_value=branch_result) + mocker.patch( + "commit_check.branch.check_merge_base", return_value=merge_base_result + ) + + # this is messy. why isn't this a private implementation detail with a + # public check_author_name and check_author email? + def author_side_effect(_, check_type: str) -> int: # type: ignore[return] + assert check_type in ("author_name", "author_email") + if check_type == "author_name": + return author_name_result + elif check_type == "author_email": + return author_email_result + + mocker.patch("commit_check.author.check_author", side_effect=author_side_effect) + + sys.argv = argv + assert main() == final_result 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