Mode headless et CI/CD : Claude Code comme brique d'automatisation
Transformez Claude Code en brique d'automatisation : mode headless claude -p, pipes Unix, sortie JSON, GitHub Actions et garde-fous pour une CI sûre.
- Scripter Claude Code en mode headless avec claude -p et exploiter sa sortie JSON
- Verrouiller une exécution non interactive avec --allowedTools, --permission-mode et --max-turns
- Intégrer Claude Code dans GitHub Actions et maîtriser les coûts en CI
Depuis le début de cette formation, vous dialoguez avec Claude Code. Vous tapez, il répond, vous validez. Dans ce module, on coupe le dialogue. Claude Code devient une commande silencieuse, comme grep ou git : vous la lancez, elle travaille, elle rend son résultat, elle s’arrête. C’est le mode headless, et c’est la clé qui ouvre l’automatisation : scripts shell, intégration continue, GitHub Actions. À la fin de ce module, Claude travaillera pour vous même quand vous n’êtes pas devant l’écran.
Ce que vous allez apprendre
- Lancer Claude Code en mode headless avec
claude -pet le brancher sur des pipes Unix - Récupérer une sortie structurée en JSON, exploitable dans n’importe quel script
- Verrouiller une exécution sans humain avec
--allowedTools,--permission-modeet--max-turns - Appliquer trois recettes concrètes : migration de masse, triage d’issues, revue de pull request
- Câbler votre dépôt GitHub pour qu’une simple mention
@claudedéclenche le travail
Le déclic : Claude Code est une commande Unix comme les autres
Le mot headless signifie « sans tête ». Comprenez : sans interface interactive. Pas de conversation, pas de questions-réponses. Vous donnez un prompt, Claude Code l’exécute, imprime le résultat, et rend la main. C’est l’option -p (comme print) qui active ce mode.
# Une question, une réponse, terminé
claude -p "Que fait le module d'authentification de ce projet ?"
Ça paraît anodin. C’est en réalité un changement de nature. Une commande qui lit l’entrée standard et écrit sur la sortie standard, c’est la définition même d’un outil Unix. Et un outil Unix, ça se branche sur tout : des scripts, des tâches planifiées, des chaînes d’intégration continue.
Des pipes pour tout brancher
Un pipe, c’est le symbole | dans votre terminal. Il branche la sortie d’une commande sur l’entrée de la suivante, comme un tuyau entre deux machines. Claude Code sait lire ce qui arrive par ce tuyau.
# Le contenu du fichier de log part dans le tuyau,
# Claude le reçoit et applique le prompt
cat erreurs.log | claude -p 'explique la cause racine de ces erreurs'
Et comme toute commande Unix, la réponse peut repartir dans un fichier ou vers une autre commande.
# La réponse est écrite dans un fichier de rapport
cat erreurs.log | claude -p 'explique la cause racine' > rapport.txt
Retenez cette image : Claude Code devient un maillon de chaîne. Ce qui entre, ce qui sort, tout se raccorde.
Une sortie que vos scripts comprennent
Un texte libre, c’est agréable pour un humain. Pour un script, c’est fragile. L’option --output-format règle le problème avec trois valeurs : text (le défaut), json et stream-json.
Avec json, la réponse arrive dans un objet structuré : le texte dans le champ result, l’identifiant de session dans session_id, et même le coût de l’appel dans total_cost_usd. Votre script peut extraire exactement ce dont il a besoin.
# jq est l'outil standard pour lire du JSON en ligne de commande
# L'option -r extrait le texte brut, sans guillemets
claude -p "Résume ce projet" --output-format json | jq -r '.result'
Le format stream-json, lui, émet un objet JSON par ligne, au fil de l’eau. Utile quand vous voulez suivre une longue exécution en temps réel, événement par événement.
💡 Astuce — Le champ
total_cost_usdde la sortie JSON vous donne le coût exact de chaque appel API. Enregistrez-le dans vos journaux de CI : vous saurez précisément ce que coûte chaque automatisation, sans consulter aucun tableau de bord.
Garder le contrôle quand personne ne surveille
En mode interactif, Claude vous demande la permission avant chaque action sensible. En mode headless, il n’y a personne pour répondre. Tout doit donc être déclaré à l’avance. Trois options forment votre ceinture de sécurité.
La première, --allowedTools, est une liste blanche stricte : vous énumérez les outils autorisés, tout le reste est refusé. La syntaxe reprend les règles de permission vues au module sur la configuration.
# Lecture, recherche, et uniquement les commandes npm run test:*
claude -p "Lance les tests et corrige les échecs" \
--allowedTools "Read,Grep,Edit,Bash(npm run test:*)"
La deuxième, --permission-mode, fixe un cadre global. Le mode plan, par exemple, met Claude en lecture seule : parfait pour des analyses sans aucun risque d’écriture.
# Analyse en lecture seule : aucun fichier ne sera modifié
claude -p "Audite la gestion des erreurs de ce projet" --permission-mode plan
La troisième, --max-turns, plafonne le nombre d’allers-retours entre Claude et ses outils. C’est votre coupe-circuit : une tâche qui s’emballe s’arrête d’elle-même au lieu de tourner sans fin.
Reste l’authentification. En CI, pas de /login interactif : la clé API passe par la variable d’environnement ANTHROPIC_API_KEY, stockée dans le coffre à secrets de votre plateforme. Jamais dans le code, jamais dans un fichier versionné.
⚠️ Piège courant — Oublier
--allowedToolsen headless ne rend pas Claude tout-puissant, c’est l’inverse : dès qu’il tente une action non autorisée, l’exécution se bloque ou échoue. Votre script rend alors un résultat incomplet sans que vous compreniez pourquoi. Déclarez toujours explicitement ce dont la tâche a besoin, et rien de plus.
Trois recettes d’automatisation qui changent le quotidien
La théorie est en place. Passons à la pratique avec trois recettes directement réutilisables.
Recette 1 : la migration de masse, fichier par fichier
Imaginez 200 fichiers à convertir de JavaScript vers TypeScript. En session interactive, le contexte déborderait vite. En headless, chaque fichier a droit à son propre appel, avec un contexte tout neuf : chaque itération est isolée, vérifiable, et un commit la fige.
#!/bin/bash
# Migration de masse : un appel Claude par fichier, un commit par fichier
for fichier in $(find src -name "*.js"); do
echo "Migration de $fichier..."
claude -p "Convertis $fichier en TypeScript. Ne modifie aucun autre fichier." \
--allowedTools "Read,Edit,Write" \
--max-turns 10
# Verification immediate : les tests valident chaque etape
npm test || echo "$fichier" >> a-verifier.txt
git add -A && git commit -m "migration TS : $fichier"
done
Si un fichier pose problème, il atterrit dans a-verifier.txt et la boucle continue. Le lendemain matin, vous relisez la liste des cas douteux au lieu de surveiller 200 conversions une à une.
Recette 2 : le triage automatique des issues GitHub
Deuxième recette : labelliser et prioriser les tickets entrants. On s’appuie sur gh, l’outil en ligne de commande officiel de GitHub, et on demande à Claude une réponse ultra-courte, facile à réinjecter.
#!/bin/bash
# Triage : Claude choisit un label, gh l'applique
numero=42
label=$(gh issue view "$numero" --json title,body | \
claude -p "Voici une issue GitHub en JSON. Choisis UN label parmi : bug, amelioration, documentation, question. Reponds uniquement par le label, rien d'autre.")
gh issue edit "$numero" --add-label "$label"
Le même principe s’étend à la priorisation (répondre haute, moyenne ou basse) ou à la détection de doublons : donnez à Claude la liste des titres d’issues ouvertes et demandez-lui les paires probablement identiques. La consigne « réponds uniquement par… » est le secret : elle transforme un modèle bavard en composant prévisible.
Recette 3 : revue de pull request et changelog générés
Troisième recette : faire relire chaque pull request avant la revue humaine. Le diff part dans le tuyau, le commentaire revient dans la PR.
# Le diff de la PR 128 est relu, le verdict est poste en commentaire
gh pr diff 128 | \
claude -p "Relis ce diff en relecteur senior : bugs, failles, cas limites. Sois concis." \
--output-format json | jq -r '.result' | \
gh pr comment 128 --body-file -
Et pour le changelog, ce journal des modifications que personne n’aime rédiger, la matière première existe déjà : vos messages de commit.
# Les commits depuis la derniere version deviennent un changelog propre
git log --oneline v1.4.0..HEAD | \
claude -p "Transforme ces commits en changelog en francais, groupe par categorie : nouveautes, corrections, interne."
💡 Astuce — Dans toutes ces recettes, soignez la dernière phrase du prompt : « réponds uniquement par… », « sois concis », « groupe par catégorie ». En automatisation, la précision de la consigne remplace la conversation. Un prompt flou en interactif se rattrape ; en headless, il se paie à chaque exécution.
GitHub Actions sans effort : la mention @claude
Tout ce que nous venons de scripter à la main, GitHub peut le déclencher pour vous. GitHub Actions, c’est le service d’automatisation intégré à GitHub : à chaque événement du dépôt (une issue ouverte, une PR créée, un commentaire posté), il exécute des tâches sur des machines louées à la minute, les runners.
La mise en place tient en une commande, lancée depuis Claude Code dans votre dépôt.
# Dans une session Claude Code, a la racine du depot
/install-github-app
Cette commande installe l’application GitHub de Claude sur votre dépôt, ajoute le workflow, et vous guide pour enregistrer la clé API dans le secret ANTHROPIC_API_KEY. Il faut être administrateur du dépôt pour la dérouler.
Ensuite, la magie opère : mentionnez @claude dans une issue ou un commentaire de pull request, et l’action anthropics/claude-code-action se réveille. Écrivez « @claude corrige cette erreur de typage » dans une issue, et Claude analyse le contexte, code la correction et ouvre une pull request. Le workflow minimal ressemble à ceci :
name: Claude Code
on:
issue_comment:
types: [created]
jobs:
claude:
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
# La cle vit dans les secrets du depot, jamais en clair
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
Côté bonnes pratiques : la clé API reste dans les secrets GitHub, les permissions de l’application se limitent au strict nécessaire (contenus, issues, pull requests), et chaque proposition de Claude passe par une revue humaine avant fusion. Le paramètre claude_args du workflow accepte les mêmes options que la ligne de commande, --max-turns en tête.
💡 Astuce — Votre fichier CLAUDE.md versionné, vu au module sur la mémoire, s’applique aussi en CI. Les conventions de code, les règles de revue, les interdits du projet : Claude les respecte sur le runner GitHub exactement comme sur votre machine. Un CLAUDE.md soigné, c’est une CI disciplinée.
Une CI sûre et économe
Automatiser, c’est déléguer. Et on ne délègue sereinement qu’avec des garde-fous. Quatre principes structurent une CI saine avec Claude Code.
Premier principe : l’isolation. Un job de CI tourne dans un conteneur, une machine virtuelle jetable, détruite après usage. C’est là, et seulement là, que l’option --dangerously-skip-permissions devient légitime : elle désactive toutes les demandes de permission, ce qui est dangereux sur votre poste, mais acceptable dans un environnement sans secrets superflus, sans accès à vos données, et qui disparaît à la fin du job.
Deuxième principe : les permissions minimales. Même isolé, un job n’a pas besoin de tout. Une revue de diff se contente de --permission-mode plan ou d’une liste --allowedTools réduite à la lecture. Réservez les droits d’écriture aux jobs qui en ont réellement besoin.
Troisième principe : le budget. En CI, chaque exécution consomme des jetons d’API, et une tâche mal bornée peut coûter cher. Vos leviers : --max-turns pour plafonner chaque appel, des timeouts au niveau du workflow, et le choix du modèle. Un triage d’issue n’a pas besoin du modèle le plus puissant : la gamme Haiku, rapide et économe, suffit largement, quand une revue de sécurité mérite la gamme Opus. L’option --model fait la bascule, et notre comparatif des IA vous aide à situer chaque gamme.
Quatrième principe : les journaux. Conservez les sorties JSON de chaque exécution comme artefacts de CI. Le champ total_cost_usd alimente votre suivi de coûts, le champ session_id permet de retracer une exécution douteuse, et l’historique complet vous protège le jour où il faut comprendre ce que l’automate a réellement fait. Pour approfondir la place de ces agents dans votre boîte à outils, notre dossier IA complète ce module.
⚠️ Piège courant — Ne recopiez jamais
--dangerously-skip-permissionsd’un exemple de CI vers votre terminal personnel. Sur votre machine, cette option donne à Claude le droit d’exécuter n’importe quelle commande sans validation. La règle tient en une phrase : conteneur jetable, oui ; poste de travail, jamais.
À vous de jouer
Écrivons ensemble le script de l’exercice : une relecture automatique du diff de votre branche courante, qui échoue si Claude signale un problème bloquant. Exactement ce qu’on branche ensuite dans une CI.
- À la racine d’un projet git, créez un fichier
relecture.shet rendez-le exécutable avecchmod +x relecture.sh. - Récupérez le diff de la branche courante par rapport à
main: c’est la commandegit diff main...HEAD. Si le diff est vide, le script se termine tout de suite avec succès. - Envoyez ce diff à Claude par un pipe, avec
--output-format jsonet une consigne de verdict stricte : la réponse doit commencer parVERDICT: BLOQUANTouVERDICT: OK. - Extrayez le texte avec
jq -r '.result', affichez-le, puis cherchez le verdict avecgrep. S’il est bloquant, quittez avec le code d’erreur 1 : c’est ce code qui fait échouer une CI.
#!/bin/bash
# relecture.sh — echoue si Claude signale un probleme bloquant
diff=$(git diff main...HEAD)
if [ -z "$diff" ]; then
echo "Aucun changement a relire."
exit 0
fi
reponse=$(echo "$diff" | claude -p "Relis ce diff. Commence ta reponse par VERDICT: BLOQUANT si un probleme critique (bug, faille, donnee sensible) doit empecher la fusion, sinon par VERDICT: OK. Justifie en trois points maximum." --output-format json)
texte=$(echo "$reponse" | jq -r '.result')
echo "$texte"
if echo "$texte" | grep -q "VERDICT: BLOQUANT"; then
echo "Relecture bloquante : fusion refusee."
exit 1
fi
echo "Relecture validee."
- Testez : créez une branche, introduisez volontairement un bug évident (une clé d’API en dur dans le code, par exemple), puis lancez
./relecture.sh. Le script doit afficher le verdict bloquant et sortir en erreur. - Pour aller plus loin, appelez ce script depuis votre CI ou un hook git : vous venez de construire votre premier garde-barrière IA.
En résumé
claude -p "prompt"transforme Claude Code en commande Unix : lisible par pipe, redirigeable, scriptable partout.--output-format jsonstructure la réponse : le texte dansresult, le coût danstotal_cost_usd, la session danssession_id;stream-jsonémet le détail ligne par ligne.- En non-interactif, tout se déclare à l’avance :
--allowedToolsen liste blanche,--permission-modepour le cadre,--max-turnsen coupe-circuit, et la clé API dans la variable d’environnementANTHROPIC_API_KEY. - Les trois recettes à retenir : boucle de migration fichier par fichier, triage d’issues avec réponse courte réinjectable, revue de PR et changelog générés depuis le diff et les commits.
/install-github-appcâble votre dépôt en quelques minutes ; ensuite, mentionner@claudedans une issue ou une PR déclenche l’actionanthropics/claude-code-action.--dangerously-skip-permissionsn’a sa place que dans un conteneur isolé et jetable ; sur votre machine, jamais.
Vous savez maintenant piloter Claude Code depuis n’importe quel script. Mais la ligne de commande a ses limites : au prochain et dernier module, vous passerez de l’autre côté du miroir avec l’Agent SDK, pour intégrer le moteur de Claude Code directement dans vos propres applications TypeScript ou Python.
Testez vos connaissances
Dans un script, vous lancez : cat erreurs.log | claude -p 'explique la cause racine'. Que se passe-t-il ?
Avec -p, Claude Code se comporte comme une commande Unix classique : il lit ce qui arrive par le pipe (l'entrée standard), traite le prompt, imprime le résultat sur la sortie standard, puis se termine. On peut donc l'enchaîner avec d'autres commandes.
Votre script doit récupérer uniquement le texte de la réponse de Claude pour le traiter automatiquement. Quelle est la bonne approche ?
L'option --output-format json enveloppe la réponse dans un objet JSON structuré : le texte se trouve dans le champ result, accompagné de métadonnées comme session_id et total_cost_usd. Un outil comme jq permet ensuite d'extraire précisément le champ voulu.
En CI, vous voulez que Claude puisse lire les fichiers et lancer uniquement des commandes git diff, rien d'autre. Que passez-vous à claude -p ?
L'option --allowedTools définit une liste blanche stricte : ici l'outil de lecture de fichiers et les seules commandes shell commençant par git diff. Tout le reste est refusé. C'est le réglage le plus précis pour une exécution non interactive.
Votre dépôt a été configuré avec /install-github-app. Un collègue écrit « @claude corrige ce bug » dans un commentaire d'issue. Que se passe-t-il ?
Une fois le dépôt câblé, la mention @claude dans une issue ou une pull request déclenche l'action anthropics/claude-code-action. Elle s'exécute sur un runner GitHub avec la clé stockée dans le secret ANTHROPIC_API_KEY, analyse le contexte et agit.
Dans quel cas l'option --dangerously-skip-permissions est-elle un choix raisonnable ?
Cette option désactive toutes les demandes de permission. Sur votre machine, c'est risqué : Claude pourrait exécuter n'importe quelle commande. Dans un conteneur jetable et isolé, sans secrets superflus, le pire scénario reste confiné : c'est là que l'option devient légitime.