Files
grm/scripts/configure_repo.py
T

130 lines
4.6 KiB
Python

#!/usr/bin/env python3
"""Configure GRM repository: branch protection + labels via Gitea REST API.
Uses the ``tea`` Gitea CLI for label creation and the ``GiteaClient`` for
branch protection and repo settings (tea only supports basic protect/unprotect,
not the detailed config we need with status checks and required approvals).
Usage:
REPO_TOKEN=<token> python3 scripts/configure_repo.py
"""
import http
import os
from typing import cast
import click
from dotenv import load_dotenv # pyright: ignore[reportMissingImports,reportUnknownVariableType]
from gitea_runner_manager.api_clients import GiteaClient
from gitea_runner_manager.config import (
BRANCH_PROTECTION_CONFIG,
GITEA_API_URL,
LABEL_CONFIG,
REPO_NAME,
REPO_OWNER,
REPO_SETTINGS_CONFIG,
)
from gitea_runner_manager.exceptions import APIError
from gitea_runner_manager.i18n import _
from scripts.gitea_cli import TeaCLI, TeaCLIError
load_dotenv(override=True)
def _handle_http_error(e: APIError) -> None:
"""Raise a user-friendly Click exception for HTTP errors."""
if e.status == http.HTTPStatus.FORBIDDEN:
raise click.ClickException(
_(
"HTTP {status} Forbidden — your token lacks admin rights.\n"
"Make sure the token belongs to a repo owner or organisation admin.\n"
"Alternatively, configure branch protection manually in Settings → Branches.",
status=e.status,
)
)
raise click.ClickException(_("HTTP error: {status}{message}", status=e.status, message=e.message))
def _ensure_label_via_tea(tea: TeaCLI, repo: str, name: str, color: str, description: str) -> bool:
"""Create a label via tea if it doesn't already exist.
Returns True if created, False if it already existed.
"""
try:
existing = tea.list_labels(repo)
if any(label.get("name") == name for label in existing):
return False
tea.create_label(repo, name=name, color=color, description=description)
return True
except TeaCLIError:
# Fall back to GiteaClient if tea fails
return _ensure_label_via_client(name, color, description)
def _ensure_label_via_client(name: str, color: str, description: str) -> bool:
"""Fallback: create label via GiteaClient. Returns True if created."""
client = GiteaClient(
GITEA_API_URL,
os.environ.get("REPO_TOKEN", ""),
REPO_OWNER,
REPO_NAME,
)
result = client.ensure_label(name=name, color=color, description=description)
return result is not None
def main() -> None:
token = os.environ.get("REPO_TOKEN", "")
if not token:
raise click.ClickException(_("ERROR: REPO_TOKEN is not set."))
repo = f"{REPO_OWNER}/{REPO_NAME}"
client = GiteaClient(GITEA_API_URL, token, REPO_OWNER, REPO_NAME)
tea = TeaCLI(repo=repo)
try:
click.echo(_("Configuring branch protection for {branch}...", branch="master"))
client.ensure_branch_protection("master", BRANCH_PROTECTION_CONFIG)
click.echo(_(" - Direct pushes: BLOCKED (require PR)"))
click.echo(
_(
" - Required approvals: {count}",
count=BRANCH_PROTECTION_CONFIG["required_approvals"],
)
)
click.echo(_(" - Dismiss stale approvals: yes"))
click.echo(_(" - Block outdated branches: yes"))
click.echo(_(" - Block rejected reviews: yes"))
checks = ", ".join(cast(list[str], BRANCH_PROTECTION_CONFIG["status_check_contexts"]))
click.echo(_(" - Required status checks: {checks}", checks=checks))
click.echo("")
label_name = cast(str, LABEL_CONFIG["name"])
click.echo(_("Creating {label} label...", label=label_name))
created = _ensure_label_via_tea(
tea,
repo,
name=label_name,
color=cast(str, LABEL_CONFIG["color"]),
description=cast(str, LABEL_CONFIG["description"]),
)
if created:
click.echo(_(" Label '{label}' created.", label=label_name))
else:
click.echo(_(" Label '{label}' already exists.", label=label_name))
click.echo("")
click.echo(_("Configuring repository settings..."))
client.update_repo_settings(cast(dict[str, object], REPO_SETTINGS_CONFIG))
click.echo(_(" - Auto-delete branch after merge: yes"))
click.echo("")
click.echo(_("Repository configuration complete."))
except APIError as e:
_handle_http_error(e)
if __name__ == "__main__": # pragma: no cover
main() # pragma: no cover