130 lines
4.6 KiB
Python
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
|