#!/usr/bin/env python3 """Generate badge SVG files and push them to the ``badges`` branch. Replaces the inline shell script in the post-merge workflow with a tested Python equivalent. The script fetches the latest master before generating badges so that the version badge always reflects the current state of the repository (even if a release commit was pushed moments before by the parallel release job). Usage:: python3 scripts/ci/push_badges.py """ from __future__ import annotations import subprocess # nosec B404 import sys from pathlib import Path from typing import Any import click def _run(cmd: list[str], **kwargs: Any) -> subprocess.CompletedProcess[str]: """Run a command and return the result.""" return subprocess.run(cmd, check=True, text=True, **kwargs) # nosec B603 def fetch_latest_master(branch: str = "master") -> None: """Fetch and hard-reset to the latest remote branch. Ensures the working tree reflects the absolute latest state of the remote, which is critical when the release job may have just pushed a new version commit. """ _run(["git", "fetch", "origin", branch]) # nosec B607 _run(["git", "reset", "--hard", f"origin/{branch}"]) # nosec B607 click.echo(f"Synced to latest origin/{branch}") def generate_badges(output_dir: str) -> None: """Generate badge SVG files using generate_badges.py.""" _run([sys.executable, "scripts/generate_badges.py", "--output-dir", output_dir]) badges = list(Path(output_dir).glob("*.svg")) if not badges: raise click.ClickException("No badge SVG files generated") click.echo(f"Generated {len(badges)} badge files") def push_to_badges_branch(badges_dir: str) -> None: """Push generated badges to the orphan ``badges`` branch.""" _run(["git", "config", "user.name", "gitea-actions-bot"]) # nosec B607 _run(["git", "config", "user.email", "actions@oblachno.fyi"]) # nosec B607 _run(["git", "checkout", "--orphan", "badges"]) # nosec B607 _run(["git", "rm", "-rf", "."]) # nosec B607 # Copy badge files to root import shutil for svg in Path(badges_dir).glob("*.svg"): shutil.copy2(svg, Path.cwd() / svg.name) _run(["git", "add", "./*.svg"]) # nosec B607 _run(["git", "commit", "--no-verify", "-m", "Update badges [skip ci]"]) # nosec B607 _run(["git", "push", "origin", "badges", "--force"]) # nosec B607 click.echo("Badges pushed to badges branch") @click.command() @click.option("--output-dir", default=".badges/", help="Temporary directory for badge files.") @click.option("--branch", default="master", help="Branch to sync before generating badges.") def main(output_dir: str, branch: str) -> None: """Generate badges and push them to the badges branch.""" fetch_latest_master(branch) generate_badges(output_dir) push_to_badges_branch(output_dir) if __name__ == "__main__": # pragma: no cover main() # pragma: no cover