diff --git a/scripts/extract_changelog.py b/scripts/extract_changelog.py new file mode 100755 index 0000000..14dbb84 --- /dev/null +++ b/scripts/extract_changelog.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +""" +Extract changelog entries for a specific version from CHANGELOG.md. + +Usage: + python extract_changelog.py VERSION +""" + +import sys +import re +from pathlib import Path + + +def extract_version_content(content: str, version: str) -> str: + """Extract content for specified version from changelog text.""" + # Escape version for regex pattern + version_escaped = re.escape(version) + pattern = rf"## \[{version_escaped}\].*?(?=## \[|$)" + + match = re.search(pattern, content, re.DOTALL) + if not match: + raise ValueError(f"Version {version} not found in changelog") + + return match.group(0).strip() + + +def main(): + """Main entry point for the script.""" + if len(sys.argv) != 2: + print("Usage: python extract_changelog.py VERSION", file=sys.stderr) + sys.exit(1) + + version = sys.argv[1] + changelog_path = Path(__file__).parent.parent / "CHANGELOG.md" + + try: + content = changelog_path.read_text() + except FileNotFoundError: + print(f"Error: Could not find {changelog_path}", file=sys.stderr) + sys.exit(1) + except Exception as e: + print(f"Error reading changelog: {e}", file=sys.stderr) + sys.exit(1) + + try: + version_content = extract_version_content(content, version) + print(version_content) + except ValueError as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/tests/scripts/test_extract_changelog.py b/tests/scripts/test_extract_changelog.py new file mode 100644 index 0000000..f9da477 --- /dev/null +++ b/tests/scripts/test_extract_changelog.py @@ -0,0 +1,75 @@ +import pytest +from scripts.extract_changelog import extract_version_content + +@pytest.fixture +def basic_changelog(): + return """## [1.2.0] +### Added +- Feature A +- Feature B + +## [1.1.0] +### Changed +- Change X +- Change Y +""" + +@pytest.fixture +def complex_changelog(): + return """## [2.0.0] +### Breaking +- Major change + +## [1.9.0] +### Added +- Feature C +### Fixed +- Bug fix + +## [1.8.0] +Some content +""" + +def test_basic_version_extraction(basic_changelog): + """Test extracting a simple version entry""" + result = extract_version_content(basic_changelog, "1.2.0") + expected = """## [1.2.0] +### Added +- Feature A +- Feature B""" + assert result == expected + +def test_middle_version_extraction(complex_changelog): + """Test extracting a version from middle of changelog""" + result = extract_version_content(complex_changelog, "1.9.0") + expected = """## [1.9.0] +### Added +- Feature C +### Fixed +- Bug fix""" + assert result == expected + +def test_version_not_found(): + """Test error handling when version doesn't exist""" + with pytest.raises(ValueError, match="Version 9.9.9 not found in changelog"): + extract_version_content("## [1.0.0]\nSome content", "9.9.9") + +def test_empty_changelog(): + """Test handling empty changelog""" + with pytest.raises(ValueError, match="Version 1.0.0 not found in changelog"): + extract_version_content("", "1.0.0") + +def test_malformed_changelog(): + """Test handling malformed changelog without proper version headers""" + content = "Some content\nNo version headers here\n" + with pytest.raises(ValueError, match="Version 1.0.0 not found in changelog"): + extract_version_content(content, "1.0.0") + +def test_version_with_special_chars(): + """Test handling versions with special regex characters""" + content = """## [1.0.0-beta.1] +Special version +## [1.0.0] +Regular version""" + result = extract_version_content(content, "1.0.0-beta.1") + assert result == "## [1.0.0-beta.1]\nSpecial version"