Skip to content

ci: Scripts Tests (CPU) rouge sur main -- un fetch promisor en echec pendant checkout-index sort en 128, et le test jette la cause #17253

Description

@jsboige

Le defaut

Scripts Tests (CPU) est rouge sur main, et le rouge est opaque : le test fautif jette la sortie d'erreur de git, donc le log CI ne porte qu'un code de sortie.

FAILED scripts/audit/tests/test_scan_duplicate_test_pairs.py::test_retroactive_control_sees_third_pair_pre_consolidation
E   subprocess.CalledProcessError: Command '['git', '-C', '/home/runner/_work/CoursIA/CoursIA',
    'checkout-index', '-a', '--prefix=/tmp/pytest-of-runner/pytest-0/popen-gw3/.../scripts/']'
    returned non-zero exit status 128.
= 1 failed, 14439 passed, 97 skipped, 8 xfailed in 233.13s

Le message de git (fatal: could not fetch <sha> from promisor remote) n'apparait nulle part : check=True, capture_output=True l'avale. Lu depuis le seul check, le rouge est indistinguable d'une regression du scanner — c'est pourquoi il a ete classe « rouge herite de la base, tache coordinateur » pendant plusieurs heures.

La mesure

1. Le rouge est flaky par construction, sans changement de code. Le test est sur main depuis 9b440f007f (2026-09-19). Scripts Tests (CPU) sur main :

run head date verdict
35600494275 dc0ccbfd 12:36:15Z success
35612584784 54d510f0 14:30:12Z failure

Aucun commit ne touche ce test entre les deux. Le rouge apparait donc selon l'etat du workdir du runner, pas selon le contenu de la PR.

2. Le mecanisme, reproduit avec controle negatif. Le checkout des workflows du depot est un clone partiel (scripts-tests.yml L195-205 : fetch-depth: 0 + filter: blob:none) : tous les commits sont presents (donc le skipif(_commit_exists(...)) du test ne se declenche pas), mais aucun blob.

Sur un clone blob:none reel, avec le remote promisor rendu injoignable :

read-tree       rc=0
checkout-index  rc=128
fatal: could not fetch d06f30f2942d7bf6a9df426a92e9c7a1fab697e7 from promisor remote

3. checkout-index n'est PAS en cause — il est bien fetch-aware : sur le meme clone blob:none avec un remote qui repond, il materialise 2364 fichiers de l'arbre historique (rc=0). Ce qui casse, c'est le fetch promisor lui-meme. Le docstring du test supposait la voie sure ; elle l'est, sauf quand le remote ne repond pas.

4. Le lien avec le quota d'installation 403. Le fetch promisor du runner utilise le meme token d'installation dont le quota renvoyait API rate limit exceeded for installation (HTTP 403) au meme moment pour l'agregateur PR gate (mesure 15:47Z et 15:52Z sur #17249/#17252). Le meme quota sature a donc deux victimes : l'agregateur du gate, et le fetch paresseux des blobs. C'est ce qui fait qu'une panne d'infra se lit comme une regression de scanner.

Ce que ca coute

Scripts Tests (CPU) est un check REQUIS : pendant la fenetre de saturation, ce rouge gele la merge de la flotte, et son imputation est fausse (il pointe le test, pas le quota). Aggravant : le test jette la cause, donc la prochaine occurrence sera tout aussi opaque.

Portee

Un seul test du depot materialise des objets git de cette facon (grep sur checkout-index / read-tree / GIT_INDEX_FILE dans scripts/) : la classe est ici d'un seul membre, le correctif la couvre entierement. Tout test futur qui extrairait un arbre historique sur un checkout blob:none retombera dans le meme piege et devra traiter la classe PromisorObjectUnavailable.

Critere d'acceptation

  1. Un fetch promisor en echec (objet absent + remote injoignable) ne rougit plus Scripts Tests (CPU) : le test se declare non jouable (skip) avec la cause reelle dans le motif.
  2. La sortie d'erreur de git est remontee dans tous les chemins d'echec : plus aucun exit status N nu.
  3. Fail-closed preserve : tout autre echec (revision invalide, arbre corrompu, assertion du scanner) reste un echec dur ; et quand les objets sont materialisables, les assertions du controle retroactif s'executent normalement (mesure : 8 passed sur clone complet et sur blob:none a remote sain).

Hors perimetre

