예제 #1
0
def main():
    conf = config.read_cfg()
    parser = cli(data)

    # Show help if no arg provided
    if len(sys.argv) == 1:
        parser.print_help(sys.stderr)
        raise SystemExit()

    try:
        args = parser.parse_args()
    except TypeError:
        out.error("Command is required")
        raise SystemExit()

    if args.name:
        conf.update({"name": args.name})

    if args.version:
        warnings.warn(
            "'cz --version' will be deprecated in next major version. "
            "Please use 'cz version' command from your scripts")
        logging.getLogger("commitizen").setLevel(logging.DEBUG)

    if args.debug:
        warnings.warn("Debug will be deprecated in next major version. "
                      "Please remove it from your scripts")
        logging.getLogger("commitizen").setLevel(logging.DEBUG)

    # TODO: This try block can be removed after command is required in 2.0
    try:
        args.func(conf, vars(args))()
    except AttributeError:
        out.error("Command is required")
예제 #2
0
    def _ask_tag(self) -> str:
        latest_tag = get_latest_tag_name()
        if not latest_tag:
            out.error("No Existing Tag. Set tag to v0.0.1")
            return "0.0.1"

        is_correct_tag = questionary.confirm(
            f"Is {latest_tag} the latest tag?",
            style=self.cz.style,
            default=False).ask()
        if not is_correct_tag:
            tags = get_tag_names()
            if not tags:
                out.error("No Existing Tag. Set tag to v0.0.1")
                return "0.0.1"

            latest_tag = questionary.select(
                "Please choose the latest tag: ",
                choices=get_tag_names(),  # type: ignore
                style=self.cz.style,
            ).ask()

            if not latest_tag:
                raise NoAnswersError("Tag is required!")
        return latest_tag
예제 #3
0
 def _valid_command_argument(self):
     if bool(self.commit_msg_file) is bool(self.rev_range):
         out.error(
             (
                 "One and only one argument is required for check command! "
                 "See 'cz check -h' for more information"
             )
         )
         raise SystemExit()
예제 #4
0
파일: commit.py 프로젝트: modlin/commitizen
    def read_backup_message(self) -> str:
        # Check the commit backup file exists
        if not os.path.isfile(self.temp_file):
            out.error("No commit backup found")
            raise SystemExit(NO_COMMIT_BACKUP)

        # Read commit message from backup
        with open(self.temp_file, "r") as f:
            return f.read().strip()
예제 #5
0
파일: bump.py 프로젝트: multimac/commitizen
 def find_increment(self, commits: List[git.GitCommit]) -> Optional[str]:
     bump_pattern = self.cz.bump_pattern
     bump_map = self.cz.bump_map
     if not bump_map or not bump_pattern:
         out.error(f"'{self.config.settings['name']}' rule does not support bump")
         raise SystemExit(NO_PATTERN_MAP)
     increment = bump.find_increment(
         commits, regex=bump_pattern, increments_map=bump_map
     )
     return increment
예제 #6
0
def commiter_factory(config: BaseConfig) -> BaseCommitizen:
    """Return the correct commitizen existing in the registry."""
    name: str = config.settings["name"]
    try:
        _cz = registry[name](config)
    except KeyError:
        msg_error = ("The committer has not been found in the system.\n\n"
                     f"Try running 'pip install {name}'\n")
        out.error(msg_error)
        raise SystemExit(NO_COMMITIZEN_FOUND)
    else:
        return _cz
