Shell-Tests in CI-Pipelines ausführen
Binden Sie ShellCheck und Bats in GitHub Actions ein, damit jede Änderung an Shell-Skripten nur bei erfolgreichen Prüfungen zugelassen wird.
Shell-Tests in CI-Pipelines ausführen ist eine kostenlose DevOps Bootcamp-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des DevOps Bootcamp-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der DevOps Bootcamp-Kurs umfasst insgesamt 4 Lektionen.
Warum CI für Shell-Skripte wichtig ist
Shell-Skripte sind Code – und verdienen wie jeder Code automatisierte Qualitätssicherungen. Ohne CI kann ein Tippfehler in einem Deployment-Skript unbemerkt die Produktion erreichen und um 3 Uhr morgens einen Ausfall verursachen.
Eine solide CI-Pipeline für Bash-Projekte erzwingt bei jedem Pull Request zwei Dinge:
- Statische Analyse mit
ShellCheck– erkennt Syntaxfehler, unsichere Muster und POSIX-Portabilitätsprobleme, bevor das Skript überhaupt ausgeführt wird. - Unit-/Integrationstests mit
Bats(Bash Automated Testing System) – führt Ihre Funktionen aus und prüft, ob sie sich korrekt verhalten.
Zusammen bilden sie ein Sicherheitsnetz, das Refactorings ohne Bedenken und ein schnelleres Onboarding ermöglicht. In dieser Lektion werden beide Tools in GitHub Actions eingebunden, die häufigste kostenlose CI-Plattform für Open-Source- und kleine Teamprojekte.
Einführung in GitHub Actions für Shell-Projekte
GitHub Actions ist eine ereignisgesteuerte CI/CD-Plattform, die in GitHub integriert ist. Ein Workflow ist eine YAML-Datei im Verzeichnis .github/workflows/. Sie wird durch Ereignisse (push, pull_request usw.) ausgelöst und führt Jobs auf gehosteten Runnern aus.
Die wichtigsten benötigten Konzepte:
on:– der Auslöser (z. B.push,pull_request)jobs:– parallel ausführbare Arbeitseinheiten, jeweils auf einer frischen VMsteps:– sequenzielle Shell-Befehle oder wiederverwendbare Actions innerhalb eines Jobsruns-on:– das Runner-Image (hier verwenden wirubuntu-latest)
Workflow-Dateien müssen in das Repository committet werden. GitHub erkennt sie automatisch – eine externe Einrichtung ist nicht erforderlich.
# 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"ShellCheck in einem Workflow installieren
ShellCheck ist auf ubuntu-latest-Runnern vorinstalliert, daher benötigen Sie in den meisten Fällen keine Installationsschritte. Die vorinstallierte Version kann jedoch hinter der neuesten Veröffentlichung zurückliegen. Für reproduzierbare Builds sollten Sie eine bestimmte Version festlegen.
Zwei Installationsstrategien:
- Das vorinstallierte Binary verwenden – am einfachsten und für die meisten Projekte ausreichend.
- Eine festgelegte Version installieren – über das offizielle GitHub-Release-Tarball; dadurch wird garantiert, dass lokal und in CI dieselbe Linter-Version verwendet wird.
Der folgende Schritt zeigt den Ansatz mit festgelegter Version. Dabei wird eine feste Versionszeichenfolge als Umgebungsvariable gespeichert, sodass ein Upgrade nur eine einzeilige Änderung erfordert.
# .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 --versionShellCheck für jedes Skript ausführen
Nach der Installation benötigen Sie einen Schritt, der alle Shell-Skripte im Repository findet und prüft. Verwenden Sie find, um die Dateien zu suchen, und leiten Sie sie anschließend an shellcheck weiter.
Wichtige Optionen:
-e SC2034– schließt eine bestimmte Regel aus (sparsam und mit einem Kommentar verwenden).--severity=warning– lässt nur bei Warnungen und schwerwiegenderen Problemen fehlschlagen (ignoriert Style-Vorschläge).-x– folgtsource-Direktiven, um auch eingebundene Dateien zu prüfen.
Wenn shellcheck ein Problem findet, wird es mit einem Fehlercode ungleich null beendet. Dadurch schlägt der CI-Schritt automatisch fehl – zusätzliche Logik ist nicht erforderlich.
# .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[@]}"Was ist Bats und wie funktioniert es?
Bats (Bash Automated Testing System) ist ein TAP-kompatibles Test-Framework für Bash. Jede Testdatei ist eine .bats-Datei, die @test-Blöcke enthält.
Ein Test ist erfolgreich, wenn sein Hauptteil mit 0 beendet wird, und schlägt fehl, wenn er mit einem Wert ungleich null beendet wird. Bats stellt Hilfsvariablen und -funktionen bereit:
$status– Exit-Code des letztenrun-Befehls.$output– zusammengeführte Standardausgabe und Fehlerausgabe des letztenrun-Befehls.$lines– Array mit den Ausgabezeilen.run <cmd>– führt einen Befehl aus, ohne den Test bei einem Exit-Code ungleich null fehlschlagen zu lassen.
Der run-Helfer ist unverzichtbar: Ohne ihn würde ein fehlschlagender Befehl den Test abbrechen, bevor Sie $status untersuchen können.
#!/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"* ]]
}Bats-Core als Git-Submodul installieren
Die übliche Vorgehensweise, Bats zu einem Projekt hinzuzufügen, ist die Verwendung eines Git-Submoduls. Dadurch wird ein bestimmter Commit festgelegt, die Runner-Version bleibt identisch zur lokalen Entwicklungsumgebung, und Sie sind nicht von Paketmanagern abhängig.
Führen Sie diese Befehle einmal lokal aus und committen Sie anschließend das Ergebnis:
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
Stellen Sie Submodule in CI mit actions/checkout@v4 und der Option submodules: recursive wieder her. Der folgende Schritt zeigt die vollständige Checkout-Konfiguration.
# .github/workflows/ci.yml — checkout with submodules
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive # restores bats-core + helpersBats-Tests in CI ausführen
Sobald Bats verfügbar ist (über ein Submodul oder eine Paketinstallation), lassen sich die Tests mit einem einzigen Befehl ausführen. Geben Sie ein Verzeichnis an. Bats findet dann mit der Option --recursive rekursiv jede .bats-Datei.
Die Option --formatter tap gibt das TAP-Format (Test Anything Protocol) aus, das viele CI-Systeme für die Testberichterstattung analysieren können. Der standardmäßige pretty-Formatter ist für Menschen in unbearbeiteten Logs besser lesbar.
Verwenden Sie --timing, um langsame Tests frühzeitig sichtbar zu machen – ein Test, der länger als 5 Sekunden dauert, deutet meist auf einen unerwünschten Netzwerkaufruf oder ein fehlendes Mock hin.
# .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/Ein vollständiger Workflow: ShellCheck + Bats
Führen Sie nun alles in einer produktionsreifen Workflow-Datei zusammen. Hier angewendete Best Practices:
- Zwei separate Jobs (
lintundtest) werden parallel ausgeführt und liefern dadurch schnelleres Feedback. - Der
test-Job deklariertneeds: lint, sodass Tests erst nach erfolgreicher Prüfung durch den Linter ausgeführt werden. Dadurch werden keine Runner-Minuten für offensichtlich fehlerhaften Code verschwendet. - Festgelegte Action-Versionen (
@v4) verhindern unerwartete Fehler durch Upstream-Updates. - Ein Block
permissions:beschränkt das Workflow-Token auf die erforderlichen Mindestberechtigungen.
# .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/Abhängigkeiten für schnellere Ausführungen zwischenspeichern
Wenn Bats-Helfer oder andere Tools innerhalb des Workflows über einen Paketmanager installiert werden, beschleunigt Caching nachfolgende Ausführungen erheblich. GitHub Actions stellt dafür die Action actions/cache bereit.
Wichtige Punkte für effektives Caching:
- Verwenden Sie einen Cache-Schlüssel, der das Betriebssystem, den Toolnamen und einen Hash der Lockdatei enthält. Dadurch wird der Cache bei Änderungen an den Abhängigkeiten automatisch ungültig.
- Ein Fallback über
restore-keysermöglicht es dem Workflow, bei einem Cache Miss einen veralteten Cache zu verwenden, statt ganz von vorn zu beginnen. - Für Git-Submodule ist Caching selten erforderlich, da der Checkout von Submodulen schnell erfolgt. Am wertvollsten ist Caching für
npm,pipoder die Installation kompilierter Tools.
# .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 availableBranch Protection: Erfolgreiche Checks erzwingen
Ein CI-Workflow, der Zusammenführungen nicht blockiert, hat bestenfalls empfehlenden Charakter. Die Branch-Protection-Regeln von GitHub machen aus Ihren Checks verbindliche Prüfungen.
So konfigurieren Sie sie: Wählen Sie für main Settings → Branches → Add rule und aktivieren Sie anschließend:
- Require status checks to pass before merging – wählen Sie ShellCheck und Bats Tests nach Namen aus.
- Require branches to be up to date before merging – verhindert, dass ein Pull Request, dessen Checks auf einer veralteten Basis erfolgreich waren, fehlerhaften Code einführt.
- Do not allow bypassing the above settings – wendet die Regeln auch auf Repository-Administratoren an.
Mit diesen Regeln führt der einzige Weg zu einer Zusammenführung über einen Pull Request, bei dem alle CI-Jobs erfolgreich sind – genau das gewünschte Sicherheitsnetz.
Fehlschlagende CI-Schritte lokal debuggen
Wenn eine CI-Ausführung fehlschlägt, lässt sich der Fehler am schnellsten beheben, indem Sie ihn lokal reproduzieren, bevor Sie einen weiteren Commit pushen. Dafür gibt es zwei Techniken:
- Führen Sie die exakten Befehle aus dem fehlgeschlagenen Schritt in Ihrem Terminal aus – CI verwendet einfache Shell-Befehle, die sich per Copy-and-paste reproduzieren lassen.
- Verwenden Sie
act– ein Tool, das GitHub-Actions-Workflows lokal in Docker ausführt und dadurch eine möglichst genaue Entsprechung zur Umgebung des gehosteten Runners bietet.
Eine häufige Ursache für Fehler, die nur in CI auftreten, ist ein Unterschied zwischen den Tool-Versionen auf Ihrem Mac (z. B. BSD-find unter macOS gegenüber GNU-find unter Ubuntu). Testen Sie immer mit --posix-Optionen oder verwenden Sie act, um das Ubuntu-Image lokal auszuführen.
#!/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/Wissenscheck: Konzepte der CI-Pipeline
Testen Sie Ihr Verständnis davon, wie ShellCheck und Bats in GitHub Actions eingebunden werden.
Rückblick: Shell-CI mit ShellCheck und Bats
In dieser Lektion haben Sie mithilfe von GitHub Actions eine vollständige CI-Pipeline für Bash-Projekte erstellt. Sie haben Folgendes behandelt:
- Grundlagen von GitHub Actions – Workflow-YAML liegt in
.github/workflows/, wird bei push und pull_request ausgelöst und führt Jobs aufubuntu-latest-Runnern aus. - ShellCheck – auf Ubuntu-Runnern vorinstalliert; verwenden Sie
find, um Skripte zu finden, sowie--severity=warning -xfür eine praxisgerechte Lint-Prüfung. - Bats über ein Submodul – legen Sie bats-core und die Helfer als Git-Submodule fest und stellen Sie sie in CI mit
submodules: recursivebei der Checkout-Action wieder her. - Reihenfolge der Jobs – verwenden Sie
needs:, damit Tests erst nach erfolgreicher Lint-Prüfung ausgeführt werden. So erhalten Sie schnelles Feedback und vermeiden verschwendete Rechenzeit. - Branch Protection – erzwingen Sie Statusprüfungen in den GitHub-Einstellungen, damit kein Pull Request ohne erfolgreiche CI zusammengeführt werden kann.
- Lokale Reproduktion – kopieren Sie CI-Befehle direkt in Ihr Terminal oder verwenden Sie
act, um Fehler ohne zusätzliche Commits zu debuggen.
Mit dieser Pipeline wird jede Änderung an Shell-Code automatisch geprüft, bevor sie Ihren Hauptbranch erreicht.
Häufig gestellte Fragen
Ist die Lektion „Shell-Tests in CI-Pipelines ausführen“ kostenlos?
Ja — der vollständige Text von „Shell-Tests in CI-Pipelines ausführen“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des DevOps Bootcamp-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der DevOps Bootcamp-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „Shell-Tests in CI-Pipelines ausführen“?
Binden Sie ShellCheck und Bats in GitHub Actions ein, damit jede Änderung an Shell-Skripten nur bei erfolgreichen Prüfungen zugelassen wird. Du übst DevOps Bootcamp mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um DevOps Bootcamp zu starten?
Keine Vorkenntnisse erforderlich. DevOps Bootcamp auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.
Wie lange dauert die Lektion „Shell-Tests in CI-Pipelines ausführen“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser DevOps Bootcamp-Lektion Code schreiben und ausführen?
Ja. Jede DevOps Bootcamp-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Funktionen mit Bats-core per Unit-Test prüfen
- Befehle mocken und externe Tools stubben
- Fixtures, temporäre Umgebungen und Coverage
- Shell-Tests in CI-Pipelines ausführen