Files
factory/vibe/guidebooks/factory-provisioning/ansible/07-mirrors.md
T
arcodangeandClaude Opus 5.5 c8aefbc905 feat(mirrors) — kadans-admin rejoint le miroir GitHub : tous les projets kadans sont clonables par une session Claude cloud
- inventory : `kadans-admin` ajouté à `gitea_mirrored_repos` (privé, compte
  personnel `arcodange`) ; playbook appliqué le 2026-10-03, le miroir a poussé
  `main` (feed7d4) en 75 s.
- avertissement : le miroir pousse en `--mirror`, une branche créée côté GitHub
  par une session cloud est supprimée au push suivant.
- nom du playbook corrigé dans les commentaires (06_mirrors → 07_mirrors).
- guidebook 07 · Mirrors : état courant, mise en garde, `mediabunny` écarté.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-03 08:12:27 +02:00

5.6 KiB
Raw Blame History

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

07 · Mirrors — Gitea → GitHub / GitLab

Note

Status: ✅ active · Last Updated: 2026-10-03 Upstream: Ansible sub-hub · Factory provisioning hub Downstream: Roles reference — gitea_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.

Caution

Gitea pushes with git push --mirror (Mirror: true, Force: true in services/mirror/mirror_push.go): every ref that exists only on GitHub is deleted at the next sync — on any commit to the Gitea repo, and every 8 h. A branch pushed to GitHub by a Claude cloud session (claude/*) is therefore short-lived: fetch it back to Gitea, or pull the result locally, before the next commit lands on Gitea.

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-10-03)

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) 6 (kadans, kadans-api, kadans-admin, 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, mediabunny (user). mediabunny stays home because kadans resolves it from the npm registry (mediabunny@^1.56.1), not from the fork — a cloud session cloning kadans does not need it.

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.