#!/usr/bin/env python3 """ci/semver - next semver from conventional commits since the last tag. bin/ci/semver # last tag..HEAD bin/ci/semver v0.1.0 v0.1.0..HEAD # explicit tag + range prints: v0.1.1 | v0.2.0 | v1.0.0 | none Bump rules (conventional commits): BREAKING CHANGE / feat! -> major feat: -> minor fix:, perf:, refactor:,... -> patch no commits in range -> none """ from __future__ import annotations import subprocess import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "tools")) from semver import bump_type, bump_version # noqa: E402 def subjects_for(range_: str) -> list[str]: args = ["git", "log", "--format=%s"] if range_ and range_ != "HEAD": args.append(range_) try: out = subprocess.run(args, capture_output=True, text=True, check=True).stdout except subprocess.CalledProcessError: return [] return [line.strip() for line in out.splitlines() if line.strip()] def last_tag() -> str | None: try: out = subprocess.run(["git", "tag", "--sort=-v:refname"], capture_output=True, text=True, check=True).stdout tags = [t.strip() for t in out.splitlines() if t.strip().startswith("v")] return tags[0] if tags else None except subprocess.CalledProcessError: return None def main(argv: list[str]) -> int: tag = argv[1] if len(argv) > 1 else last_tag() range_ = argv[2] if len(argv) > 2 else (f"{tag}..HEAD" if tag else "HEAD") print(bump_version(tag, bump_type(subjects_for(range_))) or "none") return 0 if __name__ == "__main__": sys.exit(main(sys.argv))