cool-solution — dev.blog
Tecnologie

Claude Code in GitHub Actions: review automatica delle PR con claude-code-action

Quando lavoro con Claude Code in GitHub Actions il momento che preferisco è aprire una pull request e trovare, poco dopo, una review già scritta: rischi individuati, righe puntate, severità assegnata. Il runner installa Claude Code e usa lo stesso agent loop disponibile in locale, attraverso l'Agent SDK della action. In questo tutorial monto il flusso da zero: parto da `claude -p` nel terminale, installo la GitHub App con `/install-github-app`, scrivo due workflow separati (review automatica e `@claude` interattivo) e chiudo con guardrail su tool, turni, budget e permessi.

Illustrazione: una pull request GitHub attraversa un workflow Actions dove Claude Code esegue una review non interattiva e pubblica un commento con i rilievi.

🔍 Modalità non interattiva: claude -p

Tutto quello che Claude Code fa in una sessione interattiva — leggere file, lanciare comandi, ragionare sul codice — lo sa fare anche senza interfaccia. Il flag `-p` (o `--print`) esegue un prompt, stampa il risultato ed esce: è la modalità non interattiva, in passato chiamata headless, su cui si costruiscono script e automazioni. Il contesto arriva dalla directory di lavoro e l'output va su stdout come per qualsiasi tool Unix.

Per la CI servono guardrail distinti. `--output-format` sceglie tra `text`, `json` e `stream-json`; il JSON include risultato, session ID e una stima del costo. `--allowedTools` pre-approva i tool, ma non è da solo una deny-list: per un run ristretto aggiungo `--permission-mode dontAsk` e nego esplicitamente i tool di scrittura. `--max-turns` limita i round trip dell'agente, mentre `--max-budget-usd` imposta il vero tetto economico basato sulla stima client-side.

Terminale · modalità non interattiva in tre varianti
# 1. Base: prompt, risultato su stdout, exit
claude -p "Elenca i file toccati piu' spesso negli ultimi 20 commit"

# 2. Output JSON: risultato + costo + session id
claude -p "Riassumi l'architettura del progetto" \
  --output-format json

# 3. Con guardrail: permessi, turni e budget
claude -p "Cerca TODO e FIXME e raggruppali per area" \
  --allowedTools "Read,Grep,Glob" \
  --disallowedTools "Edit,Write" \
  --permission-mode dontAsk \
  --max-turns 5 \
  --max-budget-usd 0.50

Headless mode · docs ufficiali

Il flag -p trasforma Claude Code in un tool Unix componibile: prompt in ingresso, testo o JSON in uscita, exit code a fine corsa.

🧪 La review in locale, prima della CI

Prima di portare un prompt di review in un workflow, lo collaudo nel terminale. Il ciclo è corto: lancio lo script, leggo la review, correggo il prompt e rilancio. Il JSON mostra anche la stima `total_cost_usd`, utile per scegliere `--max-turns` e `--max-budget-usd`; per la spesa ufficiale controllo comunque la Usage page della Claude Console.

Lo script recupera il branch base, calcola il diff in modo deterministico e lo passa su stdin. Claude non ha quindi bisogno di eseguire `git diff`. `dontAsk` nega i tool non pre-approvati e `--disallowedTools` blocca la scrittura: la review produce solo il report. In CI applico lo stesso principio separando il workflow che commenta da quello che può modificare il codice.

scripts/review-local.sh · review non interattiva del diff
#!/usr/bin/env bash
# Review headless del diff rispetto al branch di base.
set -euo pipefail

base="${1:-main}"

git fetch origin "${base}"
git diff "origin/${base}...HEAD" | claude --bare -p "Fai la code review
del diff ricevuto su stdin.
Per ogni rilievo indica: file, riga, severita' (alta/media/bassa),
problema e correzione proposta. Concentrati su bug, sicurezza,
performance. Ignora lo stile." \
  --allowedTools "Read,Grep,Glob" \
  --disallowedTools "Edit,Write" \
  --permission-mode dontAsk \
  --max-turns 10 \
  --max-budget-usd 1.00 \
  --output-format json
