diff --git a/commitizen/changelog_formats/__init__.py b/commitizen/changelog_formats/__init__.py index 26e697cad..d9f9cd079 100644 --- a/commitizen/changelog_formats/__init__.py +++ b/commitizen/changelog_formats/__init__.py @@ -67,17 +67,30 @@ def get_changelog_format( """ Get a format from its name - :raises FormatUnknown: if a non-empty name is provided but cannot be found in the known formats + :raises ChangelogFormatUnknown: if a non-empty name is provided but cannot + be found in the known formats, or if the filename's extension cannot + be matched to a known format and no ``changelog_format`` is set. """ name: str | None = config.settings.get("changelog_format") - format = ( - name and KNOWN_CHANGELOG_FORMATS.get(name) or _guess_changelog_format(filename) - ) - - if not format: - raise ChangelogFormatUnknown(f"Unknown changelog format '{name}'") + known = ", ".join(sorted(KNOWN_CHANGELOG_FORMATS)) or "(none registered)" + + if name: + format_cls = KNOWN_CHANGELOG_FORMATS.get(name) + if format_cls is None: + raise ChangelogFormatUnknown( + f"Unknown changelog format '{name}'. Known formats: {known}." + ) + return format_cls(config) + + format_cls = _guess_changelog_format(filename) + if format_cls is None: + raise ChangelogFormatUnknown( + "Cannot infer changelog format from filename " + f"'{filename}'. Set the `changelog_format` setting " + f"explicitly. Known formats: {known}." + ) - return format(config) + return format_cls(config) def _guess_changelog_format(filename: str | None) -> type[ChangelogFormat] | None: diff --git a/tests/test_changelog_formats.py b/tests/test_changelog_formats.py index 6ffbc8dc3..a62ac6ad7 100644 --- a/tests/test_changelog_formats.py +++ b/tests/test_changelog_formats.py @@ -53,11 +53,42 @@ def test_get_format_empty_filename(config: BaseConfig, filename: str | None): @pytest.mark.parametrize("filename", [None, ""]) def test_get_format_empty_filename_no_setting(config: BaseConfig, filename: str | None): config.settings["changelog_format"] = None - with pytest.raises(ChangelogFormatUnknown): + with pytest.raises(ChangelogFormatUnknown) as excinfo: get_changelog_format(config, filename) + # The error message should hint at setting `changelog_format` and list + # the known formats so users on non-standard file extensions know what + # to do (#894). + msg = str(excinfo.value) + assert "changelog_format" in msg + assert "Known formats" in msg @pytest.mark.parametrize("filename", ["extensionless", "file.unknown"]) def test_get_format_unknown(config: BaseConfig, filename: str | None): - with pytest.raises(ChangelogFormatUnknown): + with pytest.raises(ChangelogFormatUnknown) as excinfo: get_changelog_format(config, filename) + # Same hint when the filename extension is unknown. + msg = str(excinfo.value) + assert "changelog_format" in msg + assert "Known formats" in msg + + +def test_get_format_unknown_name_lists_known_formats(config: BaseConfig): + """Regression test for #894: when ``changelog_format`` is set to an + unknown value, the error must list the registered formats so users + can self-correct.""" + config.settings["changelog_format"] = "definitely-not-a-format" + with pytest.raises(ChangelogFormatUnknown) as excinfo: + get_changelog_format(config) + msg = str(excinfo.value) + assert "definitely-not-a-format" in msg + assert "Known formats" in msg + + +def test_get_format_unknown_name_with_known_filename_raises(config: BaseConfig): + config.settings["changelog_format"] = "invalidformat" + with pytest.raises(ChangelogFormatUnknown) as excinfo: + get_changelog_format(config, "CHANGELOG.md") + msg = str(excinfo.value) + assert "invalidformat" in msg + assert "Known formats" in msg