Files
2dph/bin/ci/semver
T
eSlider 9f22380e82 refactor(tools): bin/{subject}/{method} layout; Go serve+watch modules
Move serve/ (module) -> bin/server, tools/ -> bin/tools, replace bin/kb-watch
bash with bin/watch Go package; self-executing Go shebangs bin/serve.go and
bin/kb/watch.go; Docker + CI + git/import + docs repointed. Multi-stage image
builds static serve+watch binaries (no Go runtime in container).
2026-08-11 09:52:20 +01:00

53 lines
1.7 KiB
Python
Executable File

#!/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[1] / "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))