🔐 Update: versioning.py um GPG-Tagging erweitert
This commit is contained in:
parent
91baacd355
commit
2a9fa1e548
1 changed files with 25 additions and 121 deletions
146
versioning.py
146
versioning.py
|
|
@ -1,5 +1,3 @@
|
||||||
# versioning.py
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
@ -12,13 +10,11 @@ CHANGELOG_FILE = Path("CHANGELOG.md")
|
||||||
VERSION_FILE = Path("__version__.py")
|
VERSION_FILE = Path("__version__.py")
|
||||||
VERSION_PATTERN = r"## \[v?(\d+\.\d+\.\d+)\]"
|
VERSION_PATTERN = r"## \[v?(\d+\.\d+\.\d+)\]"
|
||||||
|
|
||||||
|
|
||||||
def get_latest_version():
|
def get_latest_version():
|
||||||
content = CHANGELOG_FILE.read_text(encoding="utf-8")
|
content = CHANGELOG_FILE.read_text(encoding="utf-8")
|
||||||
matches = re.findall(VERSION_PATTERN, content)
|
matches = re.findall(VERSION_PATTERN, content)
|
||||||
return matches[0] if matches else "0.0.0"
|
return matches[0] if matches else "0.0.0"
|
||||||
|
|
||||||
|
|
||||||
def bump_version(version: str, level: str = "patch") -> str:
|
def bump_version(version: str, level: str = "patch") -> str:
|
||||||
major, minor, patch = map(int, version.split("."))
|
major, minor, patch = map(int, version.split("."))
|
||||||
if level == "major":
|
if level == "major":
|
||||||
|
|
@ -27,132 +23,40 @@ def bump_version(version: str, level: str = "patch") -> str:
|
||||||
return f"{major}.{minor + 1}.0"
|
return f"{major}.{minor + 1}.0"
|
||||||
return f"{major}.{minor}.{patch + 1}"
|
return f"{major}.{minor}.{patch + 1}"
|
||||||
|
|
||||||
|
|
||||||
def write_version_file(version: str):
|
def write_version_file(version: str):
|
||||||
VERSION_FILE.write_text(f"VERSION = \"{version}\"\n", encoding="utf-8")
|
VERSION_FILE.write_text(f"VERSION = \"{version}\"\n", encoding="utf-8")
|
||||||
typer.echo(f"🔢 __version__.py aktualisiert auf {version}")
|
|
||||||
|
|
||||||
|
def update_changelog(version: str):
|
||||||
def prepend_changelog(version: str):
|
date = datetime.now().strftime("%Y-%m-%d")
|
||||||
today = datetime.today().strftime("%Y-%m-%d")
|
new_entry = f"## [{version}] - {date}\n\n- Beschreibung...\n\n"
|
||||||
new_entry = f"\n\n## [v{version}] – {today}\n\n### 💡 Neue Funktionen\n- \n\n### 🔧 Änderungen & Fixes\n- \n\n### 📦 Internes\n- "
|
|
||||||
original = CHANGELOG_FILE.read_text(encoding="utf-8")
|
|
||||||
CHANGELOG_FILE.write_text(new_entry + original, encoding="utf-8")
|
|
||||||
typer.echo(f"📝 Neuer Eintrag für v{version} zu CHANGELOG.md hinzugefügt")
|
|
||||||
|
|
||||||
|
|
||||||
def validate_changelog(version: str) -> bool:
|
|
||||||
content = CHANGELOG_FILE.read_text(encoding="utf-8")
|
content = CHANGELOG_FILE.read_text(encoding="utf-8")
|
||||||
pattern = rf"## \[v?{re.escape(version)}\](.*?)^## \["
|
CHANGELOG_FILE.write_text(new_entry + content, encoding="utf-8")
|
||||||
match = re.search(pattern, content + "\n## [", re.DOTALL | re.MULTILINE)
|
|
||||||
if match:
|
|
||||||
section = match.group(1).strip()
|
|
||||||
if any(line.strip() != "-" for line in section.splitlines() if line.strip()):
|
|
||||||
return True
|
|
||||||
typer.echo("⚠️ CHANGELOG-Eintrag ist noch leer oder unvollständig.")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
def create_git_tag(version: str, signed: bool = True):
|
||||||
def tag_exists(tag: str) -> bool:
|
tag_args = ["git", "tag"]
|
||||||
result = subprocess.run(["git", "tag"], capture_output=True, text=True)
|
if signed:
|
||||||
return tag in result.stdout.splitlines()
|
tag_args.append("-s") # signierter Tag
|
||||||
|
|
||||||
|
|
||||||
def ensure_branch(expected_branch="main"):
|
|
||||||
result = subprocess.run(["git", "rev-parse", "--abbrev-ref", "HEAD"], capture_output=True, text=True)
|
|
||||||
branch = result.stdout.strip()
|
|
||||||
if branch != expected_branch:
|
|
||||||
typer.echo(f"⛔ Du befindest dich auf Branch '{branch}', erwartet wird '{expected_branch}'")
|
|
||||||
raise typer.Exit(code=1)
|
|
||||||
|
|
||||||
|
|
||||||
def extract_changelog_entry(version: str) -> str:
|
|
||||||
content = CHANGELOG_FILE.read_text(encoding="utf-8")
|
|
||||||
pattern = rf"## \[v?{re.escape(version)}\](.*?)^## \["
|
|
||||||
match = re.search(pattern, content + "\n## [", re.DOTALL | re.MULTILINE)
|
|
||||||
return match.group(1).strip() if match else ""
|
|
||||||
|
|
||||||
|
|
||||||
def create_git_tag(version: str):
|
|
||||||
try:
|
|
||||||
if tag_exists(f"v{version}"):
|
|
||||||
typer.echo(f"⚠️ Tag v{version} existiert bereits.")
|
|
||||||
return
|
|
||||||
|
|
||||||
subprocess.run(["git", "add", "."], check=True)
|
|
||||||
subprocess.run(["git", "commit", "-m", f"🔖 Release v{version}"], check=True)
|
|
||||||
subprocess.run(["git", "tag", f"v{version}"], check=True)
|
|
||||||
typer.echo(f"🏷️ Git-Tag 'v{version}' erstellt und commit durchgeführt.")
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
typer.echo("⚠️ Git-Fehler beim Taggen oder Committen. Bitte manuell prüfen.")
|
|
||||||
|
|
||||||
|
|
||||||
def push_to_github():
|
|
||||||
try:
|
|
||||||
subprocess.run(["git", "push"], check=True)
|
|
||||||
subprocess.run(["git", "push", "--tags"], check=True)
|
|
||||||
typer.echo("🚀 Änderungen und Tags an GitHub gepusht.")
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
typer.echo("⚠️ Fehler beim Pushen zu GitHub. Bitte Zugang oder Netzwerk prüfen.")
|
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
|
||||||
def list():
|
|
||||||
"Listet alle verfügbaren Versionen aus dem CHANGELOG"
|
|
||||||
typer.echo("\n📚 Verfügbare Versionen im CHANGELOG:")
|
|
||||||
content = CHANGELOG_FILE.read_text(encoding="utf-8")
|
|
||||||
versions = re.findall(VERSION_PATTERN, content)
|
|
||||||
for v in versions:
|
|
||||||
typer.echo(f"- v{v}")
|
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
|
||||||
def rollback():
|
|
||||||
"Letzte Version zurückrollen (Tag löschen + Commit zurücknehmen)"
|
|
||||||
last_version = get_latest_version()
|
|
||||||
if typer.confirm(f"⚠️ Letzte Version 'v{last_version}' wirklich zurücknehmen?"):
|
|
||||||
try:
|
|
||||||
subprocess.run(["git", "tag", "-d", f"v{last_version}"], check=True)
|
|
||||||
subprocess.run(["git", "reset", "--hard", "HEAD~1"], check=True)
|
|
||||||
typer.echo(f"🔙 Version 'v{last_version}' wurde zurückgerollt.")
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
typer.echo("❌ Rollback fehlgeschlagen.")
|
|
||||||
else:
|
else:
|
||||||
typer.echo("⛔ Abgebrochen.")
|
tag_args.append("-a") # un-signierter, annotierter Tag
|
||||||
|
tag_args += [f"v{version}", "-m", f"Release v{version}"]
|
||||||
|
subprocess.run(tag_args, check=True)
|
||||||
|
|
||||||
|
def push_git_tag(version: str):
|
||||||
|
subprocess.run(["git", "push"], check=True)
|
||||||
|
subprocess.run(["git", "push", "origin", f"v{version}"], check=True)
|
||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
def create(level: str = typer.Option("patch", help="Versionstyp: patch, minor oder major"),
|
def create(level: str = "patch", push: bool = False, unsigned: bool = False):
|
||||||
push: bool = typer.Option(False, help="Änderungen direkt an GitHub pushen")):
|
current = get_latest_version()
|
||||||
"Neue Version erstellen inkl. CHANGELOG, Git-Tag und optional Push"
|
new_version = bump_version(current, level)
|
||||||
ensure_branch()
|
write_version_file(new_version)
|
||||||
|
update_changelog(new_version)
|
||||||
current_version = get_latest_version()
|
subprocess.run(["git", "add", "."], check=True)
|
||||||
next_version = bump_version(current_version, level)
|
subprocess.run(["git", "commit", "-m", f"Bump version to v{new_version}"], check=True)
|
||||||
|
create_git_tag(new_version, signed=not unsigned)
|
||||||
typer.echo(f"💡 Aktuelle Version: {current_version}")
|
if push:
|
||||||
typer.echo(f"🚀 Neue Version: {next_version}")
|
push_git_tag(new_version)
|
||||||
|
typer.echo(f"✅ Version {new_version} erstellt und getaggt{' (unsigned)' if unsigned else ' (signed)'}.")
|
||||||
if typer.confirm("Version übernehmen und eintragen?"):
|
|
||||||
write_version_file(next_version)
|
|
||||||
prepend_changelog(next_version)
|
|
||||||
|
|
||||||
typer.echo("\nBitte CHANGELOG.md bearbeiten und danach fortfahren.")
|
|
||||||
typer.prompt("Drücke Enter, sobald du den neuen Abschnitt ausgefüllt hast")
|
|
||||||
|
|
||||||
if not validate_changelog(next_version):
|
|
||||||
typer.echo("❌ Release abgebrochen: Bitte fülle den CHANGELOG-Eintrag aus.")
|
|
||||||
raise typer.Exit(code=1)
|
|
||||||
|
|
||||||
create_git_tag(next_version)
|
|
||||||
|
|
||||||
if push:
|
|
||||||
push_to_github()
|
|
||||||
|
|
||||||
typer.echo(f"✅ Version {next_version} erfolgreich erstellt.")
|
|
||||||
else:
|
|
||||||
typer.echo("❌ Abgebrochen.")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app()
|
app()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue