From bf288d58b7765d071a8f4d6e249c83b5c3623ffd Mon Sep 17 00:00:00 2001 From: Benoit Vinceneux <72924097+benoitvx@users.noreply.github.com> Date: Tue, 7 Jul 2026 19:28:52 +0200 Subject: [PATCH 1/3] feat(phases): refactor GitHub auth into _github_auth + _github_fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quatre ameliorations du bloc GitHub de phase_a : 1. Dérivation API réussie (curl 2xx + login + id) → plus de prompts 'Nom pour les commits' ni 'Email noreply GitHub'. Utilisation directe du login et de l'email noreply +@users.noreply.github.com. Affiche 'ok Compte GitHub : login ' puis persist. 2. Fallback uniquement si la dérivation échoue (curl non-2xx, réseau, parsing vide) → prompts manuels nom/email avec validation 'doit finir en users.noreply.github.com' (3 tentatives). 3. Séparation visuelle : info 'Étape suivante : colle ton PAT...' avant le prompt_secret du token, rend les 2 etapes distinctes. 4. Detection token colle a mauvaise etape : regex PAT (ghp_|github_pat_|gho_|ghs_|ghr_|.{31,}) → warn + retry. Token invalide (curl non-2xx) → warn + retry (max 3). Ne persiste jamais un token non valide. Nettoyage : gh_login=id, gh_id=login inverses → desormais gh_login=login (string), gh_id=id (numeric). --- lib/phases.sh | 192 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 138 insertions(+), 54 deletions(-) diff --git a/lib/phases.sh b/lib/phases.sh index 33999c5..5e2fef4 100644 --- a/lib/phases.sh +++ b/lib/phases.sh @@ -117,60 +117,8 @@ phase_a() { echo - # GitHub PAT (optionnel) - if confirm "Activer le push et les PR GitHub depuis la VM ? (nécessite un PAT GitHub)"; then - local gh_token - gh_token="$(prompt_secret "Colle ton PAT GitHub (scope repo ; Entrée pour passer)")" - if [ -n "$gh_token" ]; then - local git_name - local git_email_def="" gh_id="" gh_login="" - - # Dérive automatique du login, du nom et de l'email noreply via l'API GitHub - local _raw="" _http_code="" - _raw="$(curl -fsS -w "\n%{http_code}" -H "Authorization: Bearer ${gh_token}" -H "Accept: application/vnd.github+json" "https://api.github.com/user" 2>/dev/null)" || _raw="" - if [ -n "$_raw" ]; then - _http_code="$(printf '%s' "$_raw" | tail -1)" - _raw="$(printf '%s' "$_raw" | sed '$d')" - fi - if [ "${_http_code:-0}" -ge 200 ] 2>/dev/null && [ "${_http_code:-0}" -lt 300 ] 2>/dev/null && [ -n "$_raw" ]; then - gh_id="$(printf '%s' "$_raw" | sed -n 's/.*"login"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')" - gh_login="$(printf '%s' "$_raw" | sed -n 's/.*"id"[[:space:]]*:[[:space:]]*\([0-9]*\).*/\1/p')" - if [ -n "$gh_id" ] && [ -n "$gh_login" ]; then - git_name="$gh_id" - git_email_def="${gh_login}+${gh_id}@users.noreply.github.com" - ok "Compte GitHub identifié : ${gh_id} (noreply : ${git_email_def})" - fi - fi - - git_name="$(prompt_input "Nom pour les commits" "${git_name:-$(git config --global user.name 2>/dev/null)}")" - - local git_email email_attempts - email_attempts=0 - while [ "$email_attempts" -lt 3 ]; do - if [ -n "$git_email_def" ]; then - git_email="$(prompt_input "Email noreply GitHub" "$git_email_def")" - else - warn "Introuvable automatiquement. Ton email noreply est de la forme +@users.noreply.github.com, visible sur GitHub > Paramètres > Emails (https://github.com/settings/emails)." - git_email="$(prompt_input "Email noreply GitHub (doit finir en users.noreply.github.com)" "$(git config --global user.email 2>/dev/null)")" - fi - case "$git_email" in - *users.noreply.github.com) break ;; - esac - email_attempts=$((email_attempts + 1)) - if [ "$email_attempts" -lt 3 ]; then - warn "L'email doit finir par users.noreply.github.com. Réessaie (tentative $email_attempts/3)." - fi - done - if [ "$email_attempts" -ge 3 ]; then - warn "3 tentatives échouées — on accepte l'email tel quel." - fi - persist_zshenv "GH_TOKEN" "$gh_token" - persist_zshenv "AC_GIT_USER_NAME" "$git_name" - persist_zshenv "AC_GIT_USER_EMAIL" "$git_email" - else - warn "Pas de PAT GitHub — le push/PR depuis la VM restera inactif." - fi - fi + # GitHub PAT (optionnel) — gestions des 3 cas : activation, token, dérivation + _github_auth echo @@ -484,6 +432,142 @@ persist_zshenv() { [ "$DRY_RUN" -eq 0 ] && ok "$var ajoutée à ~/.zshenv" || true } +# ============================================================================= +# _github_auth — config GitHub : activation, token, dérivation API, fallback. +# ============================================================================= +_github_auth() { + local gh_token="" git_name="" git_email="" + local gh_id="" gh_login="" + local _attempt=0 _max_attempts=3 + + # Regex pour détecter un PAT collé (ghp_, github_pat_, gho_, ghs_, ghr_, >30 car.) + local _pat_regex='^(ghp_|github_pat_|gho_|ghs_|ghr_|.{31,}$)' + + while [ "$_attempt" -lt "$_max_attempts" ]; do + echo + info "Étape suivante : colle ton PAT GitHub (scope repo) pour activer le push." + + local _answer="" + printf '%sActiver le push et les PR GitHub depuis la VM ?%s [o/N] : ' "${C_BOLD}" "${C_RESET}" >&2 + if [ "$DRY_RUN" -eq 1 ]; then + printf '%s[dry-run] confirm: Activer le push et les PR GitHub depuis la VM ? → non%s\n' "${C_GREY}" "${C_RESET}" >&2 + _answer="n" + elif [ -t 0 ]; then + read -r _answer /dev/null)" || _raw="" + if [ -n "$_raw" ]; then + _http_code="$(printf '%s' "$_raw" | tail -1)" + _raw="$(printf '%s' "$_raw" | sed '$d')" + fi + + if [ "${_http_code:-0}" -ge 200 ] 2>/dev/null && [ "${_http_code:-0}" -lt 300 ] 2>/dev/null && [ -n "$_raw" ]; then + # Dérivation réussie + gh_login="$(printf '%s' "$_raw" | sed -n 's/.*"login"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')" + gh_id="$(printf '%s' "$_raw" | sed -n 's/.*"id"[[:space:]]*:[[:space:]]*\([0-9]*\).*/\1/p')" + + if [ -n "$gh_login" ] && [ -n "$gh_id" ]; then + # #1 : pas de prompts nom/email — direct + git_name="$gh_login" + git_email="${gh_id}+${gh_login}@users.noreply.github.com" + ok "Compte GitHub : ${gh_login} <${git_email}>" + persist_zshenv "GH_TOKEN" "$gh_token" + persist_zshenv "AC_GIT_USER_NAME" "$git_name" + persist_zshenv "AC_GIT_USER_EMAIL" "$git_email" + return 0 + fi + # 2xx mais parsing vide (cas rare) → token valide, persist puis fallback + persist_zshenv "GH_TOKEN" "$gh_token" + _github_fallback + return 0 + fi + + # #4 : token invalide ou API injoignable, proposer retry + _attempt=$((_attempt + 1)) + if [ "$_attempt" -lt "$_max_attempts" ]; then + warn "Échec : GitHub non connecté (token invalide ou API injoignable)." + if confirm "Réessayer la connexion GitHub ? (tentative $_attempt/$_max_attempts)"; then + continue + fi + fi + + # L'utilisateur ne veut pas retenter → fallback (pas de persistance du token non validé) + _github_fallback + return 0 + done + + # Limite de tentatives atteinte + warn "Tentatives épuisées — GitHub non connecté." + return 1 +} + +# _github_fallback — prompts manuels nom/email (quand la dérivation API échoue) +_github_fallback() { + local git_name git_email git_name_def + git_name_def="$(git config --global user.name 2>/dev/null || true)" + git_name="$(prompt_input "Nom pour les commits" "${git_name_def:-}")" + + local email_attempts=0 + while [ "$email_attempts" -lt 3 ]; do + warn "Introuvable automatiquement. Ton email noreply est de la forme +@users.noreply.github.com, visible sur GitHub > Paramètres > Emails (https://github.com/settings/emails)." + git_email="$(prompt_input "Email noreply GitHub (doit finir en users.noreply.github.com)" "$(git config --global user.email 2>/dev/null || true)")" + case "$git_email" in + *users.noreply.github.com) break ;; + esac + email_attempts=$((email_attempts + 1)) + if [ "$email_attempts" -lt 3 ]; then + warn "L'email doit finir par users.noreply.github.com. Réessaie (tentative $email_attempts/3)." + fi + done + if [ "$email_attempts" -ge 3 ]; then + warn "3 tentatives échouées — on accepte l'email tel quel." + fi + persist_zshenv "AC_GIT_USER_NAME" "$git_name" + persist_zshenv "AC_GIT_USER_EMAIL" "$git_email" +} + # copy_template — copie non-destructive copy_template() { local src="$SELF_DIR/$1" dest="$2" label="$3" From be31d2f08585ba2102d2bc57ea848e1bc79cb9ec Mon Sep 17 00:00:00 2001 From: Benoit Vinceneux <72924097+benoitvx@users.noreply.github.com> Date: Tue, 7 Jul 2026 19:28:57 +0200 Subject: [PATCH 2/3] docs: update backlog, feedback, tests for GitHub auth UX (T2-CH2, AC-R036) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BACKLOG.md : ajout ticket T2-CH2 'Simplifier et fiabiliser l'auth GitHub du wizard'. T1.8 marque S24 comme absorbée par T2-CH2. FEEDBACK.md : AC-R019 passe a traite (renvoie T2-CH2). Ajout AC-R036 (UX auth GitHub, source Romaric), traite. TESTS.md : S31 réécrit (plus de prompts nom/email quand derivation OK). Ajout S40a-d couvrant : derivation OK sans prompts, token colle a mauvaise etape, token invalide + retry, fallback manuel. --- BACKLOG.md | 13 ++++++++++++- FEEDBACK.md | 4 +++- TESTS.md | 46 ++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/BACKLOG.md b/BACKLOG.md index efd6fc4..1034ef8 100644 --- a/BACKLOG.md +++ b/BACKLOG.md @@ -107,7 +107,18 @@ Config MCP de référence : 3. **Gotcha de rotation** : documenter (README) + garde-fou — un `GH_TOKEN`/email déjà persisté dans le `~/.zshenv` de la VM n'est **pas** mis à jour par un changement côté hôte (grep-guard). Prévoir un chemin de mise à jour (réécrire la ligne `~/.zshenv` de la VM, ou `agent-vm rm` documenté). 4. **Next-steps de l'install** : mentionner l'auth GitHub dans « Prochaines étapes » (actuellement absente). **DoD :** sur un poste vierge, `install.sh` propose l'auth GitHub ; après acceptation, une VM fraîche pushe + ouvre une PR sans aucune édition manuelle de `runtime.sh` ; un email non-noreply est refusé avec un message clair. → `TESTS.md` S24. -**⏳ Implémenté (06/07/2026, branche `feat/github-auth-installer`)** — sous-points 1 (prompt token dans Phase A), 2 (identité + garde-fou email noreply, 3 tentatives) et 4 (next-steps) faits ; token jamais loggé (vérifié par canari en dry-run). **Reste** : sous-point 3 (gotcha de rotation — mise à jour d'un `~/.zshenv` VM déjà écrit) → PR séparée. Validation S24 à finaliser. +**Implémenté (06/07/2026, branche `feat/github-auth-installer`)** — sous-points 1 (prompt token dans Phase A), 2 (identité + garde-fou email noreply, 3 tentatives) et 4 (next-steps) faits ; token jamais loggé (vérifié par canari en dry-run). **Reste** : sous-point 3 (gotcha de rotation) → PR séparée. Validation S24 absorbée par T2-CH2. + +--- + +### T2-CH2 🟠 Simplifier et fiabiliser l'auth GitHub du wizard `<- AC-R036` +**But :** le bloc GitHub de `phase_a` est redondant et fragile : après la dérivation API réussie, il redemande quand même le nom et l'email (pré-remplis, donc inutiles). Et si l'utilisateur colle son token à la question o/N (cas réel bêta-testeur), le token est silencieusement ignoré. +**Tâches :** +1. **#1 — Supprimer les 2 prompts quand la dérivation réussit** : après curl 2xx + login + id, utiliser directement `git_name = login` et `git_email = +@users.noreply.github.com`. Afficher un seul récap `ok "Compte GitHub : login "`. Persister GH_TOKEN, AC_GIT_USER_NAME, AC_GIT_USER_EMAIL. +2. **#2 — Fallback uniquement si la dérivation échoue** : curl non-2xx/réseau/parsing vide → prompts manuels nom/email avec validation noreply. +3. **#3 — Séparation visuelle** : ajouter `info "Étape suivante : colle ton PAT GitHub (scope repo)."` avant le prompt_secret du token. +4. **#4 — Détection token collé à l'étape o/N** : regex `^(ghp_|github_pat_|gho_|ghs_|ghr_|.{31,}$)` → warn + retry. Token invalide (curl non-2xx) → warn + retry (max 3). Ne jamais persister un token non validé. +**DoD :** dérivation OK → aucun prompt nom/email ; token collé à l'étape o/N → message clair + retry ; token invalide → message + retry ; limites à 3 tentatives. → `TESTS.md` S31, S40. --- diff --git a/FEEDBACK.md b/FEEDBACK.md index 3ba3f14..f60be95 100644 --- a/FEEDBACK.md +++ b/FEEDBACK.md @@ -35,7 +35,7 @@ | AC-R016 | 🎛️ | 🟡 | Choix de profil (beta.gouv / La Suite / IAE / Autre) déroutant pour qui n'est pas familier de cette typologie. Question posée trop tôt, avant toute configuration. La valeur « Autre » donne l'impression d'avoir choisi par défaut. | Test utilisateur S28 2026-07 | 🆕 à trier | — | | AC-R017 | ✨ | 🟠 | Au setup, on aimerait choisir quelles skills et MCP on branche (Y/N). La config ouverte ne donne aucun contexte de ce qu'apporte chaque brique — « est-ce que j'ai besoin de playwright pour mon projet ? » | Test utilisateur S28 2026-07 | 🆕 à trier | — | | AC-R018 | 🎛️ | 🟡 | Bascule FR->EN vers le wizard agent-vm : quand install.sh passe la main à `agent-vm setup`, le wizard natif d'agent-vm (en anglais) s'affiche sans prévenir. Déroutant pour un public non-tech francophone. | Dogfood install 2026-07 | ✅ traité | `BACKLOG.md` T6.5 · `TESTS.md` S30 | -| AC-R019 | 🐛 | 🔴 | À l'étape auth GitHub, `install.sh` demande « Email noreply GitHub » avec pour défaut le vrai email de l'utilisateur, qui échoue la validation. Un utilisateur non-tech ne connaît pas son email noreply ni comment le trouver. | Dogfood install 2026-07 | 🔧 en cours (réouvert) | `BACKLOG.md` T6.6 · `TESTS.md` S31 (séd non matché, corrigé dans hotfix #2) | +| AC-R019 | 🐛 | 🔴 | À l'étape auth GitHub, `install.sh` demande « Email noreply GitHub » avec pour défaut le vrai email de l'utilisateur, qui échoue la validation. Un utilisateur non-tech ne connaît pas son email noreply ni comment le trouver. | Dogfood install 2026-07 | ✅ traité | `BACKLOG.md` T6.6 · `BACKLOG.md` T2-CH2 · `TESTS.md` S31, S40 | | AC-R020 | ⚙️ | 🟠 | `agent-vm setup` installe 4 harnais (Claude Code, OpenCode, Codex, Mistral Vibe). En test réel, l'installeur Mistral Vibe a retourné HTTP 429 (rate limit) et TOUT le setup a échoué, alors qu'Albert Code n'a besoin que d'OpenCode. Un seul installeur qui rate = base non finalisée. Le retry a fonctionné (429 transitoire), mais risque de fiabilité à l'install party (plusieurs testeurs en parallèle). Dépendance upstream agent-vm. | Dogfood install 2026-07 | 📥 backlogué | `BACKLOG.md` T6.7 | | AC-R021 | 🐛 | 🟠 | (a) install avortée sans shim : `install.sh` a `set -e` et `install_shim` est appelé APRÈS la création fragile de la VM de base. Un échec d'`agent-vm setup` (429) fait sortir l'installeur en erreur AVANT de poser le shim -> install incomplète, aucune commande `albert-code`. (b) ancien installeur MVP écrivait une fonction shell `albert-code()` dans ~/.zshrc, qui masque le shim PATH -> le nouveau shim est ignoré. | Dogfood install 2026-07 | ✅ traité | `BACKLOG.md` T6.8 · `TESTS.md` S32 | | AC-R022 | 🐛 | 🔴 | Le shim `albert-code` (généré par `install_shim`) avale les prompts interactifs : il source le script avec `2>/dev/null` (avale stderr où `confirm()`/`prompt_*` écrivent) puis appelle `$name "$@"`. Résultat : `albert-code setup` affiche l'entête puis semble figé (questions MCP/skills invisibles, `read` attend). | Dogfood install 2026-07 | ✅ traité | `BACKLOG.md` T6.9 · `TESTS.md` S33 | @@ -56,6 +56,8 @@ | AC-R033 | 🎛️ | 🟡 | L'utilisateur ne sait pas où il en est dans le setup (Phase B) : pas de compteur d'étapes. Afficher [1/4]..[4/4] serait utile. | Feedback Leo Guillaume (Tech Lead Albert API) 2026-07 | ✅ traité | `BACKLOG.md` T6.14 · `TESTS.md` S38 | | AC-R034 | 🎛️ | 🟡 | En fin de setup, aucun récapitulatif des choix (MCP actifs, skills cochées, statut GitHub). Un panneau récap aide à valider avant de lancer. | Feedback Leo Guillaume (Tech Lead Albert API) 2026-07 | ✅ traité | `BACKLOG.md` T6.14 · `TESTS.md` S38 | +| AC-R036 | 🎛️ | 🟠 | UX auth GitHub du wizard : après avoir collé le PAT, le wizard redemande quand même « Nom pour les commits » et « Email noreply GitHub » (pré-remplis), alors que le PAT donne tout via l'API. De plus, si l'utilisateur colle par erreur son token à la question o/N (au lieu de répondre o d'abord), le token est silencieusement ignoré sans message clair. | Feedback bêta-testeur 2026-07-07 (Romaric) | ✅ traité | `BACKLOG.md` T2-CH2 · `TESTS.md` S40 | + ## Notes - **AC-R004 (prompt caching)** : à instruire avant tout scaling. Vérifier si Albert API expose du caching sur `chat/completions` ; sinon, arbitrer l'infra d'inférence. vLLM implémente le caching nativement. diff --git a/TESTS.md b/TESTS.md index 8ac9ae8..8f0b656 100644 --- a/TESTS.md +++ b/TESTS.md @@ -237,17 +237,16 @@ et le bloc marqueur a disparu. 3. Dans `phase_run()`, répondre « oui » à la création de la VM (dry-run). **Attendu :** un encart `info` en français s'affiche avant `agent-vm setup`, mentionnant que le wizard est en anglais, expliquant ce qu'est agent-vm, et conseillant de valider les logiciels par défaut. Sortie identique en `check_base_vm` et `phase_run`. -## S31 — Dérivation automatique email noreply GitHub (T6.6, AC-R019) ☐ +## S31 — Dérivation automatique identité GitHub (T6.6, AC-R019, T2-CH2) ☐ **Préconditions :** un PAT GitHub valide (scope `repo`) ; `curl` disponible. **Étapes :** 1. Lancer `./install.sh` (ou `albert-code install`). 2. Répondre « oui » à l'activation push/PR GitHub. 3. Coller le PAT. -4. Vérifier que le prompt « Email noreply GitHub » affiche le noreply dérivé (ex. `12345+username@users.noreply.github.com`). -5. Faire Entrée. -6. Vérifier `~/.zshenv` : `AC_GIT_USER_EMAIL` = noreply GitHub. -7. (Fallback) Simuler l'absence de réseau : sans `curl` ou PAT invalide, vérifier que le message FR d'aide s'affiche. -**Attendu :** (4) le noreply est pré-rempli, l'utilisateur fait Entrée ; (6) email noreply persisté ; (7) message FR d'aide clair avec lien GitHub. Le PAT n'apparaît dans aucun log. +4. Vérifier AUCUN prompt « Nom pour les commits » ni « Email noreply GitHub » (dérivation réussie → tout est automatique). +5. Vérifier le récap : ok "Compte GitHub : <+@users.noreply.github.com>" +6. Vérifier `~/.zshenv` : `AC_GIT_USER_NAME` = login, `AC_GIT_USER_EMAIL` = noreply GitHub. +**Attendu :** (4) pas de prompts ; (5) récap correct ; (6) identité persistée. Le PAT n'apparaît dans aucun log. ## S32 — Shim avant VM + migration ancien `albert-code()` (T6.8, AC-R021) ☐ **Préconditions :** `install.sh` ou `bin/albert-code install` ; `albert-code()` dans un shell rc (ex. `~/.zshrc`). @@ -301,6 +300,41 @@ et le bloc marqueur a disparu. 6. Répéter le setup avec `CONTEXT7_API_KEY` déjà dans l'env → vérifier qu'aucun prompt Context7 n'apparaît (le MCP est activé normalement). **Attendu :** (2) ASCII art présent au début de Phase B. (3) `print_next_steps` raccourci, plus de « Crée la VM de base », plus de « Ouvre la bulle isolée », plus de « Parle en français ». (4) statut GitHub AVANT le ✓ final, pas après. (5) clé demandée, warning si vide. (6) pas de prompt si clé déjà présente. +## S40 — Auth GitHub : dérivation OK, token collé à mauvaise étape, token invalide (T2-CH2, AC-R036) ☐ + +**S40a — Dérivation API OK → pas de prompts nom/email, juste le récap** +**Préconditions :** `install.sh` disponible, `curl`, PAT valide (scope repo). +**Étapes :** +1. Lancer `bash lib/phases.sh` (sourcer) et appeler `_github_auth` avec un vrai PAT (ou stub `api.github.com/user`). +2. Répondre `o` au prompt d'activation, coller le PAT valide. +3. Observer la sortie. +**Attendu :** aucun prompt « Nom pour les commits » ni « Email noreply GitHub ». Le seul message est `✓ Compte GitHub : <+@users.noreply.github.com>`. `AC_GIT_USER_NAME` = login, `AC_GIT_USER_EMAIL` = noreply. + +**S40b — Token collé à la question o/N → message d'échec + retry** +**Préconditions :** `install.sh` disponible, un token quelconque. +**Étapes :** +1. Lancer `install.sh`. +2. Au prompt « Activer le push et les PR GitHub depuis la VM ? [o/N] », coller un token (commence par `ghp_`, `github_pat_`, ou chaîne >30 car. non reconnue comme o/n). +3. Observer le message. +**Attendu :** le script affiche `! On dirait que tu as collé ton token à la mauvaise étape (il faut d'abord répondre o, puis coller le token).` puis propose `? Réessayer la connexion GitHub ?`. Répondre non → `! GitHub non connecté.` + +**S40c — Token invalide → message d'échec + retry** +**Préconditions :** `install.sh` disponible, un token invalide. +**Étapes :** +1. Lancer `install.sh`. +2. Répondre `o` à l'activation, coller un token invalide. +3. Observer le message. +**Attendu :** le script affiche `! Échec : GitHub non connecté (token invalide ou API injoignable).` puis propose `? Réessayer la connexion GitHub ? (tentative 1/3)`. Répondre non → fallback prompts manuels nom/email. Répondre oui 3 fois → `! Tentatives épuisées — GitHub non connecté.` + +**S40d — Fallback quand dérivation échoue** +**Préconditions :** `install.sh` disponible, token quelconque (même invalide). +**Étapes :** +1. Lancer `install.sh`. +2. Répondre `o` à l'activation, coller un token invalide. +3. Au message d'échec, répondre non au retry. +4. Observer les prompts manuels. +**Attendu :** le script affiche le message d'aide FR `! Introuvable automatiquement. Ton email noreply est de la forme +@users.noreply.github.com`, puis demande le nom et l'email avec validation du suffixe. L'email ne passant pas la validation noreply, le message `! L'email doit finir par users.noreply.github.com.` s'affiche après 3 tentatives. + ## S36 — `install_shim` réécrit un shim obsolète + sortie sync_skills propre (T6.12, T6.13) ☐ **Préconditions :** dossier sandbox `/tmp/ac-test`, `install.sh` disponible. **Étapes (install_shim) :** From c904483761f0b683f1d317a5267752b42b978bb4 Mon Sep 17 00:00:00 2001 From: Benoit Vinceneux <72924097+benoitvx@users.noreply.github.com> Date: Tue, 7 Jul 2026 20:03:45 +0200 Subject: [PATCH 3/3] fix(phases): restructure _github_auth retry to use prompt_secret (masque) only Bug E2E : le retry rebouclait sur la question d'activation [o/N] non masquee, exposant un token colle en clair et faisant perdre le token sur la confirmation 'Reessayer ? [o/N]'. Correction : 1. Activation [o/N] posee UNE SEULE fois (hors boucle). 2. Token colle au [o/N] -> avertissement securite (revocation recommandee) + transition DIRECTE vers prompt_secret masque. 3. Token invalide -> warn + reboucle sur prompt_secret (masque) avec 'Recolle ton PAT (tentative N/3)'. Jamais de [o/N] en retry. 4. Ne jamais persister un token non valide. Adaptation TESTS.md S40 : retry sur prompt masque, token valide au 2e essai connecte directement (pas de fallback). --- TESTS.md | 28 ++++++++----- lib/phases.sh | 106 ++++++++++++++++++++++++++------------------------ 2 files changed, 74 insertions(+), 60 deletions(-) diff --git a/TESTS.md b/TESTS.md index 8f0b656..53cc020 100644 --- a/TESTS.md +++ b/TESTS.md @@ -310,30 +310,38 @@ et le bloc marqueur a disparu. 3. Observer la sortie. **Attendu :** aucun prompt « Nom pour les commits » ni « Email noreply GitHub ». Le seul message est `✓ Compte GitHub : <+@users.noreply.github.com>`. `AC_GIT_USER_NAME` = login, `AC_GIT_USER_EMAIL` = noreply. -**S40b — Token collé à la question o/N → message d'échec + retry** +**S40b — Token collé à la question o/N → message d'échec + avertissement sécurité + transition vers prompt masqué** **Préconditions :** `install.sh` disponible, un token quelconque. **Étapes :** 1. Lancer `install.sh`. 2. Au prompt « Activer le push et les PR GitHub depuis la VM ? [o/N] », coller un token (commence par `ghp_`, `github_pat_`, ou chaîne >30 car. non reconnue comme o/n). -3. Observer le message. -**Attendu :** le script affiche `! On dirait que tu as collé ton token à la mauvaise étape (il faut d'abord répondre o, puis coller le token).` puis propose `? Réessayer la connexion GitHub ?`. Répondre non → `! GitHub non connecté.` +3. Observer les messages. +**Attendu :** le script affiche `! On dirait que tu as collé ton token à la mauvaise étape (réponds d'abord o).` puis `! Ce token vient d'être affiché en clair dans le terminal : pense à le révoquer` et `! et à en régénérer un (github.com/settings/tokens).` puis `? Continuer la connexion GitHub ?`. Répondre oui → on passe DIRECTEMENT au prompt masqué `prompt_secret` (ETAPE TOKEN, pas de re-demande de [o/N]). Répondre non → `! GitHub non connecté.` -**S40c — Token invalide → message d'échec + retry** +**S40c — Token invalide → message d'échec + retry sur prompt masqué (pas de [o/N])** **Préconditions :** `install.sh` disponible, un token invalide. **Étapes :** 1. Lancer `install.sh`. 2. Répondre `o` à l'activation, coller un token invalide. 3. Observer le message. -**Attendu :** le script affiche `! Échec : GitHub non connecté (token invalide ou API injoignable).` puis propose `? Réessayer la connexion GitHub ? (tentative 1/3)`. Répondre non → fallback prompts manuels nom/email. Répondre oui 3 fois → `! Tentatives épuisées — GitHub non connecté.` +**Attendu :** le script affiche `! Échec : GitHub non connecté (token invalide ou API injoignable).` puis `! Tentative 1/3 — recolle ton PAT dans le champ masqué ci-dessous.` et le prompt masqué `prompt_secret` réapparaît (pas de [o/N]). Au 3e échec → fallback prompts manuels nom/email. -**S40d — Fallback quand dérivation échoue** -**Préconditions :** `install.sh` disponible, token quelconque (même invalide). +**S40d — Token valide au 2e essai après un premier échec → connexion directe, pas de fallback** +**Préconditions :** `install.sh` disponible, un token invalide puis un token valide. **Étapes :** 1. Lancer `install.sh`. 2. Répondre `o` à l'activation, coller un token invalide. -3. Au message d'échec, répondre non au retry. -4. Observer les prompts manuels. -**Attendu :** le script affiche le message d'aide FR `! Introuvable automatiquement. Ton email noreply est de la forme +@users.noreply.github.com`, puis demande le nom et l'email avec validation du suffixe. L'email ne passant pas la validation noreply, le message `! L'email doit finir par users.noreply.github.com.` s'affiche après 3 tentatives. +3. Voir le message d'échec + prompt masqué réapparaître. +4. Coller un token valide. +**Attendu :** le script dérive l'identité, affiche `✓ Compte GitHub : <+@users.noreply.github.com>` et persiste. Pas de fallback manuel, pas de prompts nom/email. + +**S40e — Fallback quand token invalide au 3e essai** +**Préconditions :** `install.sh` disponible, token invalide. +**Étapes :** +1. Lancer `install.sh`. +2. Répondre `o` à l'activation, coller un token invalide 3 fois (Entrée entre chaque pour abandonner). +3. Observer les prompts manuels. +**Attendu :** le script affiche le message d'aide FR `! Introuvable automatiquement...` puis demande le nom et l'email. Ne jamais persister GH_TOKEN. ## S36 — `install_shim` réécrit un shim obsolète + sortie sync_skills propre (T6.12, T6.13) ☐ **Préconditions :** dossier sandbox `/tmp/ac-test`, `install.sh` disponible. diff --git a/lib/phases.sh b/lib/phases.sh index 5e2fef4..b06c744 100644 --- a/lib/phases.sh +++ b/lib/phases.sh @@ -433,7 +433,8 @@ persist_zshenv() { } # ============================================================================= -# _github_auth — config GitHub : activation, token, dérivation API, fallback. +# _github_auth — config GitHub : activation UNE FOIS, token masqué, retry +# sur prompt_secret uniquement (jamais [o/N]). # ============================================================================= _github_auth() { local gh_token="" git_name="" git_email="" @@ -443,55 +444,65 @@ _github_auth() { # Regex pour détecter un PAT collé (ghp_, github_pat_, gho_, ghs_, ghr_, >30 car.) local _pat_regex='^(ghp_|github_pat_|gho_|ghs_|ghr_|.{31,}$)' - while [ "$_attempt" -lt "$_max_attempts" ]; do - echo - info "Étape suivante : colle ton PAT GitHub (scope repo) pour activer le push." - - local _answer="" - printf '%sActiver le push et les PR GitHub depuis la VM ?%s [o/N] : ' "${C_BOLD}" "${C_RESET}" >&2 - if [ "$DRY_RUN" -eq 1 ]; then - printf '%s[dry-run] confirm: Activer le push et les PR GitHub depuis la VM ? → non%s\n' "${C_GREY}" "${C_RESET}" >&2 - _answer="n" - elif [ -t 0 ]; then - read -r _answer &2 + if [ "$DRY_RUN" -eq 1 ]; then + printf '%s[dry-run] confirm: Activer le push et les PR GitHub depuis la VM ? → non%s\n' "${C_GREY}" "${C_RESET}" >&2 + _answer="n" + elif [ -t 0 ]; then + read -r _answer " @@ -524,23 +534,19 @@ _github_auth() { return 0 fi - # #4 : token invalide ou API injoignable, proposer retry + # Échec : token invalide ou API injoignable _attempt=$((_attempt + 1)) if [ "$_attempt" -lt "$_max_attempts" ]; then warn "Échec : GitHub non connecté (token invalide ou API injoignable)." - if confirm "Réessayer la connexion GitHub ? (tentative $_attempt/$_max_attempts)"; then - continue - fi + warn "Tentative $_attempt/${_max_attempts} — recolle ton PAT dans le champ masqué ci-dessous." + # Rebouble DIRECTEMENT sur prompt_secret (pas de [o/N]) + continue fi - # L'utilisateur ne veut pas retenter → fallback (pas de persistance du token non validé) + # Tentatives épuisées — fallback sans persister le token non validé _github_fallback return 0 done - - # Limite de tentatives atteinte - warn "Tentatives épuisées — GitHub non connecté." - return 1 } # _github_fallback — prompts manuels nom/email (quand la dérivation API échoue)