예제 #1
0
def create_file_and_commit(message: str, filename: Optional[str] = None):
    if not filename:
        filename = str(uuid.uuid4())

    Path(f"./{filename}").touch()
    cmd.run("git add .")
    git.commit(message)
예제 #2
0
def get_commits(
    start: Optional[str] = None,
    end: str = "HEAD",
    *,
    log_format: str = "%H%n%s%n%an%n%ae%n%b",
    delimiter: str = "----------commit-delimiter----------",
    args: str = "",
) -> List[GitCommit]:
    """Get the commits between start and end."""
    git_log_cmd = f"git log --pretty={log_format}{delimiter} {args}"

    if start:
        c = cmd.run(f"{git_log_cmd} {start}..{end}")
    else:
        c = cmd.run(f"{git_log_cmd} {end}")

    if not c.out:
        return []

    git_commits = []
    for rev_and_commit in c.out.split(f"{delimiter}\n"):
        if not rev_and_commit:
            continue
        rev, title, author, author_email, *body_list = rev_and_commit.split(
            "\n")
        if rev_and_commit:
            git_commit = GitCommit(
                rev=rev.strip(),
                title=title.strip(),
                body="\n".join(body_list).strip(),
                author=author,
                author_email=author_email,
            )
            git_commits.append(git_commit)
    return git_commits
예제 #3
0
def test_get_latest_tag_name(tmp_commitizen_project):
    with tmp_commitizen_project.as_cwd():
        tag_name = git.get_latest_tag_name()
        assert tag_name is None

        create_file_and_commit("feat(test): test")
        cmd.run("git tag 1.0")
        tag_name = git.get_latest_tag_name()
        assert tag_name == "1.0"
예제 #4
0
def test_bump_command(mocker, create_project):
    with open("./pyproject.toml", "w") as f:
        f.write("[tool.commitizen]\n" 'version="0.1.0"')

    cmd.run("git init")

    # MINOR
    create_file_and_commit("feat: new file")

    testargs = ["cz", "bump", "--yes"]
    mocker.patch.object(sys, "argv", testargs)
    cli.main()

    tag_exists = git.tag_exist("0.2.0")
    assert tag_exists is True

    # PATCH
    create_file_and_commit("fix: username exception")

    testargs = ["cz", "bump"]
    mocker.patch.object(sys, "argv", testargs)
    cli.main()

    tag_exists = git.tag_exist("0.2.1")
    assert tag_exists is True

    # PRERELEASE
    create_file_and_commit("feat: location")

    testargs = ["cz", "bump", "--prerelease", "alpha"]
    mocker.patch.object(sys, "argv", testargs)
    cli.main()

    tag_exists = git.tag_exist("0.3.0a0")
    assert tag_exists is True

    # PRERELEASE BUMP CREATES VERSION WITHOUT PRERELEASE
    testargs = ["cz", "bump"]
    mocker.patch.object(sys, "argv", testargs)
    cli.main()

    tag_exists = git.tag_exist("0.3.0")
    assert tag_exists is True

    # MAJOR
    create_file_and_commit(
        "feat: new user interface\n\nBREAKING CHANGE: age is no longer supported"
    )

    testargs = ["cz", "bump"]
    mocker.patch.object(sys, "argv", testargs)
    cli.main()

    tag_exists = git.tag_exist("1.0.0")
    assert tag_exists is True
예제 #5
0
def test_is_staging_clean_when_adding_file(tmp_commitizen_project):
    with tmp_commitizen_project.as_cwd():
        assert git.is_staging_clean() is True

        cmd.run("touch test_file")

        assert git.is_staging_clean() is True

        cmd.run("git add test_file")

        assert git.is_staging_clean() is False
예제 #6
0
파일: git.py 프로젝트: esciara/commitizen
def get_commits(start: str,
                end: str = "HEAD",
                from_beginning: bool = False) -> list:

    c = cmd.run(f"git log --pretty=format:%s%n%b {start}...{end}")

    if from_beginning:
        c = cmd.run(f"git log --pretty=format:%s%n%b {end}")

    if not c.out:
        return []
    return c.out.split("\n")