예제 #7
0
    def __call__(self):
        commit_parser = self.cz.commit_parser
        changelog_pattern = self.cz.changelog_pattern
        start_rev = self.start_rev
        unreleased_version = self.unreleased_version
        changelog_meta: Dict = {}

        if not changelog_pattern or not commit_parser:
            out.error(
                f"'{self.config.settings['name']}' rule does not support changelog"
            )
            raise SystemExit(NO_PATTERN_MAP)

        tags = git.get_tags()
        if not tags:
            tags = []

        if self.incremental:
            changelog_meta = changelog.get_metadata(self.file_name)
            latest_version = changelog_meta.get("latest_version")
            if latest_version:
                start_rev = self._find_incremental_rev(latest_version, tags)

        commits = git.get_commits(start=start_rev, args="--author-date-order")
        if not commits:
            out.error("No commits found")
            raise SystemExit(NO_COMMITS_FOUND)

        tree = changelog.generate_tree_from_commits(commits, tags,
                                                    commit_parser,
                                                    changelog_pattern,
                                                    unreleased_version)
        changelog_out = changelog.render_changelog(tree)

        if self.dry_run:
            out.write(changelog_out)
            raise SystemExit(0)

        lines = []
        if self.incremental and os.path.isfile(self.file_name):
            with open(self.file_name, "r") as changelog_file:
                lines = changelog_file.readlines()

        with open(self.file_name, "w") as changelog_file:
            if self.incremental:
                new_lines = changelog.incremental_build(
                    changelog_out, lines, changelog_meta)
                changelog_file.writelines(new_lines)
            else:
                changelog_file.write(changelog_out)
예제 #8
0
    def __init__(self, config: BaseConfig):
        super(CustomizeCommitsCz, self).__init__(config)

        if "customize" not in self.config.settings:
            out.error("fatal: customize is not set in configuration file.")
            raise SystemExit(MISSING_CONFIG)
        self.custom_settings = self.config.settings["customize"]

        custom_bump_pattern = self.custom_settings.get("bump_pattern")
        if custom_bump_pattern:
            self.bump_pattern = custom_bump_pattern

        custom_bump_map = self.custom_settings.get("bump_map")
        if custom_bump_map:
            self.bump_map = custom_bump_map
예제 #9
0
파일: commit.py 프로젝트: modlin/commitizen
    def prompt_commit_questions(self) -> str:
        # Prompt user for the commit message
        cz = self.cz
        questions = cz.questions()
        try:
            answers = questionary.prompt(questions, style=cz.style)
        except ValueError as err:
            root_err = err.__context__
            if isinstance(root_err, CzException):
                out.error(root_err.__str__())
                raise SystemExit(CUSTOM_ERROR)
            raise err

        if not answers:
            raise SystemExit(NO_ANSWERS)
        return cz.message(answers)
예제 #10
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")
예제 #11
0
 def __call__(self):
     if self.parameter.get("project"):
         version = self.config.settings["version"]
         if version:
             out.write(f"{version}")
         else:
             out.error(f"No project information in this project.")
     elif self.parameter.get("verbose"):
         out.write(f"Installed Commitizen Version: {__version__}")
         version = self.config.settings["version"]
         if version:
             out.write(f"Project Version: {version}")
         else:
             out.error(f"No project information in this project.")
     else:
         # if no argument is given, show installed commitizen version
         out.write(f"{__version__}")
예제 #12
0
    def __call__(self):
        """Validate if a commit message follows the conventional pattern.

        Raises
        ------
        SystemExit
            if the commit provided not follows the conventional pattern

        """
        commit_msg_content = self._get_commit_msg()
        if self._is_conventional(PATTERN, commit_msg_content) is not None:
            out.success("Conventional commit validation: successful!")
        else:
            out.error("conventional commit validation: failed!")
            out.error(
                "please enter a commit message in the conventional format.")
            raise SystemExit(INVALID_COMMIT_MSG)
예제 #13
0
def update_version_in_files(current_version: str,
                            new_version: str,
                            files: List[str],
                            *,
                            check_consistency=False):
    """Change old version to the new one in every file given.

    Note that this version is not the tag formatted one.
    So for example, your tag could look like `v1.0.0` while your version in
    the package like `1.0.0`.
    """
    # TODO: separate check step and write step
    for location in files:
        filepath, *regexes = location.split(":", maxsplit=1)
        regex = regexes[0] if regexes else None

        # Read in the file
        file_content = []
        current_version_found = False
        with open(filepath, "r") as version_file:
            for line in version_file:
                if regex:
                    match = re.search(regex, line)
                    if not match:
                        file_content.append(line)
                        continue

                # Replace the target string
                if current_version in line:
                    current_version_found = True
                    file_content.append(
                        line.replace(current_version, new_version))
                else:
                    file_content.append(line)

        if check_consistency and not current_version_found:
            out.error(
                f"Current version {current_version} is not found in {location}.\n"
                "The version defined in commitizen configuration and the ones in "
                "version_files are possibly inconsistent.")
            raise SystemExit(CURRENT_VERSION_NOT_FOUND)

        # Write the file out again
        with open(filepath, "w") as file:
            file.write("".join(file_content))
