Skip to content

fix(ci): le bootstrap body-live prend le document d'erreur de gh api pour le corps de la PR #17259

Description

@jsboige

Le defaut

Le bootstrap Body live de .github/workflows/always-on-guards.yml decide du repli sur la vacuite de la sortie de gh api :

LIVE=$(gh api "repos/$GH_REPO/pulls/$PR_NUMBER" --jq '.body // empty' 2>/dev/null || true)
if [ -n "$LIVE" ]; then BODY="$LIVE"; else BODY="${PAYLOAD_BODY:-}"; fi

Or gh api, sur une erreur HTTP, sort en rc=1 ET ecrit le document d'erreur sur stdout — et --jq n'y est pas applique. Mesure firsthand (2026-09-21) :

$ gh api "repos/jsboige/does-not-exist-xyz/pulls/1" --jq '.body // empty' 2>/dev/null
rc=1
stdout=[{"message":"Not Found","documentation_url":"https://docs.github.com/rest/pulls/pulls#get-a-pull-request","status":"404"}]

Le || true avale le code de sortie, $LIVE est non vide (c'est le document d'erreur), et le repli -- ecrit precisement pour la panne d'API, cf le commentaire du bloc : « Repli : appel API vide/echoue -> payload de l'evenement. Jamais bloquant sur une panne de lecture. » -- ne se declenche jamais. C'est l'intention declaree du bloc qui n'est pas tenue par son code.

Sur quota epuisi, PR_BODY recoit donc un document d'erreur a la place du corps de la PR.

Ce que ca coute, mesure

Le meme jour, quatre PRs de la lane myia-po-2023:CoursIA (#17037, #17135, #17136, #17238) portent Always-on guards rouge, et le log de la jambe est inonde de API rate limit exceeded for installation (17 a 18 occurrences par log). Les organes bloquants qui consomment PR_BODY en tirent un verdict de contenu :

Organe Verdict rendu Ce qui s'est reellement passe
tag_required {"required_pass": false, "reason": "Grain tag absent (no Grain: <TIER>/<GENRE> in body)"} le tag etait present ; le document 403 n'a pas de champ .body
perimeter ::error::A perimeter assertion on this PR ... contradicts the effective file list precedee de gh error: ... API rate limit exceeded for installation (HTTP 403)

Une panne de quota est donc presentee comme une faute de lane. Le cout n'est pas seulement le rouge : le picker en derive ROUGE IMPUTE A LA BASE et la lane est dissuadee de chercher une cause qu'elle pourrait corriger.

Pourquoi l'organe de test ne l'a pas vu

scripts/tests/test_always_on_guards_live_body.py (livre par #15697) execute le bloc avec un gh factice. Ce faux sortait sur echec en exit 1 sans rien ecrire -- il modelisait « echoue => sortie vide », qui n'est pas le comportement de l'outil. Le test test_bootstrap_falls_back_to_payload_on_api_failure etait donc vert pendant que le defaut etait vivant : il validait le repli dans le seul cas ou le repli marchait.

Perimetre

Le motif (capturer la sortie de gh puis decider sur sa vacuite au lieu du code de sortie) est unique dans le depot : une seule occurrence, .github/workflows/always-on-guards.yml:211.

Correctif

Rendre le faux fidele (rc=1 et document d'erreur sur stdout), ce qui fait rougir le test existant, puis decider sur le code de sortie dans le workflow. Voir la PR liee.

Activity

  1. added a commit that references this issue on Sep 23, 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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions