Scripts de migration Gogs -> Gitea (depots git, collaborateurs, webhooks, dates de creation)
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Aligne les dates de création affichées sur Gitea avec les dates d'origine Gogs.
|
||||
|
||||
L'API Gitea n'expose PAS d'endpoint pour modifier "created_at" d'un dépôt existant
|
||||
(vérifié : absent de EditRepoOption et MigrateRepoOptions). Seule une modification
|
||||
directe en base de données (colonne repository.created_unix) permet de la corriger.
|
||||
|
||||
Ce script NE SE CONNECTE JAMAIS À LA BASE DE DONNÉES ET N'EXÉCUTE AUCUN SQL. Il :
|
||||
1. compare, via les deux API REST (Gogs + Gitea, lecture seule), la date de
|
||||
création d'origine sur Gogs et la date actuellement affichée sur Gitea ;
|
||||
2. écrit un rapport JSON détaillé (dates_report.json) ;
|
||||
3. génère un script SQL prêt à l'emploi (fix_dates.sql) que l'UTILISATEUR exécute
|
||||
lui-même, après avoir vérifié le schéma de sa base et fait une sauvegarde.
|
||||
|
||||
Le nom exact de la colonne (created_unix) est celui utilisé par Gitea depuis
|
||||
plusieurs versions majeures, mais PEUT VARIER -- vérifier avec
|
||||
`DESCRIBE repository;` avant d'exécuter le SQL généré.
|
||||
|
||||
Usage :
|
||||
python3 fix_creation_dates.py # tous les dépôts communs
|
||||
python3 fix_creation_dates.py --only bachir/monrepo # un seul dépôt (test)
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
from migrate_gogs_to_gitea import (
|
||||
GogsClient, GiteaClient, discover_gogs_orgs, load_config,
|
||||
)
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def parse_iso(s):
|
||||
# Gogs/Gitea renvoient un ISO8601 avec offset, ex: 2017-06-12T11:22:42+02:00
|
||||
return datetime.fromisoformat(s)
|
||||
|
||||
|
||||
def build_report(gogs, gitea, cfg, only=None):
|
||||
candidate_owners = set(gitea.list_all_usernames()) | set(gitea.list_all_orgnames()) | gogs.known_orgs
|
||||
gogs_repos = gogs.list_all_repos(candidate_owners)
|
||||
if only:
|
||||
wanted = set(only)
|
||||
gogs_repos = [r for r in gogs_repos if r["full_name"] in wanted]
|
||||
|
||||
report = []
|
||||
for r in gogs_repos:
|
||||
owner = r["owner"]["username"]
|
||||
name = r["name"]
|
||||
full_name = r["full_name"]
|
||||
gitea_repo = gitea.get_repo(owner, name)
|
||||
if gitea_repo is None:
|
||||
continue # pas encore migré côté Gitea, rien à corriger pour l'instant
|
||||
|
||||
gogs_created = parse_iso(r["created_at"])
|
||||
gitea_created = parse_iso(gitea_repo["created_at"])
|
||||
delta_created_days = abs((gitea_created - gogs_created).total_seconds()) / 86400
|
||||
|
||||
gogs_updated = parse_iso(r["updated_at"])
|
||||
gitea_updated = parse_iso(gitea_repo["updated_at"])
|
||||
delta_updated_days = abs((gitea_updated - gogs_updated).total_seconds()) / 86400
|
||||
|
||||
report.append({
|
||||
"full_name": full_name,
|
||||
"owner": owner,
|
||||
"name": name,
|
||||
"gogs_created_at": r["created_at"],
|
||||
"gitea_created_at": gitea_repo["created_at"],
|
||||
"gogs_created_unix": int(gogs_created.timestamp()),
|
||||
"delta_days": round(delta_created_days, 1),
|
||||
"needs_fix": delta_created_days > 1, # >1 jour d'écart = à corriger
|
||||
"gogs_updated_at": r["updated_at"],
|
||||
"gitea_updated_at": gitea_repo["updated_at"],
|
||||
"gogs_updated_unix": int(gogs_updated.timestamp()),
|
||||
"delta_updated_days": round(delta_updated_days, 1),
|
||||
"needs_fix_updated": delta_updated_days > 1,
|
||||
})
|
||||
return report
|
||||
|
||||
|
||||
def write_sql(report, sql_path):
|
||||
to_fix = [e for e in report if e["needs_fix"] or e["needs_fix_updated"]]
|
||||
lines = [
|
||||
"-- Généré par fix_creation_dates.py -- NE PAS EXÉCUTER SANS AVOIR VÉRIFIÉ :",
|
||||
"-- 1. Le nom des colonnes cibles : DESCRIBE repository;",
|
||||
"-- (created_unix / updated_unix sont les noms standards sur les versions",
|
||||
"-- récentes de Gitea, mais vérifier avant d'exécuter)",
|
||||
"-- 2. Qu'une sauvegarde de la base (ou au moins de la table repository) existe.",
|
||||
"-- Le script valide (COMMIT) automatiquement à la fin -- fait pour une exécution",
|
||||
"-- en une commande (mysql -u ... -p BASE < fix_dates.sql). Ne pas oublier le nom",
|
||||
"-- de la base en argument (sinon: ERROR 1046 No database selected).",
|
||||
"--",
|
||||
f"-- {len(to_fix)} dépôt(s) à corriger sur {len(report)} comparé(s).",
|
||||
"",
|
||||
"START TRANSACTION;",
|
||||
"",
|
||||
]
|
||||
for e in to_fix:
|
||||
sets = []
|
||||
if e["needs_fix"]:
|
||||
sets.append(f"created_unix = {e['gogs_created_unix']}")
|
||||
if e["needs_fix_updated"]:
|
||||
sets.append(f"updated_unix = {e['gogs_updated_unix']}")
|
||||
lines.append(
|
||||
f"-- {e['full_name']}: created Gitea={e['gitea_created_at']} -> Gogs={e['gogs_created_at']}"
|
||||
f" | updated Gitea={e['gitea_updated_at']} -> Gogs={e['gogs_updated_at']}"
|
||||
)
|
||||
lines.append(
|
||||
"UPDATE repository SET {sets} "
|
||||
"WHERE lower_name = '{name}' AND owner_id = "
|
||||
"(SELECT id FROM user WHERE lower_name = '{owner}');".format(
|
||||
sets=", ".join(sets),
|
||||
name=e["name"].lower().replace("'", "''"),
|
||||
owner=e["owner"].lower().replace("'", "''"),
|
||||
)
|
||||
)
|
||||
lines.append("")
|
||||
lines.append("COMMIT;")
|
||||
Path(sql_path).write_text("\n".join(lines), encoding="utf-8")
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
ap.add_argument("--only", action="append", help="ne comparer que owner/repo (répétable)")
|
||||
ap.add_argument("--report", default="dates_report.json")
|
||||
ap.add_argument("--sql", default="fix_dates.sql")
|
||||
args = ap.parse_args()
|
||||
|
||||
cfg = load_config()
|
||||
known_orgs = discover_gogs_orgs(cfg["GOGS_URL"])
|
||||
gogs = GogsClient(cfg["GOGS_URL"], cfg["GOGS_TOKEN"], known_orgs=known_orgs)
|
||||
gitea = GiteaClient(cfg["GITEA_URL"], cfg["GITEA_ADMIN_USERNAME"], cfg["GITEA_ADMIN_PASSWORD"])
|
||||
|
||||
print("Comparaison des dates création/mise à jour Gogs <-> Gitea (lecture seule)...")
|
||||
report = build_report(gogs, gitea, cfg, only=args.only)
|
||||
to_fix = [e for e in report if e["needs_fix"] or e["needs_fix_updated"]]
|
||||
|
||||
print(f"\n{len(report)} dépôt(s) comparé(s), {len(to_fix)} avec au moins un écart > 1 jour.")
|
||||
for e in to_fix[:20]:
|
||||
bits = []
|
||||
if e["needs_fix"]:
|
||||
bits.append(f"created {e['gitea_created_at'][:10]}->{e['gogs_created_at'][:10]}")
|
||||
if e["needs_fix_updated"]:
|
||||
bits.append(f"updated {e['gitea_updated_at'][:10]}->{e['gogs_updated_at'][:10]}")
|
||||
print(f" {e['full_name']}: " + ", ".join(bits))
|
||||
if len(to_fix) > 20:
|
||||
print(f" ... et {len(to_fix) - 20} autre(s), voir {args.report}")
|
||||
|
||||
Path(args.report).write_text(json.dumps(report, indent=2, ensure_ascii=False))
|
||||
write_sql(report, args.sql)
|
||||
print(f"\nRapport détaillé : {args.report}")
|
||||
print(f"Script SQL généré (à VÉRIFIER puis exécuter toi-même) : {args.sql}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user