From 416f68cf83cc41bcdee6dfce4896e1f58442ccfa Mon Sep 17 00:00:00 2001 From: Robert Schweizer Date: Fri, 30 Dec 2022 12:25:35 +0100 Subject: [PATCH] feat: Make `cz bump` work on shallow clones This allows reliably setting up your CI to only fetch the latest changes. E.g. Gitlab CI uses shallow clones with a depth of 20 by default: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77576 Only `tag_exists()` had to be modified for this. `cz changelog` is not ready yet, more functions would have to include a `git fetch` for that. I was worried about slowing down error reporting in non-CI workflows ("git fetch" calls can sometimes take a while if your connection is bad). To minimize the risk for this, now only fetching the tag for shallow clones and always printing a warning when this happens. I was hit by this issue while trying out Commitizen in Gitlab CI on a monorepo, where some packages haven't seen a release for >50 commits. --- commitizen/git.py | 17 ++++++++++++++++- tests/commands/test_bump_command.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/commitizen/git.py b/commitizen/git.py index 2c2cb5b368..0a61445145 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -168,7 +168,22 @@ def get_tags(dateformat: str = "%Y-%m-%d") -> List[GitTag]: def tag_exist(tag: str) -> bool: c = cmd.run(f"git tag --list {tag}") - return tag in c.out + if tag in c.out: + return True + + # In shallow clones (e.g. set up by Gitlab CI), the previous release tag might not + # be available locally, so try and fetch it. + if cmd.run("git rev-parse --is-shallow-repository").out.strip() == "true": + out.warn( + f"Could not find tag {tag} locally. Since this is a shallow clone, " + "trying to fetch it from all available remotes now." + ) + for remote in cmd.run("git remote").out.strip().splitlines(): + res = cmd.run(f"git fetch --depth=1 {remote} tag {tag}") + if res.return_code == 0: + return True + + return False def is_signed_tag(tag: str) -> bool: diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index dcdca163b2..a336e6e1b7 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -1,5 +1,8 @@ import inspect +import os +import re import sys +from pathlib import Path from typing import Tuple from unittest.mock import MagicMock @@ -377,6 +380,32 @@ def test_bump_major_version_zero_when_major_is_not_zero(mocker, tmp_commitizen_p assert expected_error_message in str(excinfo.value) +def test_bump_shallow_clone( + tmp_commitizen_project, + tmp_path: Path, + mocker: MockFixture, + capsys: pytest.CaptureFixture, +): + # Set up repository with one released and one unreleased commit. + create_file_and_commit("feat: Add file for 0.1.0") + create_tag("0.1.0") + create_file_and_commit("feat: Add file for, but do not release 0.2.0") + + # Shallow clone this local repository, so the release tag is missing. + cloned_repo = tmp_path / "cloned_repo" + # Use file:// URL, so Git does a shallow clone. + cmd.run(f"git clone --depth=1 file://{tmp_commitizen_project} {cloned_repo}") + os.chdir(cloned_repo) + + # Bumping should work, but with a warning that we are pulling the tag from remote. + testargs = ["cz", "bump"] + mocker.patch.object(sys, "argv", testargs) + capsys.readouterr() # Reset capsys fixture + cli.main() + stdout, _stderr = capsys.readouterr() + assert re.search("Could not find tag.*trying to fetch", stdout) + + def test_bump_files_only(mocker: MockFixture, tmp_commitizen_project): tmp_version_file = tmp_commitizen_project.join("__version__.py") tmp_version_file.write("0.1.0")