Skip to content

Commit 4c38ef9

Browse files
committed
✨ feat(version): update cargo toml version in package section only
1 parent 0f862a7 commit 4c38ef9

2 files changed

Lines changed: 117 additions & 2 deletions

File tree

tests/unit/test_version.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
get_version_from_version_file,
2525
get_version_from_version_txt,
2626
show_file_diff,
27+
update_cargo_toml_version,
2728
)
2829

2930

@@ -738,3 +739,86 @@ def test_show_file_diff_user_cancels(self, mock_confirm):
738739
with pytest.raises(SystemExit):
739740
show_file_diff(old_content, new_content, "test.txt")
740741
mock_confirm.assert_called_once_with("Do you want to continue?", default=True)
742+
743+
744+
class TestCargoTomlVersionUpdate:
745+
"""Test cases for Cargo.toml version updating."""
746+
747+
def test_update_cargo_toml_version_package_section_only(self, tmp_path):
748+
"""Test that update_cargo_toml_version only updates version in [package] section."""
749+
cargo_toml = tmp_path / "Cargo.toml"
750+
cargo_toml_content = """[package]
751+
name = "test-package"
752+
version = "1.0.0"
753+
authors = ["Test Author"]
754+
755+
[dependencies]
756+
serde = { version = "1.0.0", features = ["derive"] }
757+
758+
[dev-dependencies]
759+
tokio = { version = "1.0.0", features = ["full"] }
760+
761+
[workspace]
762+
members = ["subcrate"]
763+
764+
# Some other version reference that should NOT be changed
765+
# version = "should-not-change"
766+
"""
767+
cargo_toml.write_text(cargo_toml_content)
768+
769+
# Update version
770+
update_cargo_toml_version(str(cargo_toml), "2.0.0", 0, show_diff=False)
771+
772+
# Read updated content
773+
updated_content = cargo_toml.read_text()
774+
775+
# Verify only the package version was updated
776+
assert 'version = "2.0.0"' in updated_content
777+
assert 'serde = { version = "1.0.0"' in updated_content # Dependency version unchanged
778+
assert 'tokio = { version = "1.0.0"' in updated_content # Dev dependency version unchanged
779+
assert '# version = "should-not-change"' in updated_content # Comment unchanged
780+
781+
# Count occurrences to ensure only one version was changed
782+
assert updated_content.count('version = "2.0.0"') == 1
783+
assert updated_content.count('version = "1.0.0"') == 2 # The two dependency versions remain
784+
785+
def test_update_cargo_toml_version_complex_package_section(self, tmp_path):
786+
"""Test updating version in a more complex [package] section."""
787+
cargo_toml = tmp_path / "Cargo.toml"
788+
cargo_toml_content = """[package]
789+
name = "complex-package"
790+
version = "0.1.0"
791+
edition = "2021"
792+
authors = ["Author One", "Author Two"]
793+
license = "MIT"
794+
description = "A test package"
795+
repository = "https://github.com/test/test"
796+
797+
[lib]
798+
name = "complex_package"
799+
path = "src/lib.rs"
800+
801+
[dependencies]
802+
log = { version = "0.4.0" }
803+
804+
[workspace]
805+
members = ["other-crate"]
806+
"""
807+
cargo_toml.write_text(cargo_toml_content)
808+
809+
update_cargo_toml_version(str(cargo_toml), "0.2.0", 0, show_diff=False)
810+
811+
updated_content = cargo_toml.read_text()
812+
813+
# Verify correct update
814+
assert 'version = "0.2.0"' in updated_content
815+
assert 'log = { version = "0.4.0" }' in updated_content # Dependency unchanged
816+
assert updated_content.count('version = "0.2.0"') == 1
817+
assert updated_content.count('version = "0.4.0"') == 1
818+
819+
def test_update_cargo_toml_version_file_not_exists(self, tmp_path):
820+
"""Test that function handles non-existent file gracefully."""
821+
non_existent_file = str(tmp_path / "nonexistent.toml")
822+
823+
# Should not raise an error
824+
update_cargo_toml_version(non_existent_file, "1.0.0", 0, show_diff=False)

tgit/version.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ def update_version_in_file(verbose: int, next_version_str: str, file: str, file_
555555
elif file == "setup.py":
556556
update_file(str(file_path), r"version=['\"].*?['\"]", f"version='{next_version_str}'", verbose, show_diff=show_diff)
557557
elif file == "Cargo.toml":
558-
update_file(str(file_path), r'version\s*=\s*".*?"', f'version = "{next_version_str}"', verbose, show_diff=show_diff)
558+
update_cargo_toml_version(str(file_path), next_version_str, verbose, show_diff=show_diff)
559559
elif file in ("VERSION", "VERSION.txt"):
560560
update_file(str(file_path), None, next_version_str, verbose, show_diff=show_diff)
561561

@@ -564,7 +564,7 @@ def update_file_in_root(next_version_str: str, verbose: int, root_path: Path, *,
564564
update_file(str(root_path / "package.json"), r'"version":\s*".*?"', f'"version": "{next_version_str}"', verbose, show_diff=show_diff)
565565
update_file(str(root_path / "pyproject.toml"), r'version\s*=\s*".*?"', f'version = "{next_version_str}"', verbose, show_diff=show_diff)
566566
update_file(str(root_path / "setup.py"), r"version=['\"].*?['\"]", f"version='{next_version_str}'", verbose, show_diff=show_diff)
567-
update_file(str(root_path / "Cargo.toml"), r'(?m)^version\s*=\s*".*?"', f'version = "{next_version_str}"', verbose, show_diff=show_diff)
567+
update_cargo_toml_version(str(root_path / "Cargo.toml"), next_version_str, verbose, show_diff=show_diff)
568568
update_file(
569569
str(root_path / "build.gradle.kts"),
570570
r'version\s*=\s*".*?"',
@@ -591,6 +591,37 @@ def update_file(filename: str, search_pattern: str | None, replace_text: str, ve
591591
f.write(new_content)
592592

593593

594+
def update_cargo_toml_version(filename: str, next_version_str: str, verbose: int, *, show_diff: bool = True) -> None:
595+
"""Update version in Cargo.toml, specifically in the [package] section only."""
596+
file_path = Path(filename)
597+
if not file_path.exists():
598+
return
599+
if verbose > 0:
600+
console.print(f"Updating {file_path}")
601+
602+
with file_path.open() as f:
603+
content = f.read()
604+
605+
# Use regex to match version in [package] section only
606+
# This pattern matches:
607+
# 1. [package] section header
608+
# 2. Any content until version = "..."
609+
# 3. Captures the version line to replace
610+
pattern = r'(\[package\].*?)(version\s*=\s*"[^"]*")'
611+
612+
def replace_version(match: re.Match[str]) -> str:
613+
package_section = match.group(1)
614+
return f'{package_section}version = "{next_version_str}"'
615+
616+
new_content = re.sub(pattern, replace_version, content, flags=re.DOTALL)
617+
618+
if show_diff:
619+
show_file_diff(content, new_content, str(file_path))
620+
621+
with file_path.open("w") as f:
622+
f.write(new_content)
623+
624+
594625
def show_file_diff(old_content: str, new_content: str, filename: str) -> None:
595626
old_lines = old_content.splitlines()
596627
new_lines = new_content.splitlines()

0 commit comments

Comments
 (0)