Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CI/CD · OTO Enterprise OS DTP · Gitea Actions
Livrable Sprint 1 (roadmap
04_roadmap/ROADMAP_8_WEEKS_OR_LESS.md§Sprint 1 · DevOps) « CI/CD Gitea Actions » — gate qualité qui conditionne tous les sprints suivants.Plateforme : Gitea Actions UNIQUEMENT (CLAUDE.md contrainte #2 · JAMAIS GitHub).
1. Ce que fait le pipeline
Workflow : .gitea/workflows/ci.yml. Déclenché sur push / pull_request vers main
et manuellement (workflow_dispatch). Jobs statiques (+ une suite unittest par module) :
| Job | Script | Rôle | Blocant |
|---|---|---|---|
constraints-guard |
ci/guard_constraints.sh |
Enforce les contraintes NON-NÉGOCIABLES de CLAUDE.md | ✅ oui |
validate-json |
ci/validate_json.sh |
Parse strict de tous les *.json (schémas Faisabilité↔Publiciste) |
✅ oui |
check-docs |
ci/check_docs.sh |
Liens Markdown internes + présence auto-score 4Big | ✅ oui (liens) |
check-artifacts |
ci/check_artifacts.sh |
Reproductibilité : chaque out/*.json versionné == build frais |
✅ oui |
check-regression |
ci/check_regression.sh |
Fraîcheur : qa/regression/out/regression_run.json (run) == run frais + verdict PASS |
✅ oui |
check-ci-integrity |
ci/check_ci_integrity.sh |
Intégrité du câblage : gate.needs == tous les jobs non-manuels · chaque ci/*.sh câblé au gate |
✅ oui |
gate |
— | Agrégat vert = gate qualité 4Big franchi | ✅ oui |
Aucune dépendance réseau/marketplace hors actions/checkout. Tout tourne avec
bash + git + python3 (déjà présents sur un runner standard).
2. Détail des contrôles
guard_constraints.sh — contraintes NON-NÉGOCIABLES
Détecte l'usage (pas la simple mention) de :
- Plateformes git interdites :
github.com,gitlab.com,bitbucket.org(#2). - CRM interdits :
EspoCRM,HubSpot(#3). - Paiement interdit :
Stripe(#10 · Cardnet only). - Écriture directe dans
/var/www/html/static/(Interdit absolu). - Commande
git clean(Interdit absolu). - Remote git pointant ailleurs que Gitea/interne.
Zéro faux positif : une ligne contenant un marqueur de prohibition
(jamais, ❌, only, pas de, interdit…) est un rappel de règle → ignorée.
Escape hatch documenté : ajouter ci-allow sur une ligne pour l'exclure.
validate_json.sh
Parse chaque *.json suivi (dont version.schema.json et
projets_master.schema.json, contrat de données Faisabilité → Publiciste).
Un JSON cassé casse le pipeline aval → attrapé ici.
check_docs.sh
- [HARD] liens Markdown relatifs internes : la cible doit exister.
- [SOFT] livrables
05_deliverables_mvp/*.md: mention d'auto-score 4Big attendue (≥95/100, CLAUDE.md #5). Avertissement seul, non blocant.
check_artifacts.sh
Régénère chaque artefact 05_deliverables_mvp/**/out/ versionné depuis son
générateur (build -o <tmp>) et exige une égalité byte-for-byte avec le
fichier commité. Découverte automatique (zéro liste à la main · #6) : tout module
avec un out/ et un générateur build entre dans le gate.
Cible la dérive silencieuse : un module auto-résout des valeurs depuis les
artefacts d'autres modules (ex. demo/scenarios lit
qa/audit_4big/coverage/ci_modules_count) ; quand la source grandit, l'artefact
consommateur devient périmé s'il n'est pas régénéré — dérive qu'aucune suite
tests/ (qui teste des fonctions, pas le fichier commité) n'attrape. Correctif :
build -o out puis commit. Les artefacts d'exécution non produits par build
(p.ex. qa/regression/out/regression_run.json, issu de run) sont hors de ce
gate — ils sont couverts par check-regression ci-dessous.
check_regression.sh
Rejoue la matrice de régression complète (qa/regression/regression_gen.py run,
~5 s, stdlib pur) vers un tmp et exige que le regression_run.json frais soit
byte-identique au commité, puis que son verdict soit PASS. Complément direct
de check_artifacts : celui-ci ne rejoue que build (→ regression_plan.json) et
laisse hors périmètre l'artefact d'exécution regression_run.json — pourtant
c'est lui qui porte les compteurs cités partout dans la doc et les logs
(« 21 suites · 534 tests · PASS »). Sans ce gate, ce chiffre pouvait se périmer en
silence (module + job CI ajoutés sans régénérer la matrice → « 21 suites » faux ;
c'est la dérive « demo 18→21 » corrigée à la main), ou une matrice rouge être
commitée verte. regression_run.json ne contient aucun horodatage/hôte → le run est
déterministe et l'égalité exacte licite. Correctif : regression_gen.py run -o out
puis commit.
check_ci_integrity.sh
Prouve, en lisant .gitea/workflows/ci.yml, que le câblage du workflow tient —
car le job gate est le seul verrou de merge : un check absent de son needs:
ne bloque rien, même rouge. Deux invariants :
- INV-A —
gate.needs== { tous les jobs définis } − {gate} − { jobs manuels } (un job manuel = gardé parif: … workflow_dispatch …, ex.e2e-baseline, légitimement hors du gate push/PR car il exige un serveur live). Détecte un job oublié du gate (MISSING → ne bloque pas), une référence fantôme (DANGLING → typo / job renommé-supprimé) et un job manuel glissé dansneeds(gate en attente perpétuelle sur push). - INV-B — chaque script
ci/*.shdu dépôt est lancé par exactement un job (run: bash ci/<script>), lui-même dansgate.needs. Un nouveau gate statique non câblé (script mort) ou décâblé casse le check.
Ferme le trou laissé par les couvertures existantes (audit_4big/registry,
qa/regression/discovery) qui ne prouvent l'appartenance au gate que des jobs de
module (ceux portant un working-directory:) — les gates statiques sans
working-directory n'étaient gardés par personne. Même classe d'anti-dérive que INV4
(disque→CI), appliquée au câblage CI. stdlib pur (bash/awk/git), zéro réseau.
3. Exécution locale (avant push)
bash ci/guard_constraints.sh # contraintes CLAUDE.md
bash ci/validate_json.sh # schémas JSON
bash ci/check_docs.sh # liens + score
bash ci/check_artifacts.sh # reproductibilité out/ (build)
bash ci/check_regression.sh # fraîcheur matrice régression (run)
bash ci/check_ci_integrity.sh # intégrité du câblage CI (gate ⊇ tous jobs)
Chaque script retourne 0 si conforme, 1 sinon. Reproduit exactement ce que
fait la CI (mêmes scripts, aucune logique cachée côté YAML).
4. Enregistrement du runner Gitea (ops · à faire sur le VPS)
⚠ Étape hors périmètre de ce worker (touche au VPS). À réaliser par l'agent DevOps. Documenté ici pour traçabilité.
Le workflow cible un runner avec le label ubuntu-latest. Sur le VPS Gitea
(:3015), enregistrer un act_runner :
# Sur le VPS, récupérer le token runner :
# Gitea → Site Administration → Actions → Runners → Create new Runner
act_runner register \
--instance http://153.75.250.214:3015 \
--token <RUNNER_TOKEN> \
--labels ubuntu-latest:docker://node:20-bookworm \
--name oto-dtp-runner
act_runner daemon # ou service systemd dédié
Vérifier ensuite : Gitea → repo michel/oto-enterprise-os-dtp → Settings →
Actions doit être activé, et le runner doit apparaître « Idle ».
5. Checklist de vérification (DevOps, sur VPS)
[ ]Gitea Actions activé au niveau instance ET repo.[ ]act_runnerenregistré, labelubuntu-latest, statut Idle.[ ]Push de test → les 6 gates statiques apparaissent et passent au vert.[ ]PR de test avec violation volontaire →constraints-guardbloque (rouge).
Auto-score 4Big du livrable : 96/100. Réserve −4 : l'enregistrement du runner (§4) est hors périmètre repo et reste à confirmer sur le VPS par DevOps ; tant que le runner n'est pas Idle, la CI ne s'exécute pas côté serveur bien que les scripts soient validés localement.