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.
| Campo | Valori | Esempio |
|---|---|---|
| Minuto | 0-59 | 30 = alle :30 |
| Ora | 0-23 | 9 = alle 09:00 |
| Giorno del mese | 1-31 | 1 = primo del mese |
| Mese | 1-12 o JAN-DEC | * = ogni mese |
| Giorno della settimana | 0-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:00GitHub 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.
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.
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: falseworkflow_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.
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.
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.
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.\"}"
fiUsa 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.
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: dependenciesControllo 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.
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#
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_dispatcha 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 -uper confermare il contesto temporale UTC.
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.