예제 #7
0
def test_is_staging_clean_when_updating_file(tmp_commitizen_project):
    with tmp_commitizen_project.as_cwd():
        assert git.is_staging_clean() is True

        cmd.run("touch test_file")
        cmd.run("git add test_file")
        cmd.run("git commit -m 'add test_file'")
        cmd.run("echo 'test' > test_file")

        assert git.is_staging_clean() is True

        cmd.run("git add test_file")

        assert git.is_staging_clean() is False
예제 #8
0
def test_bump_on_git_with_hooks_no_verify_enabled(mocker):
    cmd.run("mkdir .git/hooks")
    with open(".git/hooks/pre-commit", "w") as f:
        f.write("#!/usr/bin/env bash\n" 'echo "0.1.0"')
    cmd.run("chmod +x .git/hooks/pre-commit")

    # MINOR
    create_file_and_commit("feat: new file")

    testargs = ["cz", "bump", "--yes", "--no-verify"]
    mocker.patch.object(sys, "argv", testargs)
    cli.main()

    tag_exists = git.tag_exist("0.2.0")
    assert tag_exists is True
예제 #9
0
파일: git.py 프로젝트: jordanSu/commitizen
def commit(message: str, args: str = ""):
    f = NamedTemporaryFile("wb", delete=False)
    f.write(message.encode("utf-8"))
    f.close()
    c = cmd.run(f"git commit {args} -F {f.name}")
    os.unlink(f.name)
    return c
예제 #10
0
def test_bump_on_git_with_hooks_no_verify_disabled(mocker):
    cmd.run("mkdir .git/hooks")
    with open(".git/hooks/pre-commit", "w") as f:
        f.write("#!/usr/bin/env bash\n" 'echo "0.1.0"')
    cmd.run("chmod +x .git/hooks/pre-commit")

    # MINOR
    create_file_and_commit("feat: new file")

    testargs = ["cz", "bump", "--yes"]
    mocker.patch.object(sys, "argv", testargs)

    with pytest.raises(BumpCommitFailedError) as excinfo:
        cli.main()

    assert 'git.commit error: "0.1.0"' in str(excinfo.value)
예제 #11
0
def test_bump_tag_exists_raises_exception(mocker):
    cmd.run("mkdir .git/hooks")
    with open(".git/hooks/post-commit", "w") as f:
        f.write("#!/usr/bin/env bash\n" "exit 9")
    cmd.run("chmod +x .git/hooks/post-commit")

    # MINOR
    create_file_and_commit("feat: new file")
    git.tag("0.2.0")

    testargs = ["cz", "bump", "--yes"]
    mocker.patch.object(sys, "argv", testargs)

    with pytest.raises(BumpTagFailedError) as excinfo:
        cli.main()
    assert "0.2.0" in str(excinfo.value)  # This should be a fatal error
예제 #12
0
def test_bump_on_git_with_hooks_no_verify_disabled(mocker, capsys):
    cmd.run("mkdir .git/hooks")
    with open(".git/hooks/pre-commit", "w") as f:
        f.write("#!/usr/bin/env bash\n" 'echo "0.1.0"')
    cmd.run("chmod +x .git/hooks/pre-commit")

    # MINOR
    create_file_and_commit("feat: new file")

    testargs = ["cz", "bump", "--yes"]
    mocker.patch.object(sys, "argv", testargs)

    with pytest.raises(SystemExit):
        cli.main()

    _, err = capsys.readouterr()
    assert 'git.commit errror: "0.1.0"' in err
예제 #13
0
def test_bump_minor_increment_annotated(commit_msg, mocker):
    create_file_and_commit(commit_msg)
    testargs = ["cz", "bump", "--yes", "--annotated-tag"]
    mocker.patch.object(sys, "argv", testargs)
    cli.main()
    tag_exists = git.tag_exist("0.2.0")
    cmd_res = cmd.run('git for-each-ref refs/tags --format "%(objecttype):%(refname)"')
    assert tag_exists is True and "tag:refs/tags/0.2.0\n" in cmd_res.out
