diff --git a/commitizen/config/__init__.py b/commitizen/config/__init__.py index cce3a266c..ae980af9c 100644 --- a/commitizen/config/__init__.py +++ b/commitizen/config/__init__.py @@ -9,7 +9,7 @@ from .base_config import BaseConfig -def _resolve_config_paths() -> list[Path]: +def _resolve_config_candidates() -> list[BaseConfig]: git_project_root = git.find_git_project_root() cfg_search_paths = [Path(".")] @@ -18,12 +18,18 @@ def _resolve_config_paths() -> list[Path]: # The following algorithm is ugly, but we need to ensure that the order of the candidates are preserved before v5. # Also, the number of possible config files is limited, so the complexity is not a problem. - candidates: list[Path] = [] + candidates: list[BaseConfig] = [] for dir in cfg_search_paths: for filename in defaults.CONFIG_FILES: out_path = dir / Path(filename) - if out_path.exists() and all(not out_path.samefile(p) for p in candidates): - candidates.append(out_path) + if ( + out_path.exists() + and not any( + out_path.samefile(candidate.path) for candidate in candidates + ) + and not (conf := _create_config_from_path(out_path)).is_empty_config + ): + candidates.append(conf) return candidates @@ -44,21 +50,11 @@ def read_cfg(filepath: str | None = None) -> BaseConfig: raise ConfigFileIsEmpty() return conf - config_candidate_paths = _resolve_config_paths() - - # Check for multiple config files and warn the user - config_candidates_exclude_pyproject = [ - path for path in config_candidate_paths if path.name != "pyproject.toml" - ] - - for config_candidate_path in config_candidate_paths: - conf = _create_config_from_path(config_candidate_path) - if not conf.is_empty_config: - if len(config_candidates_exclude_pyproject) > 1: - out.warn( - f"Multiple config files detected: {', '.join(map(str, config_candidates_exclude_pyproject))}. " - f"Using config file: '{config_candidate_path}'." - ) - return conf + config_candidates = _resolve_config_candidates() + if len(config_candidates) > 1: + out.warn( + f"Multiple config files detected: {', '.join(str(conf.path) for conf in config_candidates)}. " + f"Using config file: '{config_candidates[0].path}'." + ) - return BaseConfig() + return config_candidates[0] if config_candidates else BaseConfig() diff --git a/tests/test_conf.py b/tests/test_conf.py index b41ead069..cad805859 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -14,7 +14,7 @@ from commitizen.config.yaml_config import YAMLConfig from commitizen.exceptions import ConfigFileIsEmpty, InvalidConfigurationError -PYPROJECT = """ +TOML_STR = """ [tool.commitizen] name = "cz_jira" version = "1.0.0" @@ -30,11 +30,17 @@ "scripts/generate_documentation.sh" ] post_bump_hooks = ["scripts/slack_notification.sh"] +""" +PYPROJECT = ( + TOML_STR + + """ [tool.black] line-length = 88 target-version = ['py36', 'py37', 'py38'] """ +) + DICT_CONFIG = { "commitizen": { @@ -198,7 +204,7 @@ def test_load_empty_pyproject_toml_and_cz_toml_with_config(_, tmpdir): p = tmpdir.join("pyproject.toml") p.write("") p = tmpdir.join(".cz.toml") - p.write(PYPROJECT) + p.write(TOML_STR) cfg = config.read_cfg() assert cfg.settings == _settings @@ -240,27 +246,25 @@ def test_load_empty_pyproject_toml_from_config_argument(_, tmpdir): class TestWarnMultipleConfigFiles: @pytest.mark.parametrize( - "files,expected_path,should_warn", + "files,expected_path", [ # Same directory, different file types - ([(".cz.toml", PYPROJECT), (".cz.json", JSON_STR)], ".cz.toml", True), - ([(".cz.json", JSON_STR), (".cz.yaml", YAML_STR)], ".cz.json", True), - ([(".cz.toml", PYPROJECT), (".cz.yaml", YAML_STR)], ".cz.toml", True), - # With pyproject.toml (excluded from warning) + ([(".cz.toml", TOML_STR), (".cz.json", JSON_STR)], ".cz.toml"), + ([(".cz.json", JSON_STR), (".cz.yaml", YAML_STR)], ".cz.json"), + ([(".cz.toml", TOML_STR), (".cz.yaml", YAML_STR)], ".cz.toml"), + # With pyproject.toml ( [("pyproject.toml", PYPROJECT), (".cz.json", JSON_STR)], ".cz.json", - False, ), ( - [("pyproject.toml", PYPROJECT), (".cz.toml", PYPROJECT)], + [("pyproject.toml", PYPROJECT), (".cz.toml", TOML_STR)], ".cz.toml", - False, ), ], ) def test_warn_multiple_config_files_same_dir( - _, tmpdir, capsys, files, expected_path, should_warn + _, tmpdir, capsys, files, expected_path ): """Test warning when multiple config files exist in same directory.""" with tmpdir.as_cwd(): @@ -270,27 +274,20 @@ def test_warn_multiple_config_files_same_dir( cfg = config.read_cfg() captured = capsys.readouterr() - if should_warn: - assert "Multiple config files detected" in captured.err - assert "Using" in captured.err - for filename, _ in files: - if filename != "pyproject.toml": - assert filename in captured.err - else: - assert "Multiple config files detected" not in captured.err + assert "Multiple config files detected" in captured.err + for filename, _ in files: + assert filename in captured.err + assert f"Using config file: '{expected_path}'" in captured.err assert cfg.path == Path(expected_path) - # Verify config loaded correctly (name and version match expected) - assert cfg.settings["name"] == "cz_jira" - assert cfg.settings["version"] == "1.0.0" @pytest.mark.parametrize( "config_file,content", [ (".cz.json", JSON_STR), - (".cz.toml", PYPROJECT), + (".cz.toml", TOML_STR), (".cz.yaml", YAML_STR), - ("cz.toml", PYPROJECT), + ("cz.toml", TOML_STR), ("cz.json", JSON_STR), ("cz.yaml", YAML_STR), ], @@ -340,11 +337,11 @@ def test_no_warn_with_explicit_config_path(_, tmpdir, capsys): [ (file, content, with_git) for file, content in [ - (".cz.toml", PYPROJECT), + (".cz.toml", TOML_STR), (".cz.json", JSON_STR), (".cz.yaml", YAML_STR), ("pyproject.toml", PYPROJECT), - ("cz.toml", PYPROJECT), + ("cz.toml", TOML_STR), ("cz.json", JSON_STR), ("cz.yaml", YAML_STR), ] @@ -368,6 +365,18 @@ def test_no_warn_with_single_config_file( assert "Multiple config files detected" not in captured.err assert cfg.path == Path(config_file) + def test_no_warn_with_no_commitizen_section_in_pyproject_toml_and_cz_toml( + _, tmpdir, capsys + ): + with tmpdir.as_cwd(): + tmpdir.join("pyproject.toml").write("[tool.foo]\nbar = 'baz'") + tmpdir.join(".cz.toml").write(TOML_STR) + + cfg = config.read_cfg() + captured = capsys.readouterr() + assert "Multiple config files detected" not in captured.err + assert cfg.path == Path(".cz.toml") + @pytest.mark.parametrize( "config_file, exception_string",