예제 #14
0
def read_cfg() -> BaseConfig:
    conf = BaseConfig()

    git_project_root = git.find_git_project_root()
    if not git_project_root:
        out.error(
            "fatal: not a git repository (or any of the parent directories): .git"
        )
        raise SystemExit(NOT_A_GIT_PROJECT)

    allowed_cfg_files = defaults.config_files
    cfg_paths = (
        path / Path(filename)
        for path in [Path("."), git_project_root]
        for filename in allowed_cfg_files
    )
    for filename in cfg_paths:
        if not filename.exists():
            continue

        with open(filename, "r") as f:
            data: str = f.read()

        if "toml" in filename.suffix:
            _conf = TomlConfig(data=data, path=filename)
        else:
            warnings.warn(
                ".cz, setup.cfg, and .cz.cfg will be deprecated "
                "in next major version. \n"
                'Please use "pyproject.toml", ".cz.toml" instead'
            )
            _conf = IniConfig(data=data, path=filename)

        if _conf.is_empty_config:
            continue
        else:
            conf = _conf
            break

    if not conf.path:
        global_conf = load_global_conf()
        if global_conf:
            conf = global_conf

    return conf
예제 #15
0
def main():
    conf = config.read_cfg()
    parser = cli(data)

    # Show help if no arg provided
    if len(sys.argv) == 1:
        parser.print_help(sys.stderr)
        raise SystemExit()

    # This is for the command required constraint in 2.0
    try:
        args = parser.parse_args()
    except TypeError:
        out.error("Command is required")
        raise SystemExit()

    if args.name:
        conf.update({"name": args.name})
    elif not args.name and not conf.path:
        conf.update({"name": "cz_conventional_commits"})

    if args.version:
        warnings.warn(
            ("'cz --version' will be deprecated in next major version. "
             "Please use 'cz version' command from your scripts"),
            category=DeprecationWarning,
        )
        args.func = commands.Version

    if args.debug:
        warnings.warn(
            ("Debug will be deprecated in next major version. "
             "Please remove it from your scripts"),
            category=DeprecationWarning,
        )
        logging.getLogger("commitizen").setLevel(logging.DEBUG)

    # TODO: This try block can be removed after command is required in 2.0
    # Handle the case that argument is given, but no command is provided
    try:
        args.func(conf, vars(args))()
    except AttributeError:
        out.error("Command is required")
        raise SystemExit()
예제 #16
0
파일: commit.py 프로젝트: saygox/commitizen
    def __call__(self):
        dry_run: bool = self.arguments.get("dry_run")

        if git.is_staging_clean() and not dry_run:
            raise NothingToCommitError("No files added to staging!")

        retry: bool = self.arguments.get("retry")

        if retry:
            m = self.read_backup_message()
        else:
            m = self.prompt_commit_questions()

        out.info(f"\n{m}\n")

        if dry_run:
            raise DryRunExit()

        signoff: bool = self.arguments.get("signoff")

        if signoff:
            c = git.commit(m, "-s")
        else:
            c = git.commit(m)

        if c.return_code != 0:
            out.error(c.err)

            # Create commit backup
            with open(self.temp_file, "w") as f:
                f.write(m)

            raise CommitError()

        if "nothing added" in c.out or "no changes added to commit" in c.out:
            out.error(c.out)
        else:
            with contextlib.suppress(FileNotFoundError):
                os.remove(self.temp_file)
            out.write(c.err)
            out.write(c.out)
            out.success("Commit successful!")
예제 #17
0
    def __call__(self):
        """Validate if commit messages follows the conventional pattern.

        Raises
        ------
        SystemExit
            if the commit provided not follows the conventional pattern

        """
        commit_msgs = self._get_commit_messages()
        pattern = self.cz.schema_pattern()
        for commit_msg in commit_msgs:
            if not Check.validate_commit_message(commit_msg, pattern):
                out.error(
                    "commit validation: failed!\n"
                    "please enter a commit message in the commitizen format.\n"
                    f"commit: {commit_msg}\n"
                    f"pattern: {pattern}")
                raise SystemExit(INVALID_COMMIT_MSG)
        out.success("Commit validation: successful!")
