Skip to content

Conversation

@nicoleman0
Copy link

@nicoleman0 nicoleman0 commented Jan 5, 2026

Description

This PR implements a warning system that detects when multiple configuration files exist in the project directory (excluding pyproject.toml) and displays a warning message identifying which file is being used.

The implementation follows the consensus from issue #1702 where maintainers agreed that "we should have one and only one configuration file" and decided to add a warning when multiple config files are detected.

Checklist

Code Changes

  • Add test cases to all the changes you introduce
  • Run uv run poe all locally to ensure this change passes linter check and tests
  • Manually test the changes:
    • Verify the feature/bug fix works as expected in real-world scenarios
    • Test edge cases and error conditions
    • Ensure backward compatibility is maintained
    • Document any manual testing steps performed
  • Update the documentation for the changes

Documentation Changes

  • Run uv run poe doc locally to ensure the documentation pages renders correctly
  • Check and fix any broken links (internal or external) in the documentation

Expected Behavior

When multiple configuration files are detected (e.g., .cz.toml and .cz.json), commitizen will display a warning message like:

Multiple config files detected: .cz.toml, .cz.json. Using config file: '.cz.toml'.

  • The warning only appears when multiple config files exist (excluding pyproject.toml)
  • The warning lists all detected config files in the order they would be checked
  • The warning clearly indicates which file will be used (the first one in the priority order)
  • If a config file is explicitly specified via the --config flag, no warning is displayed
  • If only one config file exists (or only pyproject.toml plus one other), no warning is displayed
  • The file priority order follows the standard commitizen config file precedence.

Fixes #1771

@codecov
Copy link

codecov bot commented Jan 5, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.93%. Comparing base (63c529d) to head (9afa4f9).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1773      +/-   ##
==========================================
+ Coverage   97.88%   97.93%   +0.04%     
==========================================
  Files          60       60              
  Lines        2606     2611       +5     
==========================================
+ Hits         2551     2557       +6     
+ Misses         55       54       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Detects when multiple configuration files exist (excluding pyproject.toml)
and displays a warning message identifying which file is being used.

Closes commitizen-tools#1771
# If more than one config file exists, warn the user
if len(existing_files) > 1:
# Find which one will be used (first non-empty one in the priority order)
used_config = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should add the extra steps.
It should just warn that multiple files were detected.
And alternatively, because the list is already in the order that cz is going to pick it put, you could just get the file that it's going to be used by getting the first element of existing_files

Copy link
Author

@nicoleman0 nicoleman0 Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, yes that makes sense - I've simplified the implementation:

  • Removed the extra steps that were checking if each config file is empty
  • Now just warns about multiple files and uses existing_files[0] to show which file will be used

Changes have been pushed. Let me know if you need anything else.

Remove extra steps to check if config files are empty.
Just warn about multiple files and show first file from the list.
# Verify the correct config is loaded (first in priority order)
assert cfg.settings == _settings

def test_warn_multiple_config_files_with_pyproject(_, tmpdir, capsys):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But technically, it's multiple configs. We probably should handle it better 🤔

Copy link
Collaborator

@bearomorphism bearomorphism left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great but need some refactoring

@bearomorphism
Copy link
Collaborator

Could you add the expected behavior in the PR description? Thanks!

@nicoleman0
Copy link
Author

Could you add the expected behavior in the PR description? Thanks!

I just updated the PR with the expected behavior (sorry about that, completely forgot to add that part), and also refactored my code with the suggestions that you all gave.

If I missed anything, let me know again.

config_candidates = list(_resolve_config_paths(filepath))

# Check for multiple config files and warn the user
if filepath is None:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure whether if filepath is None: is still needed. We can perform the check for both cases anyway.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yes I see how it's redundant, I'll fix that!

f"Using config file: '{filenames[0]}'."
)

for filename in config_candidates:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This for loop looks strange... I think we should remove the for loop

using_config_file = config_candidates[0]
...

but that can be another PR

iirc the underlying logic of BaseConfig is a bit confusing

Copy link
Author

@nicoleman0 nicoleman0 Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so should the code just use config_candidates[0] directly with the first file found, whether it's empty or not?

If so, the behavior would change to:

  • When filepath is provided: Use that file, raise ConfigFileIsEmpty if empty (same as now)
  • When auto-discovering: Use the first file found, return empty BaseConfig() if it's empty (currently skips empty files)

This would be simpler and align the warning message with actual behavior. The only downside is if someone has an empty .cz.toml and a valid .cz.json, it would now ignore the .cz.json (whereas currently it would use it).

Shall I go ahead with this simplification in this PR, or would you prefer a separate one?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could just add a TODO to remind the future contributors to address this issue. The main goal for this PR is just to add a warning for multiple configuration.

if len(config_candidates_exclude_pyproject) > 1:
filenames = [path.name for path in config_candidates_exclude_pyproject]
out.warn(
f"Multiple config files detected: {', '.join(filenames)}. "
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not entirely sure if it's good to just list the filenames but not the full path. There might be files with the same file names but different paths.

filenames = [path.name for path in config_candidates_exclude_pyproject]
out.warn(
f"Multiple config files detected: {', '.join(filenames)}. "
f"Using config file: '{filenames[0]}'."
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if the used config file here will be the same as the returned config file around the line if not conf.is_empty_config:.

In other words, will the return value of this function always be config_candidates[0]?

@Lee-W @woile @noirbizarre wdyt

@bearomorphism
Copy link
Collaborator

Could you add the expected behavior in the PR description? Thanks!

I just updated the PR with the expected behavior (sorry about that, completely forgot to add that part), and also refactored my code with the suggestions that you all gave.

If I missed anything, let me know again.

@nicoleman0 you're doing it great! But the pipeline is failing because some of the commit messages does not follow the requirements. Could you fix the commit messages? Thanks!

@nicoleman0
Copy link
Author

Could you add the expected behavior in the PR description? Thanks!

I just updated the PR with the expected behavior (sorry about that, completely forgot to add that part), and also refactored my code with the suggestions that you all gave.
If I missed anything, let me know again.

@nicoleman0 you're doing it great! But the pipeline is failing because some of the commit messages does not follow the requirements. Could you fix the commit messages? Thanks!

@bearomorphism I believe I've fixed the generic commit messages and I also refactored the read_cfg function.

The redundant if filepath is None: check has been removed. The code now performs the multiple config file check in both cases, though it will only ever warn when filepath is None.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add warning if more than one configuration files are found and identify which one we're using

4 participants