123 lines
5.7 KiB
Markdown
123 lines
5.7 KiB
Markdown
# Migration Gogs → Gitea (figureslibres.io)
|
|
|
|
Scripts utilisés pour migrer les dépôts d'une instance Gogs vers une instance Gitea
|
|
sur le même serveur (YunoHost), en conservant les propriétaires d'origine.
|
|
|
|
**Portée volontairement limitée à :**
|
|
- les dépôts git (branches, tags, historique complet) ;
|
|
- les collaborateurs (contributeurs) de chaque dépôt ;
|
|
- les webhooks ;
|
|
- les dates de création/mise à jour affichées.
|
|
|
|
**Explicitement hors scope** : issues, wiki, labels, milestones, releases, pull
|
|
requests. Les webhooks pointant vers des services internes avec secret non exposé
|
|
par l'API (ex: Kanboard) sont exclus par défaut.
|
|
|
|
## Contexte
|
|
|
|
Gogs et Gitea tournent tous les deux en local sur le même serveur (loopback,
|
|
`127.0.0.1`), ce qui permet de scripter la migration sans jamais passer par les
|
|
domaines publics ni par le SSO. Le script part du principe que les comptes
|
|
utilisateurs (individus) sont déjà identiques des deux côtés (ex: synchronisés via
|
|
un LDAP commun) — il ne crée jamais de compte utilisateur automatiquement. Les
|
|
organisations, en revanche, peuvent être créées automatiquement si absentes.
|
|
|
|
### Pièges rencontrés (et déjà gérés par le script)
|
|
|
|
- **L'authentification HTTP Basic Auth de l'API Gogs est cassée sur cette
|
|
instance** (401 identique avec de bons ou de mauvais identifiants) → l'API Gogs
|
|
s'utilise donc avec un **token**, jamais avec login/mot de passe.
|
|
- **`GET /api/v1/repos/search` de Gogs ne fait pas une vraie recherche
|
|
site-wide**, même avec un compte admin : il ne renvoie que les dépôts publics +
|
|
ceux du compte du token. La découverte se fait donc propriétaire par
|
|
propriétaire (`/users|orgs/{login}/repos`), en partant de la liste des comptes
|
|
connus côté Gitea.
|
|
- **Les dépôts privés d'un propriétaire n'apparaissent dans cette liste que si le
|
|
compte utilisé par le script a explicitement accès au dépôt** (ajouté comme
|
|
collaborateur) — l'admin ne "voit" pas tout par magie via cet endpoint.
|
|
- **`GET /api/v1/orgs/{login}` de Gogs ne vérifie pas le type** : il renvoie `200`
|
|
pour n'importe quel login existant, user ou org. La détection des vraies
|
|
organisations se fait donc en scrapant `/explore/organizations`, pas via cet
|
|
endpoint.
|
|
- **Le service `"gogs"` de l'API `/repos/migrate` de Gitea interroge d'abord
|
|
l'API Gogs et récupère son `clone_url` public**, qui repasse par le mur SSO et
|
|
fait échouer le clonage. Le script utilise le service générique `"git"` à la
|
|
place, qui clone brutalement l'adresse fournie (le loopback), sans ce détour.
|
|
- **`ALLOW_LOCALNETWORKS` doit être activé côté Gitea** (`app.ini`, section
|
|
`[migrations]`) pour autoriser une migration dont la source est une adresse
|
|
locale/loopback (protection anti-SSRF activée par défaut).
|
|
- **Un compte de service synchronisé LDAP se fait périodiquement réécraser son
|
|
statut admin** par la resynchronisation. Utiliser un compte **local** (pas
|
|
LDAP) pour le compte de service du script, des deux côtés (Gogs et Gitea).
|
|
- **L'API Gitea n'expose aucun moyen d'éditer `created_at`/`updated_at`** d'un
|
|
dépôt existant. `fix_creation_dates.py` compare les dates via les deux API
|
|
(lecture seule) et génère un script SQL à exécuter soi-même après vérification
|
|
du schéma et sauvegarde de la base.
|
|
|
|
## Prérequis
|
|
|
|
- Python 3 + `pip install requests`
|
|
- `git` installé
|
|
- Un **token API Gogs** admin (Gogs → Settings → Applications → Generate New Token)
|
|
- Un **compte Gitea admin local** (pas LDAP) avec mot de passe
|
|
|
|
## Configuration
|
|
|
|
```bash
|
|
cp config.env.example config.env
|
|
# éditer config.env et renseigner les valeurs -- ce fichier ne doit JAMAIS être commité
|
|
```
|
|
|
|
## Utilisation
|
|
|
|
### `migrate_gogs_to_gitea.py` — dépôts, collaborateurs, webhooks
|
|
|
|
Toujours en **dry-run par défaut** (rien n'est écrit) :
|
|
|
|
```bash
|
|
python3 migrate_gogs_to_gitea.py # dry-run, tous les dépôts
|
|
python3 migrate_gogs_to_gitea.py --only owner/repo # dry-run, un seul dépôt
|
|
python3 migrate_gogs_to_gitea.py --limit 3 # dry-run, 3 premiers dépôts
|
|
python3 migrate_gogs_to_gitea.py --apply --only owner/repo # exécution réelle, ciblée
|
|
python3 migrate_gogs_to_gitea.py --apply # exécution réelle, tout
|
|
```
|
|
|
|
Un dépôt déjà présent côté Gitea n'est **jamais recréé ni écrasé** — seuls les
|
|
collaborateurs, webhooks et le topic `migrated-from-gogs` peuvent être complétés.
|
|
|
|
### Vérifier l'historique git des dépôts déjà migrés à la main
|
|
|
|
Compare les branches/tags Gogs vs Gitea des dépôts déjà présents (lecture seule) :
|
|
|
|
```bash
|
|
python3 migrate_gogs_to_gitea.py --verify-existing
|
|
```
|
|
|
|
Ajoute les refs **totalement absentes** côté Gitea (jamais les branches
|
|
divergentes, jamais de `--force`) :
|
|
|
|
```bash
|
|
python3 migrate_gogs_to_gitea.py --verify-existing --apply
|
|
```
|
|
|
|
### `fix_creation_dates.py` — dates de création/mise à jour
|
|
|
|
Ne se connecte jamais à la base de données. Compare via les deux API (lecture
|
|
seule) et génère un script SQL à vérifier puis exécuter soi-même :
|
|
|
|
```bash
|
|
python3 fix_creation_dates.py # tous les dépôts communs
|
|
python3 fix_creation_dates.py --only owner/repo # un seul dépôt (test)
|
|
```
|
|
|
|
Avant d'exécuter le `fix_dates.sql` généré :
|
|
1. Vérifier le nom des colonnes (`DESCRIBE repository;`) — `created_unix` /
|
|
`updated_unix` sont les noms standards des versions récentes de Gitea, mais à
|
|
confirmer sur la version en place.
|
|
2. Faire une sauvegarde de la table `repository` (ou de la base entière).
|
|
3. Exécuter avec le nom de la base en argument :
|
|
```bash
|
|
mysql -u USER -pMOTDEPASSE NOM_BASE < fix_dates.sql
|
|
```
|
|
(le script valide lui-même avec un `COMMIT;` en fin de fichier)
|