예제 #14
0
def test_bump_when_bumpping_is_not_support(mocker, capsys, tmpdir):
    with tmpdir.as_cwd():
        with open("./pyproject.toml", "w") as f:
            f.write("[tool.commitizen]\n" 'version="0.1.0"')

        cmd.run("git init")
        create_file_and_commit(
            "feat: new user interface\n\nBREAKING CHANGE: age is no longer supported"
        )

        testargs = ["cz", "-n", "cz_jira", "bump", "--yes"]
        mocker.patch.object(sys, "argv", testargs)

        with pytest.raises(SystemExit):
            cli.main()

        _, err = capsys.readouterr()
        assert "'cz_jira' rule does not support bump" in err
예제 #15
0
def test_get_commits_with_signature():
    config_file = ".git/config"
    config_backup = ".git/config.bak"
    shutil.copy(config_file, config_backup)

    try:
        # temporarily turn on --show-signature
        cmd.run("git config log.showsignature true")

        # retrieve a commit that we know has a signature
        commit = git.get_commits(
            start="bec20ebf433f2281c70f1eb4b0b6a1d0ed83e9b2",
            end="9eae518235d051f145807ddf971ceb79ad49953a",
        )[0]

        assert commit.title.startswith("fix")
    finally:
        # restore the repo's original config
        shutil.move(config_backup, config_file)
예제 #16
0
def get_tags(dateformat: str = "%Y-%m-%d") -> List[GitTag]:
    inner_delimiter = "---inner_delimiter---"
    formatter = (f'"%(refname:lstrip=2){inner_delimiter}'
                 f"%(objectname){inner_delimiter}"
                 f'%(committerdate:format:{dateformat})"')
    c = cmd.run(f"git tag --format={formatter} --sort=-committerdate")
    if c.err or not c.out:
        return []

    git_tags = [
        GitTag(*line.split(inner_delimiter)) for line in c.out.split("\n")[:-1]
    ]
    return git_tags
예제 #17
0
def test_bump_minor_increment_annotated_config_file(commit_msg, mocker,
                                                    tmp_commitizen_project):
    tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml")
    tmp_commitizen_cfg_file.write(f"{tmp_commitizen_cfg_file.read()}\n"
                                  f"annotated_tag = 1")
    create_file_and_commit(commit_msg)
    testargs = ["cz", "bump", "--yes"]
    mocker.patch.object(sys, "argv", testargs)
    cli.main()
    tag_exists = git.tag_exist("0.2.0")
    cmd_res = cmd.run(
        'git for-each-ref refs/tags --format "%(objecttype):%(refname)"')
    assert tag_exists is True and "tag:refs/tags/0.2.0\n" in cmd_res.out
예제 #18
0
파일: init.py 프로젝트: zombig/commitizen
    def _install_pre_commit_hook(self):
        pre_commit_config_filename = ".pre-commit-config.yaml"
        cz_hook_config = {
            "repo": "https://github.com/commitizen-tools/commitizen",
            "rev": f"v{__version__}",
            "hooks": [{
                "id": "commitizen",
                "stages": ["commit-msg"]
            }],
        }

        config_data = {}
        if not os.path.isfile(pre_commit_config_filename):
            # .pre-commit-config does not exist
            config_data["repos"] = [cz_hook_config]
        else:
            # breakpoint()
            with open(pre_commit_config_filename) as config_file:
                yaml_data = yaml.safe_load(config_file)
                if yaml_data:
                    config_data = yaml_data

            if "repos" in config_data:
                for pre_commit_hook in config_data["repos"]:
                    if "commitizen" in pre_commit_hook["repo"]:
                        out.write("commitizen already in pre-commit config")
                        break
                else:
                    config_data["repos"].append(cz_hook_config)
            else:
                # .pre-commit-config exists but there's no "repos" key
                config_data["repos"] = [cz_hook_config]

        with open(pre_commit_config_filename, "w") as config_file:
            yaml.safe_dump(config_data, stream=config_file)

        c = cmd.run("pre-commit install --hook-type commit-msg")
        if c.return_code == 127:
            out.error(
                "pre-commit is not installed in current environement.\n"
                "Run 'pre-commit install --hook-type commit-msg' again after it's installed"
            )
        elif c.return_code != 0:
            out.error(c.err)
        else:
            out.write(
                "commitizen pre-commit hook is now installed in your '.git'\n")
