GitHub Actions è diventato lo standard de-facto per CI/CD nel 2026 — gratuito per repository pubblici, 2.000 minuti/mese gratuiti per quelli privati, e con un ecosistema di migliaia di action pronte all’uso. Per un developer JavaScript che lavora con Next.js, React o Node.js, una pipeline CI/CD ben configurata è la differenza tra deploy ansiosi e rilasci sereni. In questa guida costruiamo una pipeline completa: test automatici, build, deploy preview su Vercel e deploy di produzione — tutto automatizzato.
Struttura di un workflow YAML
Un workflow GitHub Actions è un file YAML nella cartella .github/workflows/. La struttura di base:
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
NODE_VERSION: "20"
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
- run: npm ci
- run: npm run lint
- run: npm run type-check
- run: npm run test
- run: npm run buildOgni run è un comando shell. Se uno fallisce, il workflow si ferma e la PR viene marcata come “failing” — il developer riceve una notifica immediata.
CI completo: lint, type-check, test, build
Un pipeline CI professionale per un progetto Next.js/TypeScript:
# .github/workflows/ci.yml
name: CI
on:
push:
branches: ["**"] # tutti i branch
pull_request:
branches: [main]
jobs:
quality:
name: Quality Checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type check
run: npx tsc --noEmit
- name: Run tests
run: npm run test -- --coverage
env:
CI: true
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
- name: Build
run: npm run build
env:
NEXT_PUBLIC_API_URL: ${{ vars.NEXT_PUBLIC_API_URL }}Cache npm per build veloci
La cache di node_modules riduce il tempo del workflow da 2-3 minuti a 30-45 secondi. L’opzione cache: "npm" nel setup-node action gestisce tutto automaticamente — invalida la cache quando cambia package-lock.json. Per monorepo con workspaces:
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
cache-dependency-path: |
package-lock.json
apps/*/package-lock.json
packages/*/package-lock.jsonDeploy preview su Vercel per ogni PR
Ogni PR ottiene automaticamente un URL di preview univoco — fondamentale per review visive senza toccare la produzione:
# .github/workflows/preview.yml
name: Deploy Preview
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
deploy-preview:
runs-on: ubuntu-latest
environment: preview
steps:
- uses: actions/checkout@v4
- name: Deploy to Vercel (Preview)
uses: amondnet/vercel-action@v25
id: vercel-preview
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
github-token: ${{ secrets.GITHUB_TOKEN }}
scope: ${{ secrets.VERCEL_ORG_ID }}
- name: Comment PR with preview URL
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '🚀 **Preview deploy**: ${{ steps.vercel-preview.outputs.preview-url }}'
})Deploy di produzione su merge in main
# .github/workflows/deploy.yml
name: Deploy Production
on:
push:
branches: [main]
jobs:
# Riesegui CI prima del deploy
test:
uses: ./.github/workflows/ci.yml
deploy-production:
needs: test
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Deploy to Vercel (Production)
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: "--prod"
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Notify on Slack
if: always()
uses: slackapi/slack-github-action@v1
with:
payload: |
{"text": "Deploy ${{ job.status }}: ${{ github.repository }}@${{ github.sha }}"}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}Matrix strategy: test su più versioni Node
Per librerie o package npm che devono supportare più versioni di Node, la matrix strategy esegue i test in parallelo su tutte le versioni:
jobs:
test-matrix:
strategy:
matrix:
node-version: ["18", "20", "22"]
os: [ubuntu-latest, windows-latest]
fail-fast: false # continua gli altri anche se uno fallisce
runs-on: ${{ matrix.os }}
name: Test Node ${{ matrix.node-version }} on ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm testGestione secrets e environment variables
GitHub Actions distingue tra Secrets (valori sensibili, sempre mascherati nei log) e Variables (configurazione non sensibile, visibile nei log). Organizzazione consigliata:
Secrets (Settings → Secrets → Actions): ANTHROPIC_API_KEY, VERCEL_TOKEN, DATABASE_URL, STRIPE_SECRET_KEY, SLACK_WEBHOOK. Variables (Settings → Variables → Actions): NEXT_PUBLIC_API_URL, VERCEL_ORG_ID, VERCEL_PROJECT_ID, NODE_ENV. Per ambienti diversi (staging vs production), usa GitHub Environments — permette di avere secrets diversi per branch diversi e richiede approvazione manuale per i deploy di produzione.
Risorse correlate
Approfondisci sul blog
- Code review automatico con Claude AI
- Automatizzare la gestione email con n8n + Claude
- Drizzle ORM: migrations nel CI/CD
Documentazione e strumenti
- Documentazione ufficiale GitHub Actions
- Deploy su Vercel da GitHub Actions
- vercel-action su GitHub Marketplace
FAQ
Quanti minuti GitHub Actions inclusi nel piano gratuito?
Repository pubblici: minuti illimitati gratuiti. Repository privati: 2.000 minuti/mese su runner Linux (ubuntu-latest), 500 minuti equivalenti su Windows, 50 su macOS. Per progetti small team, 2.000 minuti sono spesso sufficienti. Se li superi, il costo è $0,008/minuto su Linux.
Posso usare GitHub Actions con altri hosting oltre a Vercel?
Sì. Esistono action per AWS (aws-actions/amazon-ecr-login, aws-actions/amazon-ecs-deploy), Netlify, Railway, Fly.io, Cloudflare Pages e praticamente qualsiasi piattaforma di hosting. Il pattern è sempre lo stesso: build nel workflow, poi deploy con l’action specifica della piattaforma.
Come velocizzare una pipeline CI lenta?
In ordine di impatto: (1) cache npm/yarn/pnpm correttamente configurata, (2) parallelizza i job invece di eseguirli in sequenza, (3) usa path filters per non triggerare CI su modifiche a README o doc, (4) separa il check veloce (lint + type-check, ~30s) dal check lento (test completi, ~2-3min) in due job paralleli, (5) usa self-hosted runners per evitare i tempi di startup del runner GitHub.
Come gestisco le migration del database nel CI/CD?
Aggiungi uno step nel workflow di deploy che esegue le migration prima del deploy dell’applicazione. Con Drizzle: npx drizzle-kit migrate. Con Prisma: npx prisma migrate deploy. Configura un timeout esplicito per prevenire blocchi indefiniti. Mai eseguire migration distruttive (DROP, DELETE) in automatico — aggiungi una richiesta di approvazione manuale con GitHub Environments.