Diff su stdin, scrittura negata, massimo 10 turni e budget stimato di 1 USD. --bare richiede ANTHROPIC_API_KEY e rende il run riproducibile.

⚙️ Setup: /install-github-app e i secret

Il ponte tra il repository e l'API di Anthropic si configura con un comando: apro `claude` nel repository e lancio /install-github-app. Il wizard guida l'installazione della GitHub App ufficiale, la registrazione del secret `ANTHROPIC_API_KEY` e la creazione del workflow; occorre essere amministratori del repository. In alternativa installo l'app da `github.com/apps/claude`, aggiungo il segreto nelle impostazioni Actions e creo il YAML a mano.

L'unico punto in cui vale la pena rallentare sono i permessi del workflow. Il template di review nasce prudente, con `pull-requests: read`: Claude legge la PR ma non può pubblicare il commento con la review. Per avere i rilievi direttamente sulla pull request serve alzare a write — ed è una scelta che faccio consapevolmente workflow per workflow, non un default globale del repo.

Il flusso completo: dalla PR aperta al commento di review
Diagramma del flusso: la pull request aperta avvia il workflow GitHub Actions, il runner esegue Claude Code in modalità non interattiva con claude_args e la review torna sulla PR come commento.

Il runner esegue lo stesso agent loop che uso in locale: checkout del repository, guardrail espliciti e commento finale via GitHub API.

🤖 Il workflow di review automatica

Il primo workflow parte automaticamente quando una pull request non draft viene aperta, aggiornata, riaperta o resa pronta per la review. Con un `prompt` esplicito, anthropics/claude-code-action@v1 entra in automation mode; `track_progress: true` garantisce un commento di avanzamento che conterrà anche il riepilogo finale, mentre i tool `gh pr` e l'MCP per i commenti inline permettono di pubblicare i rilievi sulla PR.

`contents: read` limita il codice a sola lettura, `pull-requests: write` consente i commenti e `id-token: write` serve alla GitHub App ufficiale. Un checkout shallow è sufficiente perché la action riceve il contesto della PR e usa `gh pr diff`; non serve scaricare tutta la cronologia. Il job esclude intenzionalmente i fork: i workflow `pull_request` avviati da fork non ricevono `ANTHROPIC_API_KEY` e il loro `GITHUB_TOKEN` resta in sola lettura.

.github/workflows/claude-pr-review.yml · review automatica
name: Claude PR Review

on:
  pull_request:
    types: [opened, synchronize, reopened, ready_for_review]

concurrency:
  group: claude-pr-review-${{ github.event.pull_request.number }}
  cancel-in-progress: true

jobs:
  review:
    if: >
      github.event.pull_request.draft == false &&
      github.event.pull_request.head.repo.full_name == github.repository
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
      id-token: write
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 1

      - uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          track_progress: true
          prompt: |
            REPO: ${{ github.repository }}
            PR NUMBER: ${{ github.event.pull_request.number }}

            Fai la code review di questa pull request.
            Per ogni rilievo: file, riga, severita' (alta/media/bassa),
            problema, correzione proposta. Concentrati su bug,
            sicurezza e performance. Ignora lo stile.
            Usa gh pr comment per il riepilogo e il tool MCP
            per i commenti inline, con confirmed: true.
          claude_args: |
            --max-turns 15
            --max-budget-usd 1.00
            --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)"
            --disallowedTools "Edit,Write"

anthropics/claude-code-action · GitHub

Automation mode con tracking, tool di commento espliciti, codice in sola lettura e cancellazione dei run superati.

💬 @claude nei commenti: la modalità interattiva

Il secondo workflow richiede una menzione esplicita. Scrivo `@claude come gestiresti la paginazione qui?` in un commento e l'agente risponde; con `@claude sistema la validazione di questo endpoint` può implementare la modifica e creare un commit sul branch della PR. Senza `prompt`, la action entra in modalità interattiva e applica il proprio controllo di accesso: per impostazione predefinita può attivarla solo un utente con permessi di scrittura sul repository.

