[DTP-Worker 20260805_001109] Nouveau gate check-mobile-workflow : la structure de mobile-build.yml devient opposable au merge (surface mutation-testée UNGATED, 1re couverture) → 32→33 jobs
- ci/check_mobile_workflow.sh (8e gate statique) · 4 invariants lus dans le fichier (#6) : MOB-1 bien-formé (YAML strict si PyYAML) · MOB-2 aucun secrets. en if: de job (portabilité act/Gitea, verrouille le correctif 234104) · MOB-3 builds gatés sur needs.preflight.outputs.has_token (activation différée #6/#8) · MOB-4 contrat outputs (déclarés==référencés==alimentés, 0 dangling). - Preuve UNGATED avant ajout : réintroduire secrets. en if: de job → 7/7 gates statiques verts. - Gate mutation-testé : 5 mutations toutes ROUGES, fichier propre re-VERT. - Câblé dans ci.yml + gate.needs ; check-ci-integrity INV-A/B verts ; run_ci auto → 33 PASS. - ci/README.md : ligne §1 + détail §2 + nuance « Second workflow ». - 0 nouveau module · 0 chiffre inventé · 0 edition prod hors CI · aucune commande VPS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,7 @@ et manuellement (`workflow_dispatch`). Jobs statiques (+ une suite `unittest` pa
|
||||
| `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 |
|
||||
| `check-readme-claims` | `ci/check_readme_claims.sh` | Intégrité des chiffres **et des faits data-derived** des docs d'entrée (`README.md` « État courant », fiches `03_agents/*/AGENT.md`, READMEs de module) : chaque nombre, label de verdict et identité citée en prose (rôle · DocType · entité · marque · persona…) est **recomputé de l'artefact commité qu'il cite** — ou re-dérivé de `CLAUDE.md` / de la roadmap pour les valeurs ancrées au mandat — puis exigé **exact** (#6), jamais une liste à la main : un doc qui se contredit avec sa propre source est un « vert trompeur ». Surfaces couvertes : comptes de tests/suites, verdicts & barème 4Big, ledgers d'invariants CI, ventilations & identités RBAC, mots-clés & graphe SEO, pipeline vente, e-CF DGII, CONFOTUR, app mobile Expo, run-books VPS, tokens design #4, etc. **Détail exhaustif surface par surface : §2 `check_readme_claims.sh` (ci-dessous).** | ✅ oui |
|
||||
| `check-mobile-workflow` | `ci/check_mobile_workflow.sh` | Intégrité du **second** workflow `mobile-build.yml` (hors gate de merge, mais sa STRUCTURE est gatée ici) : gating portable (0 `secrets.` en `if:` de job → pas de SKIP silencieux) · builds gatés sur les outputs `preflight` (activation différée #6/#8) · contrat d'outputs (déclarés == référencés == alimentés). **Détail : §2 `check_mobile_workflow.sh`.** | ✅ oui |
|
||||
| `gate` | — | Agrégat vert = gate qualité 4Big franchi | ✅ oui |
|
||||
|
||||
Aucune dépendance réseau/marketplace hors `actions/checkout`. Tout tourne avec
|
||||
@@ -41,6 +42,17 @@ il ne rend **jamais** le CI rouge (`preflight` reste vert et rapporte les secret
|
||||
attendus). Périmètre complet (#8 · exécution serveur hors repo), profils EAS et
|
||||
variables `a_confirmer` : [`README` du module Mobile](../05_deliverables_mvp/mobile/app_config/README.md).
|
||||
|
||||
Nuance importante : ses **jobs** sont hors gate de merge, mais sa **structure de
|
||||
fichier**, elle, EST gatée — par le job `check-mobile-workflow`
|
||||
([`ci/check_mobile_workflow.sh`](check_mobile_workflow.sh), dans `gate.needs`).
|
||||
Sans lui, une régression du workflow passait VERTE en silence (mutation vérifiée :
|
||||
réintroduire `secrets.` dans un `if:` de job laissait les 7 gates statiques verts).
|
||||
C'est précisément le risque « marche dans ma tête, casse le jour où Michel active
|
||||
`EAS_TOKEN` » : le contexte `secrets` n'est pas disponible dans `jobs.<id>.if`
|
||||
(table d'availability GitHub Actions), donc un tel `if:` s'évaluerait vide sur
|
||||
`act`/Gitea → build **SKIP silencieux** même token fourni. Le gate rend cet
|
||||
invariant (et l'activation différée) opposable au merge.
|
||||
|
||||
### Rejouer le gate EN LOCAL — `run_ci.sh` (racine du dépôt)
|
||||
|
||||
Avant de committer, reproduire l'intégralité du gate de merge en une commande :
|
||||
@@ -1599,6 +1611,41 @@ Un claim **absent** échoue aussi (la dérive de formulation qui ferait
|
||||
disparaître un chiffre est elle-même une régression de traçabilité). stdlib pur
|
||||
(bash/git/python3), zéro réseau.
|
||||
|
||||
### `check_mobile_workflow.sh` — intégrité du second workflow `mobile-build.yml`
|
||||
`mobile-build.yml` (EAS build iOS/Android · Sprint 5) est **hors `gate.needs`** :
|
||||
aucun de ses jobs ne conditionne un merge (ils s'activent seulement quand Michel
|
||||
fournit `EAS_TOKEN`). Conséquence longtemps tolérée : **aucun gate ne lintait sa
|
||||
structure** — `check_ci_integrity.sh` ne verrouille que `ci.yml`, `validate_json.sh`
|
||||
que le `.json`. Mutation vérifiée : réintroduire `secrets.` dans un `if:` de job y
|
||||
passait **7/7 gates statiques verts**. Or ce fichier porte un correctif de
|
||||
portabilité subtil (session `234104`) qui, s'il régresse, ne casse rien au CI mais
|
||||
fait échouer les builds « le jour où on en a besoin ». Ce gate rend 4 invariants
|
||||
opposables, tous **lus dans le fichier** (jamais une liste à la main · #6) :
|
||||
|
||||
- **MOB-1 · bien-formé** — le fichier existe, porte `name:`/`on:`/`jobs:` et les
|
||||
3 jobs attendus (`preflight`, `build-ios`, `build-android`). Parse YAML strict
|
||||
**exigé si PyYAML est présent**, sinon contrôle awk seul (runner sans pip →
|
||||
best-effort ; jamais un faux vert, car MOB-2/3/4 ne dépendent pas de PyYAML).
|
||||
- **MOB-2 · portabilité du gating** — **0** `secrets.` dans un `if:` au niveau
|
||||
**job** (`^ if:`). Le contexte `secrets` n'est **pas** exposé dans
|
||||
`jobs.<id>.if` (table d'availability GitHub Actions) ; sur `act`/Gitea il
|
||||
s'évaluerait vide → job **SKIP silencieux** même token fourni. Exactement la
|
||||
régression corrigée en `234104` (passage au pattern preflight-output).
|
||||
- **MOB-3 · activation différée préservée (#6/#8)** — chaque job de **build**
|
||||
(bloc lançant `run: eas build`) porte un `if:` job-level gaté sur
|
||||
`needs.preflight.outputs.has_token` → SKIP tant que le token absent, donc le
|
||||
workflow ne rend **jamais** le CI rouge avant activation Michel.
|
||||
- **MOB-4 · contrat d'outputs intact** — `preflight` déclare `has_token`/`has_repo`,
|
||||
chaque `needs.preflight.outputs.<X>` référencé est bien **déclaré** (0 dangling →
|
||||
pas de SKIP par output fantôme) et chaque output déclaré est **alimenté** par un
|
||||
`echo "<X>=… >> $GITHUB_OUTPUT"`.
|
||||
|
||||
Les 5 mutations correspondantes (YAML cassé · `secrets.` en `if:` de job · `if:`
|
||||
de build supprimé · output fantôme référencé · output non alimenté) rendent toutes
|
||||
le gate **ROUGE** ; le fichier propre repasse **vert**. stdlib + awk pur, PyYAML
|
||||
best-effort, zéro réseau. Ce gate est le **8ᵉ gate statique** `ci/*.sh` (câblé dans
|
||||
`gate.needs`, donc rejoué par `run_ci.sh`).
|
||||
|
||||
### `lib.sh` — helper partagé (sourcé, jamais exécuté seul)
|
||||
Les gates commençaient tous par `cd "$(git rev-parse --show-toplevel)"`. **Hors**
|
||||
d'un arbre de travail git (tarball, `git archive | tar -x`, `git` absent du PATH),
|
||||
|
||||
Reference in New Issue
Block a user