Files
factory/vibe/guidebooks/factory-provisioning/ansible/07-mirrors.md
T
arcodangeandClaude Opus 5 e84383ee34 feat(miroirs) — un dépôt personnel n'est pas une organisation
Le rôle gitea_repo ne savait viser qu'un propriétaire : l'organisation, des
deux côtés à la fois. Les dépôts qui vivent sous le compte personnel
`arcodange` ne pouvaient donc pas sortir du homelab — ni être balayés par
gitea_sync, qui n'interroge que /orgs/<org>/repos.

Trois séparations, toutes rétrocompatibles (les défauts reconduisent le
comportement org-vers-org des dix dépôts déjà en miroir) :

- le propriétaire côté Gitea (`gitea_repo_owner`) n'est plus le même objet que
  celui d'en face (`github_owner`, `gitlab_owner`) ;
- un compte personnel n'est pas une organisation : GitHub ne crée pas le dépôt
  au même endroit, d'où `github_owner_is_org` qui route vers POST /user/repos ;
- GitLab devient facultatif (`gitea_mirror_gitlab`). Il ne l'était pas : sa
  création attendait un 201 sans ignore_errors, si bien qu'un échec GitLab
  avortait l'itération — y compris la moitié GitHub, qui n'y était pour rien.

Deux défauts corrigés au passage, tous deux silencieux :

- les trois listages de gitea_sync ne paginaient pas (30 chez GitHub, 20 chez
  GitLab). Sous la taille d'une page tout va bien ; au-delà, la différence
  entre forges désigne de FAUX dépôts manquants et le rôle les « répare » ;
- la migration entrante posait `repo_owner: github_organization` pour désigner
  le propriétaire DANS Gitea.

Et un piège découvert en exécutant : un dépôt GitHub créé vide adopte comme
branche par défaut la PREMIÈRE branche que le miroir lui pousse — `kadans` a
atterri sur `arcodange/adr-ddd-front`. Le rôle réaligne désormais sur la
branche par défaut de Gitea ; le miroir étant asynchrone, l'alignement échoue
au run qui crée le dépôt et réussit au suivant, d'où le failed_when permissif.

Ce qui sort du homelab reste un CHOIX : playbooks/07_mirrors.yml parcourt une
liste explicite et relue (`gitea_mirrored_repos`) plutôt que la différence
automatique entre forges, qui recréerait un dépôt supprimé exprès.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-07-27 13:34:26 +02:00

4.9 KiB
Raw Blame History

vibe > Guidebooks > Factory provisioning > Ansible > 07 · Mirrors

07 · Mirrors — Gitea → GitHub / GitLab

Note

Status: active · Last Updated: 2026-07-27 Upstream: Ansible sub-hub · Factory provisioning hub Downstream: Roles referencegitea_repo, gitea_sync, gitea_token Related: Inventory & variables · 03 · CI/CD

Gitea is the source of truth; GitHub and GitLab hold a pushed copy. playbooks/07_mirrors.yml walks the repos declared in gitea_mirrored_repos (inventory/group_vars/all/gitea.yml) and, for each, calls gitea_repo: create the counterpart repo private if it is missing, then attach a push mirror refreshed every 8 h and on every commit.

Nothing is pulled back. A mirror only ever pushes Gitea → forge, so a change made on GitHub is overwritten at the next sync.

uv run ansible-playbook -i ansible/arcodange/factory/inventory \
  ansible/arcodange/factory/playbooks/07_mirrors.yml

# GitHub only — while the personal GitLab namespace is still unset:
… -e gitea_mirror_gitlab=false

The Gitea token is minted for the run by gitea_token and revoked in post_tasks. Everything is tagged gitea_mirrors.


Two ways to pick repos, and when each fits

gitea_sync gitea_mirrored_repos + 07_mirrors.yml
Selection Automatic: diffs the three forges for one owner, reconciles whatever is missing somewhere Explicit list, reviewed in the inventory
Fits The organisation, where every repo is meant to exist everywhere The personal account, where each repo leaving the homelab is a deliberate call
Blind spot repos_incomplete = all common says nothing about why a repo is missing — a repo deleted on purpose from GitHub is recreated Anything absent from the list is silently never mirrored

Both drive the same gitea_repo role, so the mirror they produce is identical.


Owner mapping

A Gitea repo owned by the user arcodange does not belong on the GitHub organisation — and GitHub does not even create it the same way (POST /user/repos instead of POST /orgs/<org>/repos). Hence three knobs, all defaulting to the previous org-only behaviour:

Var Default Meaning
gitea_repo_owner gitea_organization Owner on Gitea
github_owner / gitlab_owner github_organization / gitlab_root_group Owner on the far forge
github_owner_is_org true false routes creation to POST /user/repos
gitea_mirror_github / gitea_mirror_gitlab true Turn a forge off entirely

Important

GitLab was not optional before. Its create call expected 201 with no ignore_errors, so a GitLab failure aborted the iteration — including the GitHub half that had nothing to do with it. gitea_mirror_gitlab: false is the way out.

Warning

A GitHub repo created empty adopts as its default branch the first branch the mirror pushes, which is routinely a work branch rather than main. The role realigns it against Gitea's default branch, but the mirror is asynchronous: the alignment fails on the run that creates the repo and succeeds on the next one. Run the playbook twice, or fix the branch by hand.


Current state (2026-07-27)

Owner Repos mirrored Target
arcodange-org 10 (factory, tools, erp, cms, webapp, url-shortener, docker.tofu, docker-build-workflow, super-linter-workflow, vault-action) github.com/arcodange-org/* + GitLab
arcodange (user) 5 (kadans, kadans-api, kadans-dossier, kadans-jobs, video_analysis) — all private github.com/arcodange/*

Not mirrored, deliberately left out of gitea_mirrored_repos: documents, studio, prospection, kissmetrics_contract_proposal (org) and .profile, DanceVideos, SecondBrain, dance-lessons-coach, frame-sdk, telegram-gateway (user).

Note

The personal repos have no GitLab mirror yet: gitlab_personal_namespace_id is still ~. Fill it with the numeric namespace ID of the arcodange account on gitlab.com, otherwise creation would land the project in the arcodange-org group.


Reading the truth from Gitea

The push mirrors live in Gitea, not in this repo — the playbook is idempotent precisely because it asks first:

curl -s -H "Authorization: token $GITEA_TOKEN" \
  https://gitea.arcodange.lab/api/v1/repos/arcodange/kadans/push_mirrors

last_update tells you when the mirror last pushed. A repo with no entry has no mirror, whatever this page claims.