Qui `contents: write` è necessario perché l'agente può creare commit. Tengo quindi i workflow separati: la review automatica ha codice in sola lettura, mentre quello interattivo ha poteri maggiori ma parte solo da una menzione autorizzata. Aggiungo anche `pull_request_review` per coprire le menzioni nel testo di una review, oltre ai commenti generali e inline.

.github/workflows/claude.yml · risponde alle menzioni
name: Claude

on:
  issue_comment:
    types: [created]
  pull_request_review_comment:
    types: [created]
  pull_request_review:
    types: [submitted]

jobs:
  claude:
    if: |
      (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude'))
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
      issues: write
      id-token: write
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 1

      - uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          claude_args: --max-turns 25 --max-budget-usd 2.00
Nessun prompt → modalità interattiva: la action legge la menzione, risponde nel thread o committa sul branch della PR.

🎛️ claude_args: modello, permessi e turni

`claude_args` passa i flag della CLI alla action. Uso `--model` per scegliere il modello, `--max-turns` per limitare i round trip e `--max-budget-usd` per fermare il run quando la stima client-side raggiunge la soglia. `--allowedTools` pre-approva capacità specifiche; il confine effettivo nasce dalla combinazione con `--disallowedTools`, dalla modalità dei permessi e dal blocco `permissions` di GitHub.

Nella sintassi dei permessi, `Bash(git diff:)` autorizza i comandi che iniziano con `git diff`; la forma equivalente moderna è `Bash(git diff *)`. Senza il confine prima dell'asterisco, `Bash(git diff)` includerebbe anche `git diff-index`. Mantengo quindi pattern specifici e aggiungo soltanto i tool necessari al risultato atteso.

Il risultato sulla PR: rilievi ordinati per severità
Mock di una pull request GitHub con il commento di review generato da Claude: tre rilievi ordinati per severità con file, riga e correzione proposta.

Ogni run produce un commento strutturato: severità, file:riga, problema e proposta. Il formato lo decide il prompt, non la action.

⚠️ Errori da evitare

Il primo errore è saltare il collaudo locale. Un prompt di review scritto direttamente nel YAML si itera a colpi di push e attese del runner; lo stesso prompt maturato con `claude -p` arriva in CI già stabile. Il secondo è trattare la review dell'agente come un verdetto: è un primo passaggio che pulisce i problemi meccanici — null check mancanti, edge case, query sospette — e libera la review umana per architettura e scelte di design. Chi approva la PR resta una persona.

Il terzo errore è economico: `--max-turns` limita la durata agentica, mentre `--max-budget-usd` imposta la soglia di costo stimata. Parto basso, osservo `total_cost_usd` e verifico la spesa ufficiale nella Claude Console. L'ultimo errore è di sicurezza: i secret non vengono passati ai workflow `pull_request` dei fork; non sostituisco questo limite con `pull_request_target` facendo checkout del codice non attendibile nel workspace principale.

  • Collauda il prompt in locale con claude -p: iterare in CI costa minuti a tentativo, sul terminale secondi.
  • Due workflow, due perimetri: review automatica in sola lettura; scrittura solo nel workflow @claude chiamato da un umano.
  • Limita turni e budget: --max-turns controlla i round trip, --max-budget-usd la spesa stimata per run.
  • Permessi minimi che funzionano: pull-requests: write serve per il commento; contents: write solo dove l'agente committa davvero.
  • La review AI non approva: pulisce i problemi meccanici, ma il merge resta una decisione umana.

🧪 Verifica su una PR di prova

Prima di attivare il workflow su tutto il repository, apro una PR di prova nello stesso repository con un difetto deliberato e innocuo. Verifico che il run parta, che il commento venga pubblicato, che i rilievi inline abbiano file e riga corretti e che il job precedente venga cancellato dopo un nuovo push. Solo allora porto il workflow su `main`.

Crea una PR di prova nello stesso repository
git switch -c test/claude-pr-review
# Introduci un difetto innocuo e committalo
git add .
git commit -m "test: exercise Claude PR review"
git push -u origin test/claude-pr-review
gh pr create --fill --base main
gh pr checks --watch
La policy del workflow limita questa prova alle PR same-repository; per i fork serve un disegno di autenticazione separato.

✅ Checklist finale

Prima di dichiarare "attiva" la review automatica su un repo, ripasso sempre gli stessi sette punti. Sono la differenza tra un workflow che produce review utili e uno che genera rumore costoso — e ognuno di questi punti l'ho imparato sbagliandolo almeno una volta.

Se una voce non è verde, il workflow non va su `main`. Vale per i side project e vale doppio per i repo di lavoro, dove un run fuori controllo su venti PR aperte è una bolletta, non un esperimento.

Checklist pre-attivazione
[ ] Prompt di review collaudato in locale con claude -p
[ ] ANTHROPIC_API_KEY registrata come secret del repo
[ ] Workflow review: pull-requests: write, contents: read
[ ] Workflow @claude separato per le modifiche al codice
[ ] Tool gh/MCP necessari per pubblicare i commenti
[ ] --max-turns e --max-budget-usd calibrati
[ ] Policy same-repo/fork definita esplicitamente
Sette check, due minuti. È il minimo prima di accendere la review automatica su un repository condiviso.

Domande frequenti su Claude Code GitHub Actions

Cos'è la modalità headless di Claude Code?

È l'esecuzione non interattiva della CLI: con il flag -p (--print) Claude Code esegue lo stesso agent loop della sessione normale — legge file, lancia comandi, ragiona sul codice — ma stampa il risultato e esce, senza UI. È la base di ogni automazione: script locali, cron, CI/CD. L'output può essere testo semplice, JSON strutturato o stream di eventi JSON riga per riga.

Cosa fa claude-code-action rispetto a un normale bot di review?

La action ufficiale di Anthropic esegue nel runner GitHub Actions un agente basato sul Claude Agent SDK: può usare una shell vera, leggere i file del checkout e — nei workflow dove lo permetti — modificare codice e committare. Supporta gli argomenti della CLI di Claude Code e aggiunge contesto dell'evento GitHub, autenticazione e tool per i commenti.

Quanto costa la review automatica di una PR?

Dipende da modello, dimensione del diff e turni consumati. --max-turns limita i round trip, --max-budget-usd ferma il run quando raggiunge la soglia stimata e --model permette di scegliere un modello più economico. Il campo total_cost_usd del JSON è una stima client-side; per il dato ufficiale controllo la Usage page della Claude Console.

Perché separare il workflow di review da quello @claude?

Per il principio dei permessi minimi. La review automatica gira senza intervento umano sulle PR same-repository ammesse: le do contents: read e soltanto i tool necessari per ispezionare e commentare, così non può modificare il codice del repository. Il workflow @claude può scrivere codice e committare (contents: write), ma parte solo quando una persona autorizzata lo menziona esplicitamente. Automazione ampia con poteri piccoli, poteri grandi solo su richiesta umana.

La review di Claude sostituisce quella umana?

No, la anticipa. L'agente è bravo sui problemi meccanici — null check mancanti, edge case non gestiti, query N+1 e incoerenze col resto del codebase — e li segnala prima che un collega inizi la review manuale. La review umana resta necessaria per architettura, scelte di prodotto e contesto che non sta nel repo. Il merge lo approva sempre una persona.

Posso usare la action senza API key Anthropic diretta?

Sì. Con la Workload Identity Federation di Anthropic sostituisci la chiave statica con gli identificatori di federazione e id-token: write. Bedrock, Vertex AI e Microsoft Foundry richiedono invece input use_bedrock/use_vertex/use_foundry, autenticazione OIDC del provider, variabili e identificatori di modello specifici. Prompt e logica di review restano riutilizzabili, ma il workflow di autenticazione cambia.

Parliamone

Se questo tema ti riguarda, scrivimi: confrontarsi su codice e AI è sempre tempo speso bene.

Altri articoli del blog

semantic-caching-llm-dotnet-redis.md27 luglio 2026tips-claude-claude-md-luglio-2026.md24 luglio 2026claude-code-pretooluse-hook-proteggere-env-segreti-2026.md24 luglio 2026