Skip to content

Commit 2b81c8c

Browse files
nicoleman0Lee-Wbearomorphism
authored
fix(config): add warning for multiple configuration files and update documentation (#1773)
* feat(config): add warning for multiple configuration files Detects when multiple configuration files exist (excluding pyproject.toml) and displays a warning message identifying which file is being used. Closes #1771 * refactor: simplify multiple config files warning Remove extra steps to check if config files are empty. Just warn about multiple files and show first file from the list. * refactor(config): use walrus operator for git_project_root assignment Co-authored-by: Wei Lee <weilee.rx@gmail.com> * fix(config): only check for multiple configs when filepath is None Co-authored-by: Tim Hsiung <bear890707@gmail.com> * fix(config): improve warning message clarity for multiple config files Co-authored-by: Tim Hsiung <bear890707@gmail.com> * refactor(config): remove _check_and_warn_multiple_configs function; refactor read_cfg function * fix(config): streamline multiple config files warning logic --------- Co-authored-by: Wei Lee <weilee.rx@gmail.com> Co-authored-by: Tim Hsiung <bear890707@gmail.com>
1 parent b86b172 commit 2b81c8c

File tree

3 files changed

+102
-2
lines changed

3 files changed

+102
-2
lines changed

commitizen/config/__init__.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from pathlib import Path
44
from typing import TYPE_CHECKING
55

6-
from commitizen import defaults, git
6+
from commitizen import defaults, git, out
77
from commitizen.config.factory import create_config
88
from commitizen.exceptions import ConfigFileIsEmpty, ConfigFileNotFound
99

@@ -35,7 +35,20 @@ def _resolve_config_paths(filepath: str | None = None) -> Generator[Path, None,
3535

3636

3737
def read_cfg(filepath: str | None = None) -> BaseConfig:
38-
for filename in _resolve_config_paths(filepath):
38+
config_candidates = list(_resolve_config_paths(filepath))
39+
40+
# Check for multiple config files and warn the user
41+
config_candidates_exclude_pyproject = [
42+
path for path in config_candidates if path.name != "pyproject.toml"
43+
]
44+
if len(config_candidates_exclude_pyproject) > 1:
45+
filenames = [path.name for path in config_candidates_exclude_pyproject]
46+
out.warn(
47+
f"Multiple config files detected: {', '.join(filenames)}. "
48+
f"Using config file: '{filenames[0]}'."
49+
)
50+
51+
for filename in config_candidates:
3952
with open(filename, "rb") as f:
4053
data: bytes = f.read()
4154

docs/config/configuration_file.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ The first valid configuration file found will be used. If no configuration file
3131
!!! tip
3232
For Python projects, it's recommended to add your Commitizen configuration to `pyproject.toml` to keep all project configuration in one place.
3333

34+
!!! warning "Multiple Configuration Files"
35+
If Commitizen detects more than one configuration file in your project directory (excluding `pyproject.toml`), it will display a warning message and identify which file is being used. To avoid confusion, ensure you have only one Commitizen configuration file in your project.
36+
3437
## Supported Formats
3538

3639
Commitizen supports three configuration file formats:

tests/test_conf.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,90 @@ def test_load_empty_pyproject_toml_from_config_argument(_, tmpdir):
239239
with pytest.raises(ConfigFileIsEmpty):
240240
config.read_cfg(filepath="./not_in_root/pyproject.toml")
241241

242+
def test_warn_multiple_config_files(_, tmpdir, capsys):
243+
"""Test that a warning is issued when multiple config files exist."""
244+
with tmpdir.as_cwd():
245+
# Create multiple config files
246+
tmpdir.join(".cz.toml").write(PYPROJECT)
247+
tmpdir.join(".cz.json").write(JSON_STR)
248+
249+
# Read config
250+
cfg = config.read_cfg()
251+
252+
# Check that the warning was issued
253+
captured = capsys.readouterr()
254+
assert "Multiple config files detected" in captured.err
255+
assert ".cz.toml" in captured.err
256+
assert ".cz.json" in captured.err
257+
assert "Using" in captured.err
258+
259+
# Verify the correct config is loaded (first in priority order)
260+
assert cfg.settings == _settings
261+
262+
def test_warn_multiple_config_files_with_pyproject(_, tmpdir, capsys):
263+
"""Test warning excludes pyproject.toml from the warning message."""
264+
with tmpdir.as_cwd():
265+
# Create multiple config files including pyproject.toml
266+
tmpdir.join("pyproject.toml").write(PYPROJECT)
267+
tmpdir.join(".cz.json").write(JSON_STR)
268+
269+
# Read config - should use pyproject.toml (first in priority)
270+
cfg = config.read_cfg()
271+
272+
# No warning should be issued as only one non-pyproject config exists
273+
captured = capsys.readouterr()
274+
assert "Multiple config files detected" not in captured.err
275+
276+
# Verify the correct config is loaded
277+
assert cfg.settings == _settings
278+
279+
def test_warn_multiple_config_files_uses_correct_one(_, tmpdir, capsys):
280+
"""Test that the correct config file is used when multiple exist."""
281+
with tmpdir.as_cwd():
282+
# Create .cz.json with different settings
283+
json_different = """
284+
{
285+
"commitizen": {
286+
"name": "cz_conventional_commits",
287+
"version": "2.0.0"
288+
}
289+
}
290+
"""
291+
tmpdir.join(".cz.json").write(json_different)
292+
tmpdir.join(".cz.toml").write(PYPROJECT)
293+
294+
# Read config - should use pyproject.toml (first in defaults.CONFIG_FILES)
295+
# But since pyproject.toml doesn't exist, .cz.toml is second in priority
296+
cfg = config.read_cfg()
297+
298+
# Check that warning mentions both files
299+
captured = capsys.readouterr()
300+
assert "Multiple config files detected" in captured.err
301+
assert ".cz.toml" in captured.err
302+
assert ".cz.json" in captured.err
303+
304+
# Verify .cz.toml was used (second in priority after pyproject.toml)
305+
assert cfg.settings["name"] == "cz_jira" # from PYPROJECT
306+
assert cfg.settings["version"] == "1.0.0"
307+
308+
def test_no_warn_with_explicit_config_path(_, tmpdir, capsys):
309+
"""Test that no warning is issued when user explicitly specifies config."""
310+
with tmpdir.as_cwd():
311+
# Create multiple config files
312+
tmpdir.join(".cz.toml").write(PYPROJECT)
313+
tmpdir.join(".cz.json").write(JSON_STR)
314+
315+
# Read config with explicit path
316+
cfg = config.read_cfg(filepath=".cz.json")
317+
318+
# No warning should be issued
319+
captured = capsys.readouterr()
320+
assert "Multiple config files detected" not in captured.err
321+
322+
# Verify the explicitly specified config is loaded (compare to expected JSON config)
323+
json_cfg_expected = JsonConfig(data=JSON_STR, path=Path(".cz.json"))
324+
assert cfg.settings == json_cfg_expected.settings
325+
242326

243327
@pytest.mark.parametrize(
244328
"config_file, exception_string",

0 commit comments

Comments
 (0)