diff --git a/commitizen/providers/cargo_provider.py b/commitizen/providers/cargo_provider.py index 235d4a110..5152bc74c 100644 --- a/commitizen/providers/cargo_provider.py +++ b/commitizen/providers/cargo_provider.py @@ -82,15 +82,15 @@ def set_lock_version(self, version: str) -> None: ).get("package", {}) if TYPE_CHECKING: assert isinstance(package_content, dict) - try: - version_workspace = package_content["version"]["workspace"] - if version_workspace is True: - package_name = package_content["name"] - if TYPE_CHECKING: - assert isinstance(package_name, str) - members_inheriting.append(package_name) - except NonExistentKey: - pass + version_field = package_content.get("version") + if ( + isinstance(version_field, dict) + and version_field.get("workspace") is True + ): + package_name = package_content["name"] + if TYPE_CHECKING: + assert isinstance(package_name, str) + members_inheriting.append(package_name) for i, package in enumerate(packages): if package["name"] in members_inheriting: diff --git a/tests/providers/test_cargo_provider.py b/tests/providers/test_cargo_provider.py index 63f143b29..023e34b21 100644 --- a/tests/providers/test_cargo_provider.py +++ b/tests/providers/test_cargo_provider.py @@ -451,3 +451,80 @@ def test_cargo_provider_workspace_member_without_workspace_key( assert file.read_text() == dedent(expected_workspace_toml) # The lock file should remain unchanged since the member doesn't inherit workspace version assert lock_file.read_text() == dedent(expected_lock_content) + + +def test_cargo_provider_workspace_member_with_fixed_version( + config: BaseConfig, + chdir: Path, +): + """Regression test for https://github.com/commitizen-tools/commitizen/issues/2001. + + A workspace member with a hardcoded ``version = "x.y.z"`` string (rather + than ``version.workspace = true``) used to crash ``set_lock_version`` with + ``TypeError: string indices must be integers, not 'str'`` because the code + unconditionally subscripted ``package["version"]["workspace"]``. + """ + workspace_toml = """\ +[workspace] +members = ["member_with_fixed_version"] + +[workspace.package] +version = "0.1.0" +""" + + # Real Cargo syntax for a workspace member that opts out of the + # workspace-inherited version by pinning its own. + member_content = """\ +[package] +name = "member_with_fixed_version" +version = "0.9.0" +""" + + lock_content = """\ +[[package]] +name = "member_with_fixed_version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +""" + + expected_workspace_toml = """\ +[workspace] +members = ["member_with_fixed_version"] + +[workspace.package] +version = "42.1" +""" + + # The pinned member must not be bumped because it does not inherit the + # workspace version. + expected_lock_content = """\ +[[package]] +name = "member_with_fixed_version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +""" + + filename = CargoProvider.filename + file = chdir / filename + file.write_text(dedent(workspace_toml)) + + os.mkdir(chdir / "member_with_fixed_version") + member_file = chdir / "member_with_fixed_version" / "Cargo.toml" + member_file.write_text(dedent(member_content)) + + lock_filename = CargoProvider.lock_filename + lock_file = chdir / lock_filename + lock_file.write_text(dedent(lock_content)) + + config.settings["version_provider"] = "cargo" + + provider = get_provider(config) + assert isinstance(provider, CargoProvider) + assert provider.get_version() == "0.1.0" + + # Must not raise TypeError: string indices must be integers, not 'str'. + provider.set_version("42.1") + assert file.read_text() == dedent(expected_workspace_toml) + assert lock_file.read_text() == dedent(expected_lock_content)