Automatiser l’API GitHub avec Claude Code sans exposer vos tokens
Automatiser l’API GitHub avec Claude Code: permissions, pagination, limites, webhooks et exemples Node.
L’API GitHub permet de lire et de mettre à jour des issues, pull requests, releases, workflows, métadonnées de dépôts et webhooks depuis du code. Avec Claude Code, elle devient un bon levier pour transformer des tâches répétitives en automatisations vérifiables: trier les nouvelles issues, repérer les PR abandonnées, préparer des notes de release et générer un rapport quotidien de santé du dépôt.
Le même outil peut aussi créer très vite une automatisation dangereuse. Les pièges classiques sont connus: token affiché dans les logs, classic token avec un scope trop large, première page lue comme si elle contenait tous les résultats, boucle de retry après rate limit, webhook accepté sans signature, modifications massives sans dry-run. Il faut donc demander à Claude Code un flux sûr, pas seulement un script qui marche une fois.
Gardez les références officielles ouvertes: GitHub REST API docs, REST API rate limits, validation des webhooks et GitHub GraphQL API. Pour relier cette automatisation au quotidien de l’équipe, lisez aussi le guide Git workflow et le guide avancé GitHub Actions.
Le modèle sûr
Commencez par une version en lecture seule. Elle liste les issues, les PR ou les releases concernées et permet de vérifier les permissions, la pagination et le format du rapport. Ensuite seulement, ajoutez un dry-run qui affiche les labels, commentaires ou changements proposés sans les appliquer. L’écriture réelle doit être derrière un flag explicite, par exemple APPLY=true, et un nombre maximal d’éléments.
flowchart LR
A["Donner à Claude Code objectif et garde-fous"] --> B["Lire avec permissions minimales"]
B --> C["Gérer pagination et rate limits"]
C --> D["Afficher le dry-run"]
D --> E["Appliquer avec accord explicite"]
E --> F["Planifier ou recevoir un Webhook"]
REST et GraphQL ne servent pas au même niveau. REST est idéal pour démarrer: lister les PR, ajouter un label, créer une release, lire un workflow run. Les endpoints sont visibles, faciles à tester et Claude Code peut les implémenter par petits morceaux. GraphQL est utile quand un rapport doit regrouper dépôt, PR, auteur, reviews, labels et milestones dans une seule requête.
| Critère | REST API | GraphQL API |
|---|---|---|
| Usage naturel | Action sur une ressource précise | Rapport multi-ressources |
| Difficulté | Plus basse, URL et méthode HTTP | Plus haute, requête et schéma |
| Demande à Claude Code | Un endpoint à la fois | Définir les champs nécessaires |
| Risque principal | Oublier pagination ou permission | Requête trop large ou coûteuse |
Tokens et permissions
Un token GitHub doit être traité comme un mot de passe. Ne le mettez pas dans le code, les exemples, les captures, les snapshots de test ou les logs. Les exemples ci-dessous lisent GITHUB_TOKEN depuis l’environnement et n’impriment jamais sa valeur.
Préférez les fine-grained personal access tokens. Limitez le token au dépôt et aux permissions nécessaires. Un bot de triage d’issues peut avoir besoin de Issues: Read and write; un rapport de PR obsolètes peut se contenter de Pull requests: Read-only; un générateur de notes de release peut commencer avec Contents: Read et Metadata: Read. Le scope repo d’un classic token est pratique, mais trop large pour une automatisation durable.
Dans GitHub Actions, déclarez aussi permissions. Un rapport quotidien ne doit pas avoir de droit d’écriture. Un job qui ajoute des labels doit avoir uniquement la permission d’écriture correspondante. Avant de laisser Claude Code écrire le YAML, demandez-lui une table fonction, endpoint et permission requise.
Crée un script Node.js qui utilise GitHub REST API.
Contraintes:
- Lire le token depuis process.env.GITHUB_TOKEN.
- Ne jamais afficher le token ni les headers Authorization complets.
- Lire owner/repo depuis des variables d’environnement.
- Commencer en lecture seule.
- Utiliser fetch et gérer status code, pagination et headers de rate limit.
- Ajouter une section README avec les permissions GitHub nécessaires.
Script de lecture exécutable
Ce script fonctionne avec Node.js 18 ou plus récent. Il liste les issues ouvertes sans modifier le dépôt.
export GITHUB_TOKEN="github_pat_xxx"
export GITHUB_OWNER="octocat"
export GITHUB_REPO="Hello-World"
node scripts/list-open-issues.mjs
// scripts/list-open-issues.mjs
const { GITHUB_TOKEN, GITHUB_OWNER, GITHUB_REPO } = process.env;
if (!GITHUB_TOKEN || !GITHUB_OWNER || !GITHUB_REPO) {
throw new Error("Set GITHUB_TOKEN, GITHUB_OWNER, and GITHUB_REPO.");
}
const apiVersion = "2026-03-10";
async function github(path, options = {}) {
const response = await fetch(`https://api.github.com${path}`, {
...options,
headers: {
Accept: "application/vnd.github+json",
Authorization: `Bearer ${GITHUB_TOKEN}`,
"X-GitHub-Api-Version": apiVersion,
"User-Agent": "claudecodelab-safe-github-api-example",
...(options.headers ?? {}),
},
});
if (!response.ok) {
const body = await response.text();
throw new Error(`GitHub API ${response.status}: ${body.slice(0, 500)}`);
}
return response.json();
}
const issues = await github(
`/repos/${encodeURIComponent(GITHUB_OWNER)}/${encodeURIComponent(GITHUB_REPO)}/issues?state=open&per_page=10`,
);
const rows = issues
.filter((issue) => !issue.pull_request)
.map((issue) => ({
number: issue.number,
title: issue.title,
labels: issue.labels.map((label) => label.name).join(", "),
updated: issue.updated_at,
}));
console.table(rows);
La prochaine étape sûre est un rapport de propositions: labels candidats, commentaire proposé, personne à assigner. Ne fermez pas d’issues automatiquement. Exigez un flag explicite et un plafond de modifications.
Pagination et rate limits
Les endpoints de liste sont souvent paginés. per_page=100 ne veut pas dire “tous les éléments”, mais seulement une page. Un générateur de release notes qui lit une seule page peut oublier des PR mergeées. Un rapport de PR obsolètes peut sembler propre alors qu’il ignore la majorité des données.
Les rate limits doivent aussi être gérés. Réessayer immédiatement après chaque 403 ou 429 consomme du temps CI et aggrave le problème. Lisez retry-after et x-ratelimit-reset, attendez avec une limite raisonnable et échouez après un nombre fixe de tentatives.
// scripts/github-pages.mjs
const token = process.env.GITHUB_TOKEN;
if (!token) throw new Error("Set GITHUB_TOKEN.");
const apiBase = "https://api.github.com";
const apiVersion = "2026-03-10";
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function defaultHeaders() {
return {
Accept: "application/vnd.github+json",
Authorization: `Bearer ${token}`,
"X-GitHub-Api-Version": apiVersion,
"User-Agent": "claudecodelab-pagination-example",
};
}
function parseNextLink(linkHeader) {
if (!linkHeader) return null;
for (const part of linkHeader.split(",")) {
const match = part.match(/<([^>]+)>;\s*rel="([^"]+)"/);
if (match && match[2] === "next") return match[1];
}
return null;
}
async function githubRequest(url, options = {}, attempt = 0) {
const response = await fetch(url, {
...options,
headers: {
...defaultHeaders(),
...(options.headers ?? {}),
},
});
if ((response.status === 403 || response.status === 429) && attempt < 2) {
const retryAfterSeconds = Number(response.headers.get("retry-after") ?? "0");
const resetSeconds = Number(response.headers.get("x-ratelimit-reset") ?? "0");
const resetDelayMs = resetSeconds > 0 ? resetSeconds * 1000 - Date.now() : 0;
const waitMs = Math.max(retryAfterSeconds * 1000, resetDelayMs, 0);
if (waitMs > 0 && waitMs <= 10 * 60 * 1000) {
await sleep(waitMs + 1000);
return githubRequest(url, options, attempt + 1);
}
}
if (!response.ok) {
const body = await response.text();
throw new Error(`GitHub API ${response.status}: ${body.slice(0, 500)}`);
}
return {
data: await response.json(),
nextUrl: parseNextLink(response.headers.get("link")),
};
}
export async function paginate(path) {
const items = [];
let url = path.startsWith("http") ? path : `${apiBase}${path}`;
while (url) {
const page = await githubRequest(url);
if (!Array.isArray(page.data)) {
throw new Error("paginate() expected an array response.");
}
items.push(...page.data);
url = page.nextUrl;
}
return items;
}
if (import.meta.url === `file://${process.argv[1]}`) {
const owner = process.env.GITHUB_OWNER;
const repo = process.env.GITHUB_REPO;
if (!owner || !repo) throw new Error("Set GITHUB_OWNER and GITHUB_REPO.");
const pulls = await paginate(
`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls?state=open&per_page=100`,
);
console.table(pulls.map((pr) => ({ number: pr.number, title: pr.title, updated: pr.updated_at })));
}
Après avoir créé ce helper, demandez à Claude Code de chercher api.github.com dans le dépôt et de supprimer les appels directs. Vous évitez ainsi les scripts qui reviennent à une lecture sur une seule page.
Webhooks, signature et idempotence
Un webhook permet de réagir à un événement au lieu de sonder l’API. Une PR ouverte peut déclencher une suggestion de label, une issue créée peut entrer dans une file de triage, une release publiée peut notifier un service. Comme l’endpoint est exposé, il faut vérifier x-hub-signature-256 avant de faire confiance au payload.
Il faut aussi rendre le traitement idempotent: si GitHub renvoie la même livraison, le système ne doit pas ajouter deux commentaires ou deux labels. Enregistrez x-github-delivery avant les effets secondaires. Le Set ci-dessous illustre la forme; en production, utilisez Redis ou une base de données.
npm install express
export GITHUB_WEBHOOK_SECRET="your-webhook-secret"
node webhook-server.mjs
// webhook-server.mjs
import crypto from "node:crypto";
import express from "express";
const secret = process.env.GITHUB_WEBHOOK_SECRET;
if (!secret) throw new Error("Set GITHUB_WEBHOOK_SECRET.");
const app = express();
const seenDeliveries = new Set();
function verifySignature(payloadBuffer, signatureHeader) {
if (!signatureHeader) return false;
const expected = `sha256=${crypto
.createHmac("sha256", secret)
.update(payloadBuffer)
.digest("hex")}`;
const actual = Buffer.from(signatureHeader, "utf8");
const expectedBuffer = Buffer.from(expected, "utf8");
return actual.length === expectedBuffer.length && crypto.timingSafeEqual(actual, expectedBuffer);
}
app.post("/github/webhook", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.get("x-hub-signature-256");
if (!verifySignature(req.body, signature)) {
return res.status(401).send("invalid signature");
}
const deliveryId = req.get("x-github-delivery");
if (!deliveryId) return res.status(400).send("missing delivery id");
if (seenDeliveries.has(deliveryId)) {
return res.status(202).send("duplicate ignored");
}
seenDeliveries.add(deliveryId);
const event = req.get("x-github-event");
const payload = JSON.parse(req.body.toString("utf8"));
console.log(
JSON.stringify({
event,
deliveryId,
repository: payload.repository?.full_name,
action: payload.action,
}),
);
return res.status(202).send("accepted");
});
app.listen(process.env.PORT ?? 3000, () => {
console.log("Listening for GitHub webhooks.");
});
Lorsque Claude Code étend ce serveur, dites-lui de ne pas parser le JSON avant la validation, de ne pas faire de modification destructive dans la requête HTTP et d’ignorer les livraisons déjà vues.
Cas d’usage concrets
Un bot de triage d’issues détecte les étapes de reproduction manquantes, propose des labels comme bug, question ou needs-repro, puis produit un rapport. La première version ne doit pas commenter automatiquement.
Un stale PR reporter liste les PR sans activité depuis 30 jours, avec review demandée depuis trop longtemps ou CI cassée. La version sûre informe seulement; fermer une PR doit passer par une commande séparée avec dry-run.
Un générateur de release notes regroupe les PR mergeées entre deux tags et les classe par label. REST suffit pour démarrer; GraphQL devient utile quand on ajoute auteurs, reviewers et milestones.
Un daily repository health report combine issues ouvertes, PR anciennes, workflows en échec, alertes Dependabot, releases récentes et backlog de review. Il doit mettre en avant les trois actions du jour. Ce flux complète bien Claude Code workflow automation et la review workflow checklist.
Échecs fréquents
Le premier échec est le token dans les logs. Évitez console.log(process.env), les dumps de requête et les logs CI qui affichent les headers. Vérifiez avec rg "GITHUB_TOKEN|Authorization|process.env".
Le deuxième est le scope trop large. Un rapport en lecture seule ne doit pas pouvoir écrire. Le troisième est l’absence de pagination. Le quatrième est la boucle de rate limit. Le cinquième est le webhook non signé. Le sixième est la modification massive: fermer beaucoup d’issues, relabeliser toutes les PR ou réécrire des releases sans dry-run, limite, audit log et stratégie de retour arrière.
Place de Claude Code
Claude Code doit générer le client API, les helpers, les tests, le README, le workflow GitHub Actions et la checklist de review. Les décisions sur les tokens de production, les permissions et les changements destructifs restent humaines. Notez ces règles dans CLAUDE.md.
ClaudeCodeLab aide les équipes à définir CLAUDE.md, permissions, review gates, Actions et Webhooks. Pour un déploiement d’équipe, commencez par formation et conseil Claude Code. Pour pratiquer seul, utilisez la cheatsheet gratuite et les templates.
Résultat
GitHub API et Claude Code peuvent réduire fortement la maintenance répétitive si vous respectez les limites: REST pour les actions directes, GraphQL pour les rapports, permissions minimales, pagination réelle, rate limits contrôlés, webhooks vérifiés et idempotence.
Résultat de vérification de Masa: les exemples de lecture, pagination et validation de signature ci-dessus sont organisés pour être copiés, lancés et relus facilement. Dans un dépôt réel, commencer par un rapport quotidien avant d’ajouter l’écriture de labels a créé beaucoup plus de confiance qu’un bot qui ferme des issues dès le premier jour.
PDF gratuit: cheatsheet Claude Code
Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.
Nous protégeons vos données et n'envoyons pas de spam.
À propos de l'auteur
Masa
Ingénieur spécialisé dans les workflows pratiques avec Claude Code.
Articles liés
Workflow Obsidian vers CLAUDE.md avec Claude Code
Transformer des notes Obsidian en notes CLAUDE.md concises pour reprendre les sessions sans réexpliquer.
Claude Code Revenue CTA Routing : relier articles, PDF, Gumroad et consultation
Un workflow Claude Code pour orienter les lecteurs vers PDF gratuit, Gumroad ou consultation selon l'intention.
Règles de handoff Claude Code en équipe: preuves, permissions, rollback et revenus
Un format concret pour transmettre un travail Claude Code avec preuves, permissions, rollback, PDF gratuit, Gumroad et consultation.