build / Garde-fou (pull_request) Failing after 7s
build / Images Harbor (catalog-sync, Dockerfile.catalog-sync) (pull_request) Skipped
build / Images Harbor (web, Dockerfile) (pull_request) Skipped
build / Garde-fou (push) Failing after 8s
build / Images Harbor (catalog-sync, Dockerfile.catalog-sync) (push) Skipped
build / Images Harbor (web, Dockerfile) (push) Skipped
100 lines
3.5 KiB
Python
100 lines
3.5 KiB
Python
"""Publication d'un pack accepte, sous forme de pull request Gitea.
|
|
|
|
Choix volontaire : la moderation n'ecrit jamais directement sur la branche
|
|
principale. Elle ouvre une demande, que tu relis dans Gitea avant de fusionner.
|
|
Git reste la source de verite du catalogue, et une acceptation par erreur dans
|
|
l'interface reste rattrapable en un clic.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import base64
|
|
import json
|
|
import re
|
|
import urllib.error
|
|
import urllib.request
|
|
|
|
from .config import Config
|
|
|
|
TIMEOUT = 15
|
|
|
|
|
|
class ErreurGitea(RuntimeError):
|
|
pass
|
|
|
|
|
|
def _appel(methode: str, chemin: str, corps: dict | None = None) -> dict:
|
|
url = f"{Config.GITEA_URL}/api/v1{chemin}"
|
|
donnees = json.dumps(corps).encode("utf-8") if corps is not None else None
|
|
requete = urllib.request.Request(url, data=donnees, method=methode)
|
|
requete.add_header("Authorization", f"token {Config.GITEA_TOKEN}")
|
|
requete.add_header("Content-Type", "application/json")
|
|
requete.add_header("Accept", "application/json")
|
|
try:
|
|
with urllib.request.urlopen(requete, timeout=TIMEOUT) as reponse:
|
|
charge = reponse.read().decode("utf-8")
|
|
return json.loads(charge) if charge else {}
|
|
except urllib.error.HTTPError as erreur:
|
|
detail = erreur.read().decode("utf-8", "replace")[:500]
|
|
raise ErreurGitea(f"{methode} {chemin} : HTTP {erreur.code} — {detail}") from erreur
|
|
except urllib.error.URLError as erreur:
|
|
raise ErreurGitea(f"{methode} {chemin} : {erreur.reason}") from erreur
|
|
|
|
|
|
def _nom_de_branche(pack_id: str, soumission_id: int) -> str:
|
|
base = re.sub(r"[^a-z0-9-]+", "-", pack_id.lower()).strip("-") or "pack"
|
|
return f"pack/{base}-{soumission_id}"
|
|
|
|
|
|
def ouvrir_demande(soumission, auteur: str) -> str:
|
|
"""Cree la branche, y depose le pack, ouvre la pull request. Rend son URL."""
|
|
|
|
if not Config.gitea_actif():
|
|
raise ErreurGitea("publication Gitea non configuree")
|
|
|
|
proprietaire = Config.GITEA_PROPRIETAIRE
|
|
depot = Config.GITEA_DEPOT
|
|
branche = _nom_de_branche(soumission.pack_id, soumission.id)
|
|
chemin = f"{Config.GITEA_SOUS_DOSSIER}/{soumission.categorie}/{soumission.pack_id}.yml"
|
|
|
|
# Un seul appel cree la branche et le fichier : new_branch part de branch.
|
|
_appel(
|
|
"POST",
|
|
f"/repos/{proprietaire}/{depot}/contents/{chemin}",
|
|
{
|
|
"content": base64.b64encode(soumission.contenu.encode("utf-8")).decode("ascii"),
|
|
"message": f"Ajout du pack {soumission.pack_id} ({soumission.categorie})",
|
|
"branch": Config.GITEA_BRANCHE,
|
|
"new_branch": branche,
|
|
},
|
|
)
|
|
|
|
corps = [
|
|
f"Pack **{soumission.nom or soumission.pack_id}** propose depuis Enclume.",
|
|
"",
|
|
f"- identifiant : `{soumission.pack_id}`",
|
|
f"- categorie : `{soumission.categorie}`",
|
|
f"- auteur : {auteur}",
|
|
f"- soumission : #{soumission.id}",
|
|
]
|
|
if soumission.message:
|
|
corps += ["", "Message de l'auteur :", "", f"> {soumission.message.strip()}"]
|
|
corps += [
|
|
"",
|
|
"---",
|
|
"Contribution externe : relire la ligne de commande, les macros et les "
|
|
"eventuels prerequis avant de fusionner.",
|
|
]
|
|
|
|
demande = _appel(
|
|
"POST",
|
|
f"/repos/{proprietaire}/{depot}/pulls",
|
|
{
|
|
"head": branche,
|
|
"base": Config.GITEA_BRANCHE,
|
|
"title": f"Pack {soumission.pack_id}",
|
|
"body": "\n".join(corps),
|
|
},
|
|
)
|
|
return str(demande.get("html_url") or "")
|