def test_bump_pre_commit_changelog_fails_always(tmp_commitizen_project, mocker,
                                                freezer, retry):
    freezer.move_to("2022-04-01")
    testargs = ["cz", "bump", "--changelog", "--yes"]
    if retry:
        testargs.append("--retry")
    mocker.patch.object(sys, "argv", testargs)
    with tmp_commitizen_project.as_cwd():
        Path(".pre-commit-config.yaml").write_text("""
            repos:
              - repo: local
                hooks:
                - id: forbid-changelog
                  name: changelogs are forbidden
                  entry: changelogs are forbidden
                  language: fail
                  files: CHANGELOG.md
            """)
        cmd.run("git add -A")
        cmd.run("git commit -m 'feat: forbid changelogs'")
        cmd.run("pre-commit install")
        with pytest.raises(exceptions.BumpCommitFailedError):
            cli.main()
def test_bump_pre_commit_changelog(tmp_commitizen_project, mocker, freezer,
                                   retry):
    freezer.move_to("2022-04-01")
    testargs = ["cz", "bump", "--changelog", "--yes"]
    if retry:
        testargs.append("--retry")
    else:
        pytest.xfail(
            "it will fail because pre-commit will reformat CHANGELOG.md")
    mocker.patch.object(sys, "argv", testargs)
    with tmp_commitizen_project.as_cwd():
        # Configure prettier as a pre-commit hook
        Path(".pre-commit-config.yaml").write_text("""
            repos:
              - repo: https://github.com/pre-commit/mirrors-prettier
                rev: v2.6.2
                hooks:
                - id: prettier
                  stages: [commit]
            """)
        # Prettier inherits editorconfig
        Path(".editorconfig").write_text("""
            [*]
            indent_size = 4
            """)
        cmd.run("git add -A")
        cmd.run("git commit -m 'fix: _test'")
        cmd.run("pre-commit install")
        cli.main()
        # Pre-commit fixed last line adding extra indent and "\" char
        assert Path("CHANGELOG.md").read_text() == dedent("""\
            ## 0.1.1 (2022-04-01)

            ### Fix

            -   \\_test
            """)