예제 #18
0
파일: commit.py 프로젝트: modlin/commitizen
    def __call__(self):
        if git.is_staging_clean():
            out.write("No files added to staging!")
            raise SystemExit(NOTHING_TO_COMMIT)

        retry: bool = self.arguments.get("retry")

        if retry:
            m = self.read_backup_message()
        else:
            m = self.prompt_commit_questions()

        out.info(f"\n{m}\n")
        c = git.commit(m)

        if c.err:
            out.error(c.err)

            # Create commit backup
            with open(self.temp_file, "w") as f:
                f.write(m)

            raise SystemExit(COMMIT_ERROR)

        if "nothing added" in c.out or "no changes added to commit" in c.out:
            out.error(c.out)
        elif c.err:
            out.error(c.err)
        else:
            with contextlib.suppress(FileNotFoundError):
                os.remove(self.temp_file)
            out.write(c.out)
            out.success("Commit successful!")
예제 #19
0
def read_cfg() -> BaseConfig:
    conf = BaseConfig()

    git_project_root = git.find_git_project_root()
    if not git_project_root:
        out.error(
            "fatal: not a git repository (or any of the parent directories): .git"
        )
        raise SystemExit(NOT_A_GIT_PROJECT)

    cfg_paths = (
        path / Path(filename)
        for path in [Path("."), git_project_root]
        for filename in defaults.config_files
    )
    for filename in cfg_paths:
        if not filename.exists():
            continue

        with open(filename, "r") as f:
            data: str = f.read()

        _conf: Union[TomlConfig, IniConfig]
        if "toml" in filename.suffix:
            _conf = TomlConfig(data=data, path=filename)
        else:
            _conf = IniConfig(data=data, path=filename)

        if _conf.is_empty_config:
            continue
        else:
            conf = _conf
            break

    if not conf.path:
        global_conf = load_global_conf()
        if global_conf:
            conf = global_conf

    return conf
예제 #20
0
    def __call__(self):
        cz = self.cz
        questions = cz.questions()
        answers = questionary.prompt(questions)
        if not answers:
            raise SystemExit(NO_ANSWERS)
        m = cz.message(answers)
        out.info(f"\n{m}\n")
        c = git.commit(m)

        if c.err:
            out.error(c.err)
            raise SystemExit(COMMIT_ERROR)

        if "nothing added" in c.out or "no changes added to commit" in c.out:
            out.error(c.out)
        elif c.err:
            out.error(c.err)
        else:
            out.write(c.out)
            out.success("Commit successful!")
예제 #21
0
파일: bump.py 프로젝트: multimac/commitizen
    def __call__(self):  # noqa: C901
        """Steps executed to bump."""
        try:
            current_version_instance: Version = Version(self.bump_settings["version"])
        except TypeError:
            out.error(
                "[NO_VERSION_SPECIFIED]\n"
                "Check if current version is specified in config file, like:\n"
                "version = 0.4.3\n"
            )
            raise SystemExit(NO_VERSION_SPECIFIED)

        # 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"]

        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:
            out.error("[NO_COMMITS_FOUND]\n" "No new commits found.")
            raise SystemExit(NO_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
        )
        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"
        )

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

        bump.update_version_in_files(
            current_version,
            new_version.public,
            version_files,
            check_consistency=self.check_consistency,
        )
        if is_files_only:
            raise SystemExit()

        if self.changelog:
            changelog = Changelog(
                self.config,
                {
                    "unreleased_version": new_tag_version,
                    "incremental": True,
                    "dry_run": dry_run,
                },
            )
            changelog()

        self.config.set_key("version", new_version.public)
        c = git.commit(message, args=self._get_commit_args())
        if c.err:
            out.error('git.commit error: "{}"'.format(c.err.strip()))
            raise SystemExit(COMMIT_FAILED)
        c = git.tag(new_tag_version)
        if c.err:
            out.error(c.err)
            raise SystemExit(TAG_FAILED)
        out.success("Done!")
