rss-news/versioning.py
2025-07-09 20:31:18 +02:00

131 lines
5 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import re
import subprocess
from pathlib import Path
from datetime import datetime
import typer
import os
CHANGELOG_FILE = Path("CHANGELOG.md")
VERSION_FILE = Path("__version__.py")
VERSION_PATTERN = r"## \[v?(\d+\.\d+\.\d+)\]"
def get_latest_version():
try:
# Versuch über Git-Tags
tag = subprocess.check_output(["git", "describe", "--tags", "--abbrev=0"], stderr=subprocess.DEVNULL)
return tag.decode("utf-8").strip().lstrip("v")
except subprocess.CalledProcessError:
# Fallback: Changelog
content = CHANGELOG_FILE.read_text(encoding="utf-8")
matches = re.findall(VERSION_PATTERN, content)
return matches[0] if matches else "0.0.0"
def bump_version(version: str, level: str = "patch") -> str:
major, minor, patch = map(int, version.split("."))
if level == "major":
return f"{major + 1}.0.0"
elif level == "minor":
return f"{major}.{minor + 1}.0"
return f"{major}.{minor}.{patch + 1}"
def write_version_file(version: str):
VERSION_FILE.write_text(f"VERSION = \"{version}\"\n", encoding="utf-8")
def update_changelog(version: str):
date = datetime.now().strftime("%Y-%m-%d")
new_entry = f"## [{version}] - {date}\n\n- Beschreibung...\n\n"
content = CHANGELOG_FILE.read_text(encoding="utf-8")
if f"## [{version}]" in content:
typer.secho(f" Version {version} ist bereits im CHANGELOG.md vorhanden. Kein Eintrag hinzugefügt.", fg=typer.colors.BLUE)
else:
CHANGELOG_FILE.write_text(new_entry + content, encoding="utf-8")
typer.secho(f"📄 CHANGELOG.md um Version {version} ergänzt.", fg=typer.colors.MAGENTA)
def is_ssh_signing_available() -> bool:
return Path("~/.ssh/id_ed25519").expanduser().exists()
def is_gpg_available() -> bool:
try:
output = subprocess.check_output(["gpg", "--list-secret-keys"], stderr=subprocess.DEVNULL)
return bool(output.strip())
except Exception:
return False
def configure_signing(use_ssh: bool):
if use_ssh:
subprocess.run(["git", "config", "--global", "gpg.format", "ssh"], check=True)
subprocess.run(["git", "config", "--global", "user.signingkey", "~/.ssh/id_ed25519.pub"], check=True)
else:
subprocess.run(["git", "config", "--global", "gpg.format", "openpgp"], check=True)
subprocess.run(["git", "config", "--global", "commit.gpgsign", "true"], check=True)
def create(
level: str = "patch",
push: bool = False,
no_sign: bool = False,
dry_run: bool = False
):
"""
Erstellt eine neue Version mit optional signiertem Commit & Tag.
Optional: --push, --no-sign, --dry-run
"""
current = get_latest_version()
new_version = bump_version(current, level)
if dry_run:
typer.secho("🔍 Dry-Run aktiviert keine Dateien oder Git-Kommandos werden ausgeführt.\n", fg=typer.colors.YELLOW)
typer.echo(f"➡️ Aktuelle Version: {current}")
typer.echo(f"➡️ Neue Version: {new_version}")
typer.echo(f"➡️ Commit-Level: {level}")
typer.echo(f"➡️ Push nach GitHub: {'Ja' if push else 'Nein'}")
typer.echo(f"➡️ Signieren: {'Nein' if no_sign else 'Automatisch (SSH > GPG)'}")
date = datetime.now().strftime("%Y-%m-%d")
typer.echo("\n📄 Vorschlag für CHANGELOG-Eintrag:")
typer.echo(f"\n## [{new_version}] - {date}\n\n- Beschreibung...\n")
typer.secho("🚫 Dry-Run beendet.\n", fg=typer.colors.YELLOW)
return
write_version_file(new_version)
update_changelog(new_version)
subprocess.run(["git", "add", "."], check=True)
use_signing = False
signing_method = "none"
if not no_sign:
if is_ssh_signing_available():
configure_signing(use_ssh=True)
use_signing = True
signing_method = "ssh"
elif is_gpg_available():
configure_signing(use_ssh=False)
use_signing = True
signing_method = "gpg"
commit_cmd = ["git", "commit", "-m", f"Bump version to v{new_version}"]
if use_signing:
commit_cmd.append("-S")
subprocess.run(commit_cmd, check=True)
if use_signing:
subprocess.run(["git", "tag", "-s", f"v{new_version}", "-m", f"Release v{new_version}"], check=True)
else:
subprocess.run(["git", "tag", "-a", f"v{new_version}", "-m", f"Release v{new_version} (unsigned)"], check=True)
if push:
subprocess.run(["git", "push"], check=True)
subprocess.run(["git", "push", "origin", f"v{new_version}"], check=True)
if use_signing:
if signing_method == "ssh":
typer.secho(f"✅ Version {new_version} erstellt und signiert mit SSH 🔐", fg=typer.colors.GREEN)
elif signing_method == "gpg":
typer.secho(f"✅ Version {new_version} erstellt und signiert mit GPG 🔏", fg=typer.colors.CYAN)
else:
typer.secho(f"⚠️ Version {new_version} wurde ohne Signatur erstellt", fg=typer.colors.YELLOW)
if __name__ == "__main__":
typer.run(create)