Files
oto-enterprise-os-dtp/ci/README.md
T

151 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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é par `if: … 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é dans `needs` (gate en
attente perpétuelle sur push).
- **INV-B** — chaque script `ci/*.sh` du dépôt est lancé par exactement un job
(`run: bash ci/<script>`), lui-même dans `gate.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
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` :
```bash
# 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_runner` enregistré, label `ubuntu-latest`, statut Idle.
- `[ ]` Push de test → les 6 gates statiques apparaissent et passent au vert.
- `[ ]` PR de test avec violation volontaire → `constraints-guard` bloque (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.