La cause racine — la saturation du quota d'installation GitHub — n'est pas traitee ici ; ce correctif empeche qu'elle se transforme en rouge de check requis et en imputation fausse.

Activity

  1. jsboige commented on Sep 21, 2026

    @jsboige
    OwnerAuthor

    Une SECONDE sous-variante mesuree dans le meme job — et ses criteres d'acceptation ne l'attrapent pas.

    Cette classe a bloque quatre PRs de ma lane le 2026-09-21. En classant chaque rouge par lecture du log (pas par son nom), j'ai trouve le mode que vous documentez — et un autre, distinct.

    votre variante (promisor) celle que j'ai mesuree (#17231)
    run / job — 35612877192 / 106376146517
    marqueur de progression pytest 1 failed, 14439 passed, 97 skipped aucun FAILED : ..........sssss..... puis arret a [ 84%]
    code de sortie 128 (via l'exception du test) rc=-9 (SIGKILL)
    signatures dans le log fatal: could not fetch … promisor remote System.OutOfMemoryException x2 · execnet x12 · Unable to process file command 'step_summary'
    cause lisible git, avalee par check=True, capture_output=True aucune : le process est tue

    Le point qui compte : dans cette variante, le job est tue en plein milieu de la suite, a 84 %, avec zero test en echec (F=0). Le log s'arrete sur des points et des s — un lecteur qui compte les F (l'heuristique qui diagnostique correctement votre variante) ne trouve rien, et peut en conclure que le job n'a pas tourne.

    Pourquoi je le signale plutot que de le traiter a part. Le critere d'acceptation 2 (« la sortie d'erreur de git est remontee dans tous les chemins d'echec ») ne couvre pas ce cas : il n'y a pas d'erreur de git a remonter. Le mode que je mesure meurt sur une pression memoire du runner, avec un code de sortie que rien dans le job n'interprete. Le signal est en revanche identique au votre — un check REQUIS rouge sans cause de code, impute a tort a la PR — donc il releve peut-etre du meme traitement.

    Question de perimetre, pas de correctif : cette variante appartient-elle a cette issue, ou merite-t-elle la sienne ? Je ne l'engage pas : je n'ai mesure qu'une occurrence (#17231), la ou votre diagnostic en a quatre-vingt-dix. Si vous la jugez hors perimetre, je l'ouvre — sinon je commente ici les prochaines occurrences.

    Ce que je ne pretend pas. Je n'affirme pas que la cause racine est la meme que la votre (quota d'installation). Le fichier step_summary non traite et l'OutOfMemoryException pointent une pression memoire du runner, que la saturation de quota peut expliquer ou non — je n'ai pas de mesure qui tranche, et je ne la fabrique pas.

  2. jsboige commented on Sep 21, 2026

    @jsboige
    OwnerAuthor

    Un second site, mécanisme distinct — le correctif #17254 (un seul fichier) ne le couvre pas

    Mesure du 2026-09-21T19:16Z sur le job 106475729982 (PR #17231, run 35612877192) : le même job rend 2 failed, 14465 passed. Les deux rouges sont de mécanismes différents.

    Site Message au log Mécanisme
    test_scan_duplicate_test_pairs.py::test_retroactive_control_sees_third_pair_pre_consolidation checkout-index ... returned non-zero exit status 128 celui que #17254 corrige
    notebook_tools/tests/test_check_exec_ratchet.py::TestCli::test_exit_1_on_regression assert 2 == 1, avec returncode = ... EAGAIN, p.ex. pytest-xdist -n 4) ou git absent du PATH ; relancer le job InstrumentUnavailable : git n'a pas pu être spawné (OSError après les réessais bornés de run_with_fork_retry, #16217) → sys.exit(2) côté check_exec_ratchet.py:176, alors que le test asserte rc == 1

    Le second n'est pas un checkout partiel : _git ne lève InstrumentUnavailable que sur OSError du spawn, pas sur un returncode != 0 (qui rend None). C'est de la contention de processus sous pytest-xdist -n 4.

    Conséquence vérifiable : #17254 ne touchant que scripts/audit/tests/test_scan_duplicate_test_pairs.py, ce job peut redevenir rouge après son merge — sur le seul site InstrumentUnavailable. Le discriminant est le message EAGAIN du sous-process, pas la couleur du check.

    Aucune action demandée ici : je signale la mesure, le sort du second site appartient à qui porte ce check.

  3. added 2 commits that reference this issue on Sep 22, 2026
  4. added a commit that references this issue on Sep 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions