Salta al contenuto principale

Automazione Pianificata con GitHub Actions: Casi d'Uso DevOps Reali

·7 minuti
Indice dei contenuti
I trigger cron in GitHub Actions sono potenti per l’automazione che non dovrebbe richiedere un essere umano per attivarsi: audit notturni di vulnerabilità, controlli settimanali dei costi, alert di scadenza dei certificati. Questa è la guida pratica – workflow reali, casi d’uso reali e le trappole da evitare.

La maggior parte dei tutorial su GitHub Actions tratta i trigger push e pull request. Il trigger schedule riceve meno attenzione, ma è spesso dove vive l’automazione più preziosa. Un audit notturno delle dipendenze che apre una issue quando trova CVE. Un controllo settimanale dei costi che avvisa Slack quando la bolletta AWS sale. Un controllo dei certificati che ti avvisa prima che il certificato scada, non dopo. Niente di tutto questo richiede che un developer prema un pulsante.

Riferimento Rapido alla Sintassi Cron
#

GitHub Actions usa la sintassi cron POSIX standard con cinque campi.

CampoValoriEsempio
Minuto0-5930 = alle :30
Ora0-239 = alle 09:00
Giorno del mese1-311 = primo del mese
Mese1-12 o JAN-DEC* = ogni mese
Giorno della settimana0-6 o SUN-SAT (0=Domenica)1 = Lunedì
# Formato: minuto  ora  giorno-del-mese  mese  giorno-della-settimana
#
# Esempi:
# 0 9 * * 1-5      Ogni giorno feriale alle 09:00
# 0 0 * * *        Ogni giorno a mezzanotte
# 30 6 * * 1       Ogni lunedì alle 06:30
# 0 */4 * * *      Ogni 4 ore
# 0 9 1 * *        Primo di ogni mese alle 09:00
Importante

GitHub Actions esegue tutti i workflow pianificati in UTC. Non c’è modo di configurare un fuso orario per il trigger schedule. Se il tuo team è in CET (UTC+1) e vuoi un’esecuzione giornaliera alle 09:00, usa 0 8 * * *. Durante il CEST (UTC+2) diventa 0 7 * * *. Adegua l’espressione cron stagionalmente o accetta la deriva di un’ora.

Nota

GitHub non garantisce orari di esecuzione esatti per i workflow pianificati. Durante periodi di alto carico di GitHub Actions, le esecuzioni possono essere ritardate di diversi minuti. Non usare workflow pianificati per nulla che richieda precisione sub-minuto.

Combinare schedule con workflow_dispatch
#

Aggiungi sempre workflow_dispatch insieme a schedule. Senza di esso, non puoi attivare manualmente il workflow per i test e sei costretto ad aspettare la prossima esecuzione pianificata ogni volta che apporti una modifica.

.github/workflows/nightly-audit.yaml
name: Nightly Audit

on:
  schedule:
    - cron: '0 2 * * *'   # 02:00 UTC ogni giorno
  workflow_dispatch:        # attivabile manualmente dall'UI o API
    inputs:
      dry_run:
        description: 'Esegui in modalità dry-run (non aprire issue)'
        type: boolean
        default: false

workflow_dispatch con input ti permette di parametrizzare le esecuzioni manuali senza creare un workflow di debug separato.

Caso d’Uso 1: Audit Notturno delle Vulnerabilità nelle Dipendenze
#

Esegui uno scanner di vulnerabilità ogni notte e apri una GitHub issue se viene trovato qualcosa, invece di scoprirlo quando un developer esegue npm audit localmente per caso.

.github/workflows/nightly-audit.yaml
name: Nightly Dependency Audit

on:
  schedule:
    - cron: '0 2 * * *'
  workflow_dispatch:

jobs:
  audit:
    runs-on: ubuntu-latest
    permissions:
      issues: write
      contents: read
    steps:
      - uses: actions/checkout@v4

      - name: Run pip-audit
        id: audit
        run: |
          pip install pip-audit
          pip-audit --output json > audit-results.json || echo "VULNERABILITIES_FOUND=true" >> $GITHUB_ENV

      - name: Apri issue se trovate vulnerabilità
        if: env.VULNERABILITIES_FOUND == 'true'
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const results = fs.readFileSync('audit-results.json', 'utf8');
            await github.rest.issues.create({
              owner: context.repo.owner,
              repo: context.repo.repo,
              title: `[Security] Vulnerabilità nelle dipendenze trovate - ${new Date().toISOString().split('T')[0]}`,
              body: `## Risultati Audit Vulnerabilità\n\`\`\`json\n${results}\n\`\`\`\n\nAttivato dal workflow di audit notturno.`,
              labels: ['security', 'dependencies']
            });

Caso d’Uso 2: Controllo Settimanale dei Costi AWS
#

Interroga i tuoi costi AWS degli ultimi 7 giorni ogni lunedì mattina e invia a Slack se la spesa supera una soglia.

.github/workflows/weekly-cost-check.yaml
name: Weekly AWS Cost Check

on:
  schedule:
    - cron: '0 7 * * 1'   # Lunedì 07:00 UTC
  workflow_dispatch:

jobs:
  cost-check:
    runs-on: ubuntu-latest
    steps:
      - name: Ottieni costo AWS degli ultimi 7 giorni
        id: cost
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: eu-central-1
        run: |
          END=$(date -u +%Y-%m-%d)
          START=$(date -u -d '7 days ago' +%Y-%m-%d)
          AMOUNT=$(aws ce get-cost-and-usage \
            --time-period Start=$START,End=$END \
            --granularity MONTHLY \
            --metrics "UnblendedCost" \
            --query 'ResultsByTime[0].Total.UnblendedCost.Amount' \
            --output text)
          echo "COST=$AMOUNT" >> $GITHUB_ENV
          echo "Spesa settimanale: $AMOUNT USD"

      - name: Avvisa Slack se la spesa supera la soglia
        if: ${{ env.COST > 500 }}
        env:
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        run: |
          curl -s -X POST "$SLACK_WEBHOOK" \
            -H "Content-Type: application/json" \
            -d "{\"text\": \":warning: La spesa settimanale AWS è \$$COST USD (soglia: \$500). Controlla il Cost Explorer.\"}"

Caso d’Uso 3: Controllo Scadenza Certificati SSL
#

Controlla le date di scadenza dei certificati dei tuoi endpoint pubblici settimanalmente e avvisa quando un certificato è entro 30 giorni dalla scadenza.

.github/workflows/cert-check.yaml
name: Certificate Expiry Check

on:
  schedule:
    - cron: '0 8 * * 1'   # Lunedì 08:00 UTC
  workflow_dispatch:

jobs:
  cert-check:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        host:
          - api.example.com
          - app.example.com
    steps:
      - name: Controlla certificato per ${{ matrix.host }}
        env:
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        run: |
          EXPIRY=$(echo | openssl s_client -servername ${{ matrix.host }} \
            -connect ${{ matrix.host }}:443 2>/dev/null \
            | openssl x509 -noout -enddate \
            | cut -d= -f2)
          EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
          NOW_EPOCH=$(date +%s)
          DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
          echo "Il certificato per ${{ matrix.host }} scade tra $DAYS_LEFT giorni ($EXPIRY)"
          if [ "$DAYS_LEFT" -lt 30 ]; then
            curl -s -X POST "$SLACK_WEBHOOK" \
              -H "Content-Type: application/json" \
              -d "{\"text\": \":rotating_light: Il certificato per *${{ matrix.host }}* scade tra *$DAYS_LEFT giorni* ($EXPIRY). Rinnova immediatamente.\"}"
          fi
Suggerimento

Usa strategy.matrix per controllare più host in parallelo senza duplicare le definizioni dei job. Ogni voce della matrix viene eseguita in un proprio job con il proprio log.

Caso d’Uso 4: Aggiornamento Automatico delle Dipendenze
#

Esegui npm update settimanalmente, committa il risultato e apri una pull request automaticamente se i pacchetti sono stati aggiornati.

.github/workflows/weekly-deps-update.yaml
name: Weekly Dependency Update

on:
  schedule:
    - cron: '0 6 * * 1'   # Lunedì 06:00 UTC
  workflow_dispatch:

jobs:
  update:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Aggiorna dipendenze
        run: npm update

      - name: Crea pull request se qualcosa è cambiato
        uses: peter-evans/create-pull-request@v6
        with:
          commit-message: 'chore: aggiornamento settimanale dipendenze'
          title: 'chore: aggiornamento settimanale dipendenze'
          body: 'Aggiornamento automatico settimanale delle dipendenze tramite GitHub Actions.'
          branch: automated/weekly-deps-update
          delete-branch: true
          labels: dependencies

Controllo della Concorrenza
#

Se un workflow pianificato richiede più tempo del suo intervallo di pianificazione (ad es. un workflow da 5 minuti con una pianificazione di 2 minuti), si accumulano esecuzioni multiple. Usa la chiave concurrency per annullare le esecuzioni in corso quando ne inizia una nuova.

.github/workflows/nightly-audit.yaml
name: Nightly Audit

on:
  schedule:
    - cron: '0 2 * * *'
  workflow_dispatch:

concurrency:
  group: nightly-audit
  cancel-in-progress: true

jobs:
  audit:
    # ...

group è una stringa che identifica il gruppo di concorrenza. Qualsiasi nuova esecuzione nello stesso gruppo annullerà quella attualmente in esecuzione (se cancel-in-progress: true). Usa una stringa fissa per i workflow pianificati dove non vuoi mai che due esecuzioni si sovrappongano.

Debug dei Workflow Pianificati
#

Avviso

I workflow pianificati vengono eseguiti solo sul branch predefinito. Se aggiungi un trigger schedule su un branch di feature, verrà ignorato. Testa il tuo workflow facendo il merge sul branch predefinito e usando workflow_dispatch per attivarlo manualmente prima della prossima esecuzione pianificata.

Checklist di debug utile:

  • Aggiungi workflow_dispatch a ogni workflow pianificato (senza eccezioni).
  • Controlla la scheda Actions per vedere se il workflow è elencato – se non è elencato, il YAML non è valido o il workflow si trova su un branch non predefinito.
  • Usa act (il runner locale di GitHub Actions) per iterare rapidamente senza aspettare lo scheduler.
  • Aggiungi un passaggio che stampa date -u per confermare il contesto temporale UTC.
**Dimenticare che tutti gli orari sono UTC.** Un workflow impostato su `0 9 * * *` viene eseguito alle 09:00 UTC, che corrispondono alle 10:00 o 11:00 nell'ora dell'Europa Centrale a seconda dell'ora legale. Documenta l'orario UTC in modo ben visibile nel commento del file del workflow. **Nessun `workflow_dispatch` per il debug.** Senza di esso, ogni modifica al workflow richiede di aspettare la prossima esecuzione pianificata per la validazione. Questo estende il ciclo di iterazione da secondi a ore o giorni. Aggiungi sempre `workflow_dispatch`. **Nessuna chiave `concurrency` su job di lunga durata.** I job pianificati che si sovrappongono creano issue duplicate, alert Slack duplicati e uno stato parallelo confuso. Aggiungi un gruppo `concurrency` con `cancel-in-progress: true` come default. **Mettere segreti di lunga durata direttamente nei workflow cron.** I workflow pianificati vengono eseguiti senza supervisione. Se un segreto viene ruotato, il workflow fallisce silenziosamente fino a quando qualcuno non nota gli alert mancanti. Aggiungi un passaggio che valida che i segreti richiesti non siano vuoti all'inizio del workflow, in modo che i fallimenti siano evidenti.

Se vuoi approfondire uno qualsiasi di questi argomenti, offro sessioni di coaching 1:1 per ingegneri che lavorano su integrazione AI, architettura cloud e platform engineering. Prenota una sessione (50 EUR / 60 min) o scrivimi a manuel.fedele+website@gmail.com.

Articoli correlati