Ejecución de pruebas de shell en pipelines de CI
Integre ShellCheck y Bats en GitHub Actions para exigir comprobaciones correctas en cada cambio del shell.
Ejecución de pruebas de shell en pipelines de CI es una lección gratuita de Linux Command Line & Bash Scripting Mastery en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Linux Command Line & Bash Scripting Mastery, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Linux Command Line & Bash Scripting Mastery incluye 4 lecciones en total.
Por qué es importante CI para los scripts de shell
Los scripts de shell son código y, como todo código, merecen controles de calidad automatizados. Sin CI, un error tipográfico en un script de despliegue puede llegar silenciosamente a producción y provocar una interrupción del servicio a las 3 de la mañana.
Una canalización de CI sólida para proyectos de Bash aplica dos controles en cada solicitud de incorporación de cambios:
- Análisis estático mediante
ShellCheck: detecta errores de sintaxis, patrones inseguros y problemas de portabilidad POSIX antes de que el script llegue a ejecutarse. - Pruebas unitarias/de integración mediante
Bats(Bash Automated Testing System): ejecuta sus funciones y comprueba que su comportamiento sea correcto.
Juntos forman una red de seguridad que permite refactorizar sin temor y agiliza la incorporación de nuevos colaboradores. En esta lección se integran ambas herramientas en GitHub Actions, la plataforma de CI gratuita más habitual para proyectos de código abierto y equipos pequeños.
Introducción a GitHub Actions para proyectos de shell
GitHub Actions es una solución de CI/CD basada en eventos e integrada en GitHub. Un workflow es un archivo YAML almacenado en .github/workflows/. Se activa mediante eventos (push, pull_request, etc.) y ejecuta jobs en ejecutores alojados.
Estos son los conceptos clave que necesita:
on:— el activador (por ejemplo,push,pull_request)jobs:— unidades de trabajo paralelas, cada una en una máquina virtual nuevasteps:— comandos de shell secuenciales o actions reutilizables dentro de un jobruns-on:— la imagen del ejecutor (usamosubuntu-latest)
Los archivos de workflow deben confirmarse en el repositorio. GitHub los detecta automáticamente; no se requiere configuración externa.
# 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"Instalación de ShellCheck en un workflow
ShellCheck viene preinstalado en los ejecutores ubuntu-latest, por lo que en la mayoría de los casos no necesita ningún paso de instalación. Sin embargo, la versión preinstalada puede estar atrasada respecto a la versión más reciente. Para obtener compilaciones reproducibles, fije una versión específica.
Hay dos estrategias de instalación:
- Usar el binario preinstalado — es la opción más sencilla y suficiente para la mayoría de los proyectos.
- Instalar una versión fijada mediante el archivo tar de la versión oficial de GitHub — garantiza la misma versión del linter en el entorno local y en CI.
El paso siguiente muestra el enfoque con una versión fijada mediante una cadena de versión constante almacenada como variable de entorno, de modo que actualizarla solo requiera cambiar una línea.
# .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 --versionEjecución de ShellCheck en todos los scripts
Después de la instalación, necesita un paso que encuentre y analice todos los scripts de shell del repositorio. Use find para localizar los archivos y, después, páselos mediante una tubería a shellcheck.
Estas son las opciones importantes:
-e SC2034— excluye una regla específica (úselo con moderación y acompañada de un comentario).--severity=warning— hace que falle solo ante advertencias o errores, ignorando las sugerencias de style.-x— sigue las directivassourcepara analizar también los archivos incluidos.
Si shellcheck encuentra algún problema, termina con un código distinto de cero, lo que hace que el paso de CI falle automáticamente; no se necesita lógica adicional.
# .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[@]}"¿Qué es Bats y cómo funciona?
Bats (Bash Automated Testing System) es un framework de pruebas para Bash compatible con TAP. Cada archivo de pruebas es un archivo .bats que contiene bloques @test.
Una prueba se aprueba cuando el cuerpo termina con el código 0 y falla cuando termina con un código distinto de cero. Bats proporciona variables y funciones auxiliares:
$status— código de salida del último comandorun.$output— salida estándar y de error combinadas del último comandorun.$lines— matriz de líneas de salida.run <cmd>— ejecuta un comando sin hacer que la prueba falle cuando termina con un código distinto de cero.
La función auxiliar run es esencial: sin ella, un comando fallido abortaría la prueba antes de que pudiera inspeccionar $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"* ]]
}Instalación de Bats-Core mediante un submódulo de Git
La forma canónica de añadir Bats a un proyecto es mediante un submódulo de Git. Esto fija un commit específico, mantiene idéntica la versión del ejecutor y la del entorno de desarrollo local, y evita depender de gestores de paquetes.
Ejecute estos comandos una vez en el entorno local y, después, confirme el resultado:
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
En CI, restaure los submódulos con actions/checkout@v4 y la opción submodules: recursive. El paso siguiente muestra la configuración completa del checkout.
# .github/workflows/ci.yml — checkout with submodules
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive # restores bats-core + helpersEjecución de pruebas de Bats en CI
Una vez que Bats esté disponible (mediante un submódulo o la instalación de un paquete), ejecutar las pruebas requiere un solo comando. Indique un directorio y Bats descubrirá de forma recursiva todos los archivos .bats con la opción --recursive.
La opción --formatter tap genera el formato TAP (Test Anything Protocol), que muchos sistemas de CI pueden analizar para elaborar informes de pruebas. El formateador pretty predeterminado es más adecuado para la lectura humana en los registros sin procesar.
Use --timing para detectar pronto las pruebas lentas: una prueba que tarda más de 5 segundos suele indicar una llamada de red no deseada o la ausencia de un mock.
# .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
Ahora combine todo en un único archivo de workflow listo para producción. Estas son las prácticas recomendadas aplicadas:
- Dos jobs independientes (
lintytest) se ejecutan en paralelo, lo que proporciona comentarios más rápidos. - El job
testdeclaraneeds: lint, por lo que las pruebas solo se ejecutan después de que el análisis haya terminado correctamente; así se evita desperdiciar minutos del ejecutor en código obviamente defectuoso. - Las versiones fijadas de las actions (
@v4) evitan fallos inesperados causados por actualizaciones ascendentes. - Un bloque
permissions:restringe el token del workflow al mínimo necesario.
# .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/Almacenamiento en caché de dependencias para acelerar las ejecuciones
Cuando los auxiliares de Bats u otras herramientas se instalan mediante un gestor de paquetes dentro del workflow, el almacenamiento en caché acelera considerablemente las ejecuciones posteriores. GitHub Actions proporciona la action actions/cache para este fin.
Aspectos clave para un almacenamiento en caché eficaz:
- Use una clave de caché que incluya el sistema operativo, el nombre de la herramienta y un hash del archivo de bloqueo, de modo que la caché se invalide automáticamente cuando cambien las dependencias.
- Un valor alternativo en
restore-keyspermite que el workflow use una caché obsoleta en lugar de empezar desde cero cuando no hay coincidencias. - Para los submódulos de Git, la caché rara vez es necesaria porque su checkout es rápido. La caché resulta más útil para instalaciones de
npm,pipo herramientas compiladas.
# .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 availableProtección de ramas: imposición de comprobaciones correctas
Un workflow de CI que no bloquea las fusiones es, como mucho, meramente informativo. Las reglas de protección de ramas de GitHub convierten sus comprobaciones en controles obligatorios.
Para configurarlas, vaya a Settings → Branches → Add rule para main y, después, active:
- Require status checks to pass before merging — seleccione ShellCheck y Bats Tests por nombre.
- Require branches to be up to date before merging — evita que una solicitud de incorporación de cambios que superó las comprobaciones sobre una base obsoleta introduzca código defectuoso.
- Do not allow bypassing the above settings — aplica las reglas incluso a los administradores del repositorio.
Con estas reglas activadas, la única forma de fusionar cambios es mediante una solicitud de incorporación de cambios con todos los jobs de CI en verde, exactamente la red de seguridad que necesita.
Depuración local de pasos de CI fallidos
Cuando una ejecución de CI falla, la forma más rápida de corregirla es reproducir el fallo localmente antes de enviar otro commit. Hay dos técnicas:
- Ejecute los comandos exactos del paso fallido en su terminal: CI ejecuta un shell normal, por lo que los comandos se pueden reproducir copiándolos y pegándolos.
- Use
act: una herramienta que ejecuta workflows de GitHub Actions localmente dentro de Docker y ofrece la coincidencia más cercana posible con el entorno del ejecutor alojado.
Una causa habitual de fallos que solo aparecen en CI es la discrepancia entre versiones de herramientas en su Mac (por ejemplo, find de BSD en macOS frente a find de GNU en Ubuntu). Pruebe siempre con opciones --posix o use act para ejecutar localmente la imagen de 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/Comprobación de conocimientos: conceptos de la canalización de CI
Compruebe su comprensión de la integración de ShellCheck y Bats en GitHub Actions.
Recapitulación: CI de shell con ShellCheck y Bats
En esta lección ha creado una canalización de CI completa para proyectos de Bash mediante GitHub Actions. Estos son los temas cubiertos:
- Conceptos básicos de GitHub Actions — el YAML del workflow se encuentra en
.github/workflows/, se activa mediante push y pull_request, y ejecuta jobs en ejecutoresubuntu-latest. - ShellCheck — viene preinstalado en los ejecutores de Ubuntu; use
findpara descubrir scripts y--severity=warning -xcomo control práctico de lint. - Bats mediante un submódulo — fije bats-core y sus auxiliares como submódulos de Git; restáurelos en CI con
submodules: recursiveen la action de checkout. - Orden de los jobs — use
needs:para que las pruebas se ejecuten solo después de que el análisis haya terminado correctamente, manteniendo una respuesta rápida y evitando desperdiciar recursos de cómputo. - Protección de ramas — imponga comprobaciones de estado en la configuración de GitHub para que ninguna solicitud de incorporación de cambios pueda fusionarse sin un CI en verde.
- Reproducción local — copie directamente los comandos de CI en su terminal o use
actpara depurar fallos sin commits adicionales.
Con esta canalización implementada, cada cambio en el shell se valida automáticamente antes de llegar a su rama principal.
Preguntas frecuentes
¿La lección «Ejecución de pruebas de shell en pipelines de CI» es gratis?
Sí — el texto completo de «Ejecución de pruebas de shell en pipelines de CI» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Linux Command Line & Bash Scripting Mastery, actualiza a CoddyKit PRO. El curso de Linux Command Line & Bash Scripting Mastery incluye 4 lecciones en total.
¿Qué aprenderé en «Ejecución de pruebas de shell en pipelines de CI»?
Integre ShellCheck y Bats en GitHub Actions para exigir comprobaciones correctas en cada cambio del shell. Practicas Linux Command Line & Bash Scripting Mastery con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Linux Command Line & Bash Scripting Mastery?
No se requiere experiencia previa. Linux Command Line & Bash Scripting Mastery en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.
¿Cuánto tiempo toma la lección «Ejecución de pruebas de shell en pipelines de CI»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Linux Command Line & Bash Scripting Mastery?
Sí. Cada lección de Linux Command Line & Bash Scripting Mastery incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Pruebas unitarias de funciones con Bats-core
- Simulación de comandos y sustitución de herramientas externas
- Fixtures, entornos temporales y cobertura
- Ejecución de pruebas de shell en pipelines de CI