예제 #21
0
    def __call__(self):  # noqa: C901
        """Steps executed to bump."""
        try:
            current_version_instance: Version = Version(self.bump_settings["version"])
        except TypeError:
            raise NoVersionSpecifiedError()

        # Initialize values from sources (conf)
        current_version: str = self.config.settings["version"]

        tag_format: str = self.bump_settings["tag_format"]
        bump_commit_message: str = self.bump_settings["bump_message"]
        version_files: List[str] = self.bump_settings["version_files"]

        dry_run: bool = self.arguments["dry_run"]
        is_yes: bool = self.arguments["yes"]
        increment: Optional[str] = self.arguments["increment"]
        prerelease: str = self.arguments["prerelease"]
        is_files_only: Optional[bool] = self.arguments["files_only"]
        is_local_version: Optional[bool] = self.arguments["local_version"]

        current_tag_version: str = bump.create_tag(
            current_version, tag_format=tag_format
        )

        is_initial = self.is_initial_tag(current_tag_version, is_yes)
        if is_initial:
            commits = git.get_commits()
        else:
            commits = git.get_commits(current_tag_version)

        # No commits, there is no need to create an empty tag.
        # Unless we previously had a prerelease.
        if not commits and not current_version_instance.is_prerelease:
            raise NoCommitsFoundError("[NO_COMMITS_FOUND]\n" "No new commits found.")

        if increment is None:
            increment = self.find_increment(commits)

        # Increment is removed when current and next version
        # are expected to be prereleases.
        if prerelease and current_version_instance.is_prerelease:
            increment = None

        new_version = bump.generate_version(
            current_version,
            increment,
            prerelease=prerelease,
            is_local_version=is_local_version,
        )
        new_tag_version = bump.create_tag(new_version, tag_format=tag_format)
        message = bump.create_commit_message(
            current_version, new_version, bump_commit_message
        )

        # Report found information
        out.write(
            f"{message}\n"
            f"tag to create: {new_tag_version}\n"
            f"increment detected: {increment}\n"
        )

        if increment is None and new_tag_version == current_tag_version:
            raise NoneIncrementExit()

        # Do not perform operations over files or git.
        if dry_run:
            raise DryRunExit()

        bump.update_version_in_files(
            current_version,
            str(new_version),
            version_files,
            check_consistency=self.check_consistency,
        )

        if self.changelog:
            changelog_cmd = Changelog(
                self.config,
                {
                    "unreleased_version": new_tag_version,
                    "incremental": True,
                    "dry_run": dry_run,
                },
            )
            changelog_cmd()
            c = cmd.run(f"git add {changelog_cmd.file_name}")

        self.config.set_key("version", str(new_version))

        if is_files_only:
            raise ExpectedExit()

        c = git.commit(message, args=self._get_commit_args())
        if c.return_code != 0:
            raise BumpCommitFailedError(f'git.commit error: "{c.err.strip()}"')
        c = git.tag(new_tag_version)
        if c.return_code != 0:
            raise BumpTagFailedError(c.err)
        out.success("Done!")
예제 #22
0
파일: git.py 프로젝트: jordanSu/commitizen
def tag(tag: str, annotated: bool = False):
    c = cmd.run(
        f"git tag -a {tag} -m {tag}" if annotated else f"git tag {tag}")
    return c
예제 #23
0
파일: git.py 프로젝트: jordanSu/commitizen
def is_git_project() -> bool:
    c = cmd.run("git rev-parse --is-inside-work-tree")
    if c.out.strip() == "true":
        return True
    return False
예제 #24
0
파일: git.py 프로젝트: jordanSu/commitizen
def is_staging_clean() -> bool:
    """Check if staing is clean."""
    c = cmd.run("git diff --no-ext-diff --name-only")
    c_cached = cmd.run("git diff --no-ext-diff --cached --name-only")
    return not (bool(c.out) or bool(c_cached.out))
예제 #25
0
파일: git.py 프로젝트: jordanSu/commitizen
def find_git_project_root() -> Optional[Path]:
    c = cmd.run("git rev-parse --show-toplevel")
    if not c.err:
        return Path(c.out.strip())
    return None
예제 #26
0
파일: git.py 프로젝트: jordanSu/commitizen
def get_tag_names() -> List[Optional[str]]:
    c = cmd.run("git tag --list")
    if c.err:
        return []
    return [tag.strip() for tag in c.out.split("\n") if tag.strip()]
예제 #27
0
파일: git.py 프로젝트: jordanSu/commitizen
def get_latest_tag_name() -> Optional[str]:
    c = cmd.run("git describe --abbrev=0 --tags")
    if c.err:
        return None
    return c.out.strip()
예제 #28
0
파일: git.py 프로젝트: jordanSu/commitizen
def tag_exist(tag: str) -> bool:
    c = cmd.run(f"git tag --list {tag}")
    return tag in c.out
예제 #29
0
def tmp_git_project(tmpdir):
    with tmpdir.as_cwd():
        cmd.run("git init")

        yield tmpdir
예제 #30
0
파일: git.py 프로젝트: esciara/commitizen
def tag(tag: str):
    c = cmd.run(f"git tag {tag}")
    return c