예제 #22
0
    def __call__(self):
        """Steps executed to bump."""
        try:
            current_version_instance: Version = Version(self.parameters["version"])
        except TypeError:
            out.error("[NO_VERSION_SPECIFIED]")
            out.error("Check if current version is specified in config file, like:")
            out.error("version = 0.4.3")
            raise SystemExit(NO_VERSION_SPECIFIED)

        # Initialize values from sources (conf)
        current_version: str = self.config["version"]
        tag_format: str = self.parameters["tag_format"]
        bump_commit_message: str = self.parameters["bump_message"]
        current_tag_version: str = bump.create_tag(
            current_version, tag_format=tag_format
        )
        files: list = self.parameters["files"]
        dry_run: bool = self.parameters["dry_run"]

        is_yes: bool = self.arguments["yes"]
        prerelease: str = self.arguments["prerelease"]
        increment: Optional[str] = self.arguments["increment"]

        is_initial = self.is_initial_tag(current_tag_version, is_yes)
        commits = git.get_commits(current_tag_version, from_beginning=is_initial)

        # 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:
            out.error("[NO_COMMITS_FOUND]")
            out.error("No new commits found.")
            raise SystemExit(NO_COMMITS_FOUND)

        if increment is None:
            bump_pattern = self.cz.bump_pattern
            bump_map = self.cz.bump_map
            if not bump_map or not bump_pattern:
                out.error(f"'{self.config['name']}' rule does not support bump")
                raise SystemExit(NO_PATTERN_MAP)
            increment = bump.find_increment(
                commits, regex=bump_pattern, increments_map=bump_map
            )

        # 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
        )
        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(message)
        out.write(f"tag to create: {new_tag_version}")
        out.write(f"increment detected: {increment}")

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

        config.set_key("version", new_version.public)
        bump.update_version_in_files(current_version, new_version.public, files)
        c = git.commit(message, args="-a")
        if c.err:
            out.error(c.err)
            raise SystemExit(COMMIT_FAILED)
        c = git.tag(new_tag_version)
        if c.err:
            out.error(c.err)
            raise SystemExit(TAG_FAILED)
        out.success("Done!")
예제 #23
0
    def __call__(self):
        commit_parser = self.cz.commit_parser
        changelog_pattern = self.cz.changelog_pattern
        start_rev = self.start_rev
        unreleased_version = self.unreleased_version
        changelog_meta: Dict = {}
        change_type_map: Optional[Dict] = self.change_type_map
        changelog_message_builder_hook: Optional[
            Callable] = self.cz.changelog_message_builder_hook
        changelog_hook: Optional[Callable] = self.cz.changelog_hook

        if not changelog_pattern or not commit_parser:
            out.error(
                f"'{self.config.settings['name']}' rule does not support changelog"
            )
            raise SystemExit(NO_PATTERN_MAP)

        tags = git.get_tags()
        if not tags:
            tags = []

        if self.incremental:
            changelog_meta = changelog.get_metadata(self.file_name)
            latest_version = changelog_meta.get("latest_version")
            if latest_version:
                start_rev = self._find_incremental_rev(latest_version, tags)

        commits = git.get_commits(start=start_rev, args="--author-date-order")
        if not commits:
            out.error("No commits found")
            raise SystemExit(NO_COMMITS_FOUND)

        tree = changelog.generate_tree_from_commits(
            commits,
            tags,
            commit_parser,
            changelog_pattern,
            unreleased_version,
            change_type_map=change_type_map,
            changelog_message_builder_hook=changelog_message_builder_hook,
        )
        changelog_out = changelog.render_changelog(tree)
        changelog_out = changelog_out.lstrip("\n")

        if self.dry_run:
            out.write(changelog_out)
            raise SystemExit(0)

        lines = []
        if self.incremental and os.path.isfile(self.file_name):
            with open(self.file_name, "r") as changelog_file:
                lines = changelog_file.readlines()

        with open(self.file_name, "w") as changelog_file:
            partial_changelog: Optional[str] = None
            if self.incremental:
                new_lines = changelog.incremental_build(
                    changelog_out, lines, changelog_meta)
                changelog_out = "".join(new_lines)
                partial_changelog = changelog_out

            if changelog_hook:
                changelog_out = changelog_hook(changelog_out,
                                               partial_changelog)
            changelog_file.write(changelog_out)