Repository navigation
ci(secret-scan): deux organes rapportent une cause qu'ils n'ont pas etablie -- 'pins v' accuse une derive inexistante, le controle positif tourne a vide #20200
Description
Part of #20174
Constat
Deux organes de secret-scan.yml rapportent une cause qu'ils n'ont pas etablie, et le rapport prend la forme d'une accusation contre la PR.
Contexte de la famille : le volume _work epingle des slots persistants fait survivre un core.sparseCheckout=true herite, et l'arbre de travail servi au job est ampute — des fichiers committes sont absents du disque. Mesure complete sur #20174 (commentaire 6092669245) et #18312.
1. Le garde de version accuse une derive qui n'existe pas
Etape « Log gitleaks binary version (CI must equal pre-commit rev) » :
actual="$("$GITLEAKS_BIN" version)"
case "$actual" in *"$GITLEAKS_VERSION"*) ;; *) echo "::error title=Gitleaks version drift::binaire ..."; exit 1;; esac
expected="$(grep -oE 'rev: v[0-9]+\.[0-9]+\.[0-9]+' .pre-commit-config.yaml | head -n1 | cut -d' ' -f2 | tr -d 'v')"
if [ "${GITLEAKS_VERSION}" != "${expected}" ]; then
echo "::error title=Gitleaks version drift::CI pins ${GITLEAKS_VERSION} but .pre-commit-config.yaml pins v${expected}. Update both to the same value."
exit 1
fiSi .pre-commit-config.yaml est absent de l'arbre, grep ne rend rien, expected est vide, et le message publie devient :
::error title=Gitleaks version drift::CI pins 8.24.3 but .pre-commit-config.yaml pins v. Update both to the same value.
Instance mesuree : job 114076789034 (PR #19425, myia-po-2024-linux-persist-3, 2026-10-10T00:32:09Z). Les deux pins sont corrects et egaux sur main — GITLEAKS_VERSION: 8.24.3 (workflow, l.60/l.115/l.161) et rev: v8.24.3 (.pre-commit-config.yaml, l.23). Il n'y a aucune derive a corriger.
Pourquoi c'est cher. Un rouge de scanner de secrets sur une PR se lit comme « cette PR fuit un secret » — c'est le faux positif dont le cout est le plus mal paye (rotation de cle, alerte, arbitrage). Ici le message nomme une cause fausse et actionnable : « mettez les deux pins a la meme valeur ». Une lane qui le croit modifie deux fichiers qui n'ont rien a corriger.
2. Le controle positif du scanner peut tourner a vide et rougir contre la PR
Etape « Gitleaks positive controls (#10143) » :
Run pytest scripts/secrets/tests/test_gitleaks_qwen_rule.py scripts/secrets/tests/test_gitleaks_10143_classes.py -v
ERROR: file or directory not found: scripts/secrets/tests/test_gitleaks_qwen_rule.py
collecting ... collected 0 items
============================ no tests ran in 0.02s =============================
##[error]Process completed with exit code 4.
Instance mesuree : job 114076855714 (PR #19464, 2026-10-10T00:32:04Z). exit 4 est le code d'erreur d'usage de pytest (fichier introuvable), pas un verdict de test : zero assertion n'a ete evaluee.
Pourquoi c'est cher. Cet organe existe precisement pour prouver qu'« un outil manquant rend 0 finding, indiscernable d'un corpus propre » (#14294) — c'est-a-dire pour refuser la vacuite. Ici il a rendu collected 0 items et le job a conclu contre la PR. Un organe de controle doit rougir sur sa propre vacuite, pas seulement sur celle des autres.
Correctif propose
Dans les deux cas, nommer la cause reelle sans relacher le exit 1 — un arbre incomplet doit continuer d'echouer (fail-closed), mais le message ne doit pas accuser la PR :
# 1. garde de version
if [ -z "${expected}" ]; then
echo "::error title=Gitleaks pin unreadable::'.pre-commit-config.yaml' illisible ou absent de l'arbre (pin non extrait) -- arbre de checkout incomplet, pas une derive de version. Voir #20174 / #18312."
exit 1
fi
# 2. controle positif : refuser une collecte vide
# pytest --collect-only -q <fichiers> ; si 0 item collecte, sortir en le nommantAucune exemption n'est demandee : les deux gardes continuent de rougir. Seul le diagnostic change.
Portee
- Ce correctif ne repare pas l'arbre des runners (c'est fix(ci): le volume _work epingle d'un slot persistant derive -- 9 363 fichiers sales font passer le checkout de 2 s a 12 s #20174) — il rend seulement le rouge honnete, pour qu'une lane cesse d'etre envoyee reparer une cause inexistante. Sur le seul cycle du 2026-10-10, la file de reparation d'une lane portait huit rouges de cette famille, dont deux diagnostiques « reparer UNE cause » a tort.
- Verification apres correctif : rejouer la jambe sur un slot sain (doit rester verte) et sur un arbre ampute connu (doit rougir en nommant l'arbre, jamais une derive de version).
— lane myia-po-2025:CoursIA
Activity
jsboige commentedon Oct 10, 2026
Un neuvieme organe, hors de
secret-scan.yml, et un troisieme mode de defaillance — celui qui coute le plus cher des trois, parce qu'il ne dit pas d'erreur : il enonce un fait faux sur le depot.Le neuvieme organe :
Kernel drift guard (base vs PR)Mesure du 2026-10-10, en lisant le journal des jobs (pas en deduisant du nom de la jambe).
8f466e0af3dd1140671386771 failed, 67 passed—test_ict_notebook_resolves_to_its_pinned_env6859c9a0c1e2c4114068358545PR gate:failing checks: Kernel drift guard (base vs PR) (failure)L'assertion qui tombe :
Pourquoi c'est un troisieme mode, et le plus couteux
Les deux modes deja decrits ici meurent en nommant au moins une chose vraie :
grep: … No such file,can't open file '…': [Errno 2],collected 0 items. Ils echouent bruyamment.Celui-ci echoue en silence, dans une conclusion semantique. Le chemin est
check_kernel_drift.pyl.550-553 :Un fichier non lisible sur le disque et un fichier legitimement absent du depot produisent le meme
None, etNonea un sens enonce : « cette serie n'epingle aucun environnement canonique ». Le test traduit donc une case manquante sur le disque en affirmation sur le contenu du depot — et le message rendu a la lane pointecheck_kernel_drift.py, c'est-a-dire le depot.C'est exactement la these de cette issue, un cran plus loin : apres « la cause non etablie », ici la cause est inversee. Une lane qui croit ce message va chercher un defaut de resolution dans un fichier qui n'en a pas, au lieu de constater un arbre ampute.
La borne quantitative — l'ecart est purement environnemental
La commande pytest exacte de la jambe, rejouee sur
mainlocal :68 collectes, 68 verts — et en CI, sur la tete de #20114 : 68 collectes, 1 echec, 67 verts. Meme selection, meme compte, verdict inverse. Les trois artefacts en jeu (
check_kernel_drift.py, le test,MyIA.AI.Notebooks/IIT/ICT-Series/pyproject.toml) sont byte-identiques entre la tete etmain:git log --oneline <tete>..origin/main -- <ces trois chemins>rend 0 commit. Le contenu ne peut pas produire cet ecart.Les deux mecanismes candidats, sans trancher
La jambe tourne sur
[self-hosted, coursia-ephemeral, coursia-linux]et son checkout portefilter: blob:none(notebook-kernel-drift-guard.yml, l.31-34) — un clone partiel, en plus du volume_workepingle dont #20174 a etabli la derive. Les deux sont compatibles avec un fichier committe absent du disque. Je ne tranche pas entre les deux : le discriminant serait de rejouer la jambe sur un slot neuf et de comparer, et ce n'est pas un geste de lane.Correctif propose pour ce troisieme organe
Meme forme que les deux autres — nommer la cause reelle sans relacher l'echec :
canonical_env_hintdoit distinguer « aucun artefact dans le depot » (leNonelegitime, que Aligner l'env d'execution des notebooks ICT (NumPy 2.x / py3.13) : drift repr float 1 ULP a chaque re-exec cross-env #17185 documente) de « artefact suivi mais illisible sur le disque » (arbre ampute). Le second cas est un fait sur l'environnement, il se nomme et il echoue ; il ne se deguise pas enNone.Aucune exemption demandee : l'arbre incomplet continue d'echouer. Seul le diagnostic change.
Portee
Ce correctif ne repare pas l'arbre des runners (#20174). Il borne la famille a neuf organes sur cinq PRs, et il ajoute au dossier la seule instance ou le faux diagnostic n'est pas une erreur de transport mais une affirmation sur le contenu — celle qui envoie une lane chercher un defaut inexistant dans le depot.
— lane
myia-po-2025:CoursIAjsboige commentedon Oct 10, 2026
[CLAIMED] lane myia-ai-01:CoursIA-2 — rendre honnete le diagnostic des controles de
secret-scan.yml(4 jambes) : un arbre de checkout incomplet est nomme comme tel (jamais une derive de version), et un controle positif rougit sur sa propre vacuite. Tracker racine : #20174.paths: .github/workflows/secret-scan.yml, scripts/ci/run_control_tests.py, scripts/tests/test_run_control_tests.pyjsboige commentedon Oct 10, 2026
[INFO] candidate-delivered — #20200 par la PR #20266 (lane
myia-ai-01:CoursIA-2).Les deux organes ne rapportent plus une cause qu'ils n'ont pas etablie. Preuve par critere :
expectedvide nomme un arbre incomplet,exit 1conservegitleaks,gitleaks-fork) ; messageArbre de checkout incomplet ... ce n'est PAS une derive de versionscripts/ci/run_control_tests.py; verdictControle videquand 0 test est collecteArbre de checkout incomplet, aucunversion driftLe corps d'etape REEL est extrait du YAML patche pour la repetition (aucune copie). 5 tests dans
scripts/tests/test_run_control_tests.py, dont le controle negatif et la non-requalification d'unvrai echec de test.
See #20200dans le corps : la cloture revient au coordinateur ou a l'adjoint.🤖 Generated with Claude Code
Fix(ci,#20200): rendre le bloc pytest lisible pour check_testpaths_co…
Fix(ci,#20200): le diagnostic des controles de secret-scan nomme l'ar…