Eseguire test shell nelle pipeline CI
Inserisca ShellCheck e Bats in GitHub Actions per subordinare ogni modifica shell al superamento dei controlli.
Eseguire test shell nelle pipeline CI è una lezione DevOps Bootcamp gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento DevOps Bootcamp, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso DevOps Bootcamp include 4 lezioni in totale.
Perché la CI è importante per gli script di shell
Gli script di shell sono codice e, come tutto il codice, meritano controlli di qualità automatizzati. Senza CI, un errore di battitura in uno script di deployment può arrivare silenziosamente in produzione e causare un'interruzione del servizio alle 3 di notte.
Una pipeline CI solida per i progetti Bash verifica due aspetti a ogni pull request:
- Analisi statica tramite
ShellCheck: rileva errori di sintassi, schemi non sicuri e problemi di portabilità POSIX prima ancora che lo script venga eseguito. - Test unitari e di integrazione tramite
Bats(Bash Automated Testing System): esegue le funzioni e verifica che il comportamento sia corretto.
Insieme formano una rete di sicurezza che rende il refactoring più sicuro e accelera l'inserimento dei nuovi collaboratori. In questa lezione integrerà entrambi gli strumenti in GitHub Actions, la piattaforma CI gratuita più utilizzata per i progetti open source e quelli dei piccoli team.
Introduzione a GitHub Actions per i progetti di shell
GitHub Actions è una soluzione CI/CD basata sugli eventi e integrata in GitHub. Un workflow è un file YAML archiviato in .github/workflows/. Si attiva in risposta a eventi (push, pull_request e così via) ed esegue job su runner ospitati.
Concetti fondamentali:
on:— il trigger (ad esempiopush,pull_request)jobs:— unità di lavoro parallele, ciascuna eseguita su una VM nuovasteps:— comandi di shell sequenziali o actions riutilizzabili all'interno di un jobruns-on:— l'immagine del runner (utilizziamoubuntu-latest)
I file dei workflow devono essere sottoposti a commit nel repository. GitHub li rileva automaticamente: non è necessaria alcuna configurazione esterna.
# Minimal skeleton — .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
shell-checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Steps go here"Installazione di ShellCheck in un workflow
ShellCheck è preinstallato nei runner ubuntu-latest, quindi nella maggior parte dei casi non è necessario alcun passaggio di installazione. Tuttavia, la versione preinstallata potrebbe essere meno recente dell'ultima disponibile. Per build riproducibili, fissi una versione specifica.
Due strategie di installazione:
- Utilizzare il binario preinstallato — è la soluzione più semplice e sufficiente per la maggior parte dei progetti.
- Installare una versione fissata tramite l'archivio tar della release ufficiale su GitHub: garantisce la stessa versione del linter in locale e nella CI.
Il passaggio seguente mostra l'approccio con versione fissata, utilizzando una stringa di versione memorizzata come variabile d'ambiente, così gli aggiornamenti richiedono una sola modifica.
# .github/workflows/ci.yml — ShellCheck install step
- name: Install ShellCheck
env:
SC_VERSION: v0.10.0
run: |
curl -sSfL \
"https://github.com/koalaman/shellcheck/releases/download/${SC_VERSION}/shellcheck-${SC_VERSION}.linux.x86_64.tar.xz" \
| tar -xJf - --strip-components=1 -C /usr/local/bin shellcheck-${SC_VERSION}/shellcheck
shellcheck --versionEsecuzione di ShellCheck su ogni script
Dopo l'installazione, è necessario un passaggio che individui e analizzi con il linter tutti gli script di shell presenti nel repository. Utilizzi find per individuare i file, quindi li passi tramite pipe a shellcheck.
Flag importanti da conoscere:
-e SC2034— esclude una regola specifica (lo utilizzi con parsimonia e accompagnato da un commento).--severity=warning— fa fallire il controllo solo per gli avvisi e i livelli superiori (ignorando i suggerimenti di style).-x— segue le direttivesourceper analizzare con il linter anche i file inclusi.
Se shellcheck rileva un problema, termina con un codice diverso da zero, facendo fallire automaticamente il passaggio CI: non serve alcuna logica aggiuntiva.
# .github/workflows/ci.yml — ShellCheck lint step
- name: Lint shell scripts
run: |
# Find all .sh files and files with a bash/sh shebang
mapfile -t scripts < <(
find . -type f -name '*.sh' -not -path './.git/*'
)
if [[ ${#scripts[@]} -eq 0 ]]; then
echo 'No shell scripts found — skipping.'
exit 0
fi
echo "Linting ${#scripts[@]} file(s)..."
shellcheck --severity=warning -x "${scripts[@]}"Che cos'è Bats e come funziona
Bats (Bash Automated Testing System) è un framework di test per Bash conforme a TAP. Ogni file di test ha estensione .bats e contiene blocchi @test.
Un test ha esito positivo quando il suo corpo termina con codice 0 e fallisce quando termina con un codice diverso da zero. Bats mette a disposizione variabili e funzioni di supporto:
$status— codice di uscita dell'ultimo comandorun.$output— stdout+stderr combinati dell'ultimo comandorun.$lines— array delle righe di output.run <cmd>— esegue un comando senza far fallire il test in caso di uscita diversa da zero.
L'helper run è essenziale: senza di esso, un comando che fallisce interromperebbe il test prima che Lei possa esaminare $status.
#!/usr/bin/env bats
# tests/greet.bats
setup() {
# Runs before every @test block
source "${BATS_TEST_DIRNAME}/../lib/greet.sh"
}
@test "greet outputs hello with the given name" {
run greet "Alice"
[ "$status" -eq 0 ]
[ "$output" = "Hello, Alice!" ]
}
@test "greet fails when no argument is provided" {
run greet
[ "$status" -eq 1 ]
[[ "$output" == *"Usage"* ]]
}Installazione di Bats-Core tramite un sottomodulo Git
Il modo canonico per aggiungere Bats a un progetto consiste nell'utilizzare un sottomodulo Git. In questo modo si fissa uno specifico commit, si mantiene la stessa versione del runner in locale e si evita di dipendere dai package manager.
Esegua questi comandi una volta in locale, quindi esegua il commit del risultato:
git submodule add https://github.com/bats-core/bats-core test/batsgit submodule add https://github.com/bats-core/bats-support test/test_helper/bats-supportgit submodule add https://github.com/bats-core/bats-assert test/test_helper/bats-assert
Nella CI, ripristini i sottomoduli con actions/checkout@v4 e l'opzione submodules: recursive. Il passaggio seguente mostra la configurazione completa del checkout.
# .github/workflows/ci.yml — checkout with submodules
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive # restores bats-core + helpersEsecuzione dei test Bats nella CI
Quando Bats è disponibile (tramite sottomodulo o installazione di un package), eseguire i test richiede un solo comando. Indichi una directory e Bats individuerà ricorsivamente ogni file .bats con il flag --recursive.
Il flag --formatter tap produce l'output nel formato TAP (Test Anything Protocol), che molti sistemi CI analizzano per generare i report dei test. Il formatter predefinito pretty è più adatto alla lettura umana nei log non elaborati.
Utilizzi --timing per individuare subito i test lenti: un test che dura più di 5 secondi generalmente segnala una chiamata di rete indesiderata o un mock mancante.
# .github/workflows/ci.yml — Bats test step
- name: Run Bats tests
run: |
# If installed as a submodule:
./test/bats/bin/bats \
--recursive \
--timing \
tests/
# If installed via apt or brew (alternative):
# bats --recursive --timing tests/Un workflow completo: ShellCheck + Bats
Ora riunisca tutto in un unico file di workflow pronto per la produzione. Qui vengono applicate le seguenti best practice:
- Due job separati (
lintetest) vengono eseguiti in parallelo, fornendo un feedback più rapido. - Il job
testdichiaraneeds: lint, quindi i test vengono eseguiti solo dopo il superamento del linting, evitando di sprecare minuti del runner su codice evidentemente non funzionante. - Le versioni fissate delle action (
@v4) evitano problemi imprevisti causati dagli aggiornamenti upstream. - Un blocco
permissions:limita il token del workflow ai permessi minimi necessari.
# .github/workflows/ci.yml
name: Shell CI
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
lint:
name: ShellCheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run ShellCheck
run: |
mapfile -t scripts < <(find . -name '*.sh' -not -path './.git/*')
[[ ${#scripts[@]} -gt 0 ]] && shellcheck --severity=warning -x "${scripts[@]}"
test:
name: Bats Tests
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Run tests
run: ./test/bats/bin/bats --recursive --timing tests/Memorizzazione nella cache delle dipendenze per esecuzioni più rapide
Quando gli helper di Bats o altri strumenti vengono installati tramite un package manager all'interno del workflow, la cache accelera notevolmente le esecuzioni successive. GitHub Actions mette a disposizione l'action actions/cache a questo scopo.
Punti fondamentali per una cache efficace:
- Utilizzi una chiave della cache che includa il sistema operativo, il nome dello strumento e l'hash di un lockfile, così la cache viene invalidata automaticamente quando cambiano le dipendenze.
- Un fallback
restore-keysconsente al workflow di utilizzare una cache non aggiornata invece di ripartire da zero quando non trova una corrispondenza. - Per i sottomoduli Git, la cache è raramente necessaria perché il checkout dei sottomoduli è rapido. La cache è più utile per installazioni di
npm,pipo di strumenti compilati.
# .github/workflows/ci.yml — cache step example
- name: Cache Bats npm helpers
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-bats-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-bats-
- name: Install helpers
run: npm ci # uses cache when availableProtezione dei branch: imposizione dei controlli superati
Un workflow CI che non impedisce i merge è, nel migliore dei casi, un semplice suggerimento. Le regole di protezione dei branch di GitHub trasformano i controlli in veri gate obbligatori.
Per configurarle: apra Settings → Branches → Add rule per main, quindi abiliti:
- Require status checks to pass before merging — selezioni ShellCheck e Bats Tests in base al nome.
- Require branches to be up to date before merging — impedisce che una PR che ha superato i controlli su una base obsoleta introduca codice non funzionante.
- Do not allow bypassing the above settings — applica le regole anche agli amministratori del repository.
Con queste regole attive, l'unico modo per eseguire il merge è una PR con tutti i job CI superati: esattamente la rete di sicurezza che desidera.
Debug dei passaggi CI non riusciti in locale
Quando un'esecuzione CI fallisce, il ciclo di correzione più rapido consiste nel riprodurre localmente il problema prima di inviare un altro commit. Due tecniche:
- Eseguire gli stessi comandi del passaggio non riuscito nel terminale: la CI esegue una normale shell, quindi i comandi possono essere riprodotti con copia e incolla.
- Utilizzare
act: uno strumento che esegue localmente i workflow di GitHub Actions all'interno di Docker, offrendo la corrispondenza più vicina possibile all'ambiente del runner ospitato.
Una causa comune dei problemi che si verificano solo nella CI è una differenza di versione degli strumenti tra il Suo Mac (ad esempio find BSD su macOS rispetto a find GNU su Ubuntu). Verifichi sempre con i flag --posix oppure utilizzi act per eseguire localmente l'immagine Ubuntu.
#!/usr/bin/env bash
# run_ci_locally.sh — mimic the CI lint step on your machine
set -euo pipefail
echo '=== ShellCheck ==='
mapfile -t scripts < <(find . -name '*.sh' -not -path './.git/*')
if [[ ${#scripts[@]} -eq 0 ]]; then
echo 'No .sh files found.'
else
shellcheck --severity=warning -x "${scripts[@]}"
echo "Linted ${#scripts[@]} file(s) — OK"
fi
echo '=== Bats ==='
./test/bats/bin/bats --recursive --timing tests/Verifica delle conoscenze: concetti della pipeline CI
Verifichi la Sua comprensione dell'integrazione di ShellCheck e Bats in GitHub Actions.
Riepilogo: CI per shell con ShellCheck e Bats
In questa lezione ha creato una pipeline CI completa per i progetti Bash utilizzando GitHub Actions. Ecco gli argomenti trattati:
- Nozioni di base su GitHub Actions — il workflow YAML risiede in
.github/workflows/, si attiva con push e pull_request ed esegue i job sui runnerubuntu-latest. - ShellCheck — è preinstallato sui runner Ubuntu; utilizzi
findper individuare gli script e--severity=warning -xper un controllo lint pratico. - Bats tramite sottomodulo — fissi bats-core e gli helper come sottomoduli Git; li ripristini nella CI con
submodules: recursivenell'action di checkout. - Ordine dei job — utilizzi
needs:affinché i test vengano eseguiti solo dopo il superamento del linting, mantenendo un feedback rapido ed evitando sprechi di risorse di calcolo. - Protezione dei branch — imponga i controlli dello stato nelle impostazioni di GitHub, così nessuna PR può essere integrata senza una CI superata.
- Riproduzione locale — copi i comandi CI direttamente nel terminale oppure utilizzi
actper eseguire il debug dei problemi senza creare commit aggiuntivi.
Con questa pipeline attiva, ogni modifica alla shell viene verificata automaticamente prima di raggiungere il Suo branch principale.
Domande Frequenti
La lezione «Eseguire test shell nelle pipeline CI» è gratuita?
Sì — il testo completo di «Eseguire test shell nelle pipeline CI» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso DevOps Bootcamp, passa a CoddyKit PRO. Il corso DevOps Bootcamp include 4 lezioni in totale.
Cosa imparerò in «Eseguire test shell nelle pipeline CI»?
Inserisca ShellCheck e Bats in GitHub Actions per subordinare ogni modifica shell al superamento dei controlli. Eserciti DevOps Bootcamp con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare DevOps Bootcamp?
Non è richiesta alcuna esperienza precedente. DevOps Bootcamp su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.
Quanto tempo richiede la lezione «Eseguire test shell nelle pipeline CI»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione DevOps Bootcamp?
Sì. Ogni lezione DevOps Bootcamp include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Test unitari delle funzioni con Bats-core
- Simulare comandi e creare stub per strumenti esterni
- Fixture, ambienti temporanei e coverage
- Eseguire test shell nelle pipeline CI