Configura la integración continua


Configurar la integración continua (CI) de tu track es muy importante, ya que ayuda a detectar errores.

GitHub Actions

Los repositorios de Exercism (incluidos los repositorios de tracks) usan GitHub Actions para ejecutar su CI. GitHub Actions se basa en flujos de trabajo, que definen scripts que se ejecutan automáticamente cada vez que ocurre un evento específico (por ejemplo, hacer push de un commit). Para obtener más información sobre los flujos de trabajo de GitHub Actions, consulta la documentación sobre flujos de trabajo.

Flujos de trabajo preinstalados

Los tracks vienen con varios flujos de trabajo preinstalados, y a la mayoría de ellos no deberías hacerles cambios (se llaman flujos de trabajo compartidos). Sin embargo, hay un flujo de trabajo que sí deberías cambiar: el flujo de trabajo test.yml.

Flujo de trabajo de test

El objetivo del flujo de trabajo test.yml es verificar que los ejercicios del track estén en buen estado. El flujo de trabajo está configurado para ejecutarse automáticamente (en la terminología de GitHub Actions, se dispara) cuando se hace push a la rama main o a la rama de un pull request.

El flujo de trabajo en sí no debería hacer mucho, aparte de:

  • Hacer checkout del código (ya está implementado)
  • Instalar las dependencias (por ejemplo, instalar paquetes; opcional)
  • Instalar las herramientas (por ejemplo, instalar un SDK; opcional)
  • Ejecutar el script de verificación de ejercicios (ya está implementado)

Implementa el script de verificación de ejercicios

Como se mencionó, los ejercicios se verifican mediante un script, concretamente el script bin/verify-exercises (bash). Este script está casi terminado y hace lo siguiente:

  • Recorre todos los directorios de ejercicios
  • Para cada directorio de ejercicio, luego:
    • Copia la solución de ejemplo/ejemplar a los archivos de solución (stub) (ya está implementado)
    • Llama a la función unskip_tests, en la que puedes quitar el skip de los tests en tus archivos de test (opcional)
    • Llama a la función run_tests, en la que debes ejecutar los tests (obligatorio)

Las funciones run_tests y unskip_tests son lo único que necesitas implementar.

Quitar el skip de los tests

Si tu track admite el skip de tests, debemos asegurarnos de que ningún test quede en skip al verificar la solución de ejemplo/ejemplar de un ejercicio. En general, hay dos formas en las que los tracks admiten «quitar el skip» de los tests:

  1. Eliminar anotaciones/código/texto de los archivos de test. Por ejemplo, cambiar test.skip por test.
  2. Proporcionar una variable de entorno. Por ejemplo, establecer SKIP_TESTS=false.

Eliminar anotaciones/código/texto de los archivos de test

Si el skip de tests se basa en archivos (la primera opción mencionada arriba), edita la función unskip_tests para modificar los archivos de test (el código existente ya se encarga de recorrer los archivos de test).

Note

La función unskip_test se ejecuta sobre una copia del directorio de un ejercicio, así que siéntete libre de modificar los archivos como mejor te parezca.

Ejemplo

El archivo bin/verify-exercises del track de Arturo usa sed para quitar el skip de los tests dentro de los archivos de test:

unskip_tests() {
    jq -r '.files.test[]' .meta/config.json | while read -r test_file; do
        sed -i 's/test.skip/test/g' "${test_file}"
    done
}

Proporcionar una variable de entorno

Caution

Si quitar el skip de los tests requiere que se establezca una variable de entorno, asegúrate de que se establezca en la función run_tests.

Ejecutar los tests

La función run_tests se encarga de ejecutar los tests de un ejercicio. Cuando se llama a la función, los archivos de ejemplo/ejemplar ya se habrán copiado a los archivos de solución (stub), así que solo necesitas llamar al comando correcto para ejecutar los tests.

La función debe devolver cero como código de salida si todos los tests pasan; de lo contrario, debe devolver un código de salida distinto de cero.

Note

La función run_tests se ejecuta sobre una copia del directorio de un ejercicio, así que siéntete libre de modificar los archivos como mejor te parezca.

Opción 1: usar las herramientas del lenguaje

La opción predeterminada para el script de verificación de ejercicios es usar las herramientas del lenguaje (SDK/binario/etc.), que es lo que usan la mayoría de los tracks. Cada track tendrá su propia forma de ejecutar los tests, pero por lo general es un solo comando.

Ejemplo

El archivo bin/verify-exercises del track de Arturo modifica la función run_tests para simplemente llamar al comando arturo sobre el archivo de test:

run_tests() {
    arturo tester.art
}

Opción 2: usar la imagen Docker del test runner

La segunda opción es verificar los ejercicios ejecutando el test runner del track. Esto, por supuesto, depende de que el track tenga un test runner que funcione.

Si tu track todavía no tiene un test runner, puedes hacer una de estas dos cosas:

  • crear un test runner que funcione, o
  • usar la opción 1 y usar directamente las herramientas del lenguaje

Hay que hacer las siguientes modificaciones en el script bin/verify-exercises file predeterminado:

  1. Verificar que el comando docker esté disponible
  2. Hacer pull (descargar) la imagen Docker del test runner
  3. Usar docker run para ejecutar la imagen Docker del test runner en cada ejercicio
  4. Usar jq para verificar que el archivo results.json que devuelve el contenedor de Docker indique que todos los tests pasaron
  5. Eliminar la función unskip_test y la llamada a esa función
Note

El principal beneficio de este enfoque es que imita mejor cómo se ejecutan los tests en producción (en el sitio web). Con este enfoque, es menos probable que falle en producción algo que pasó en CI. La desventaja de este enfoque es que suele ser más lento, debido a que hay que hacer pull de la imagen Docker y al overhead de Docker.

Ejemplo

El archivo bin/verify-exercises file del track de Unison agrega la comprobación de que el comando docker también esté instalado:

required_tool docker

Luego, hace pull de la imagen del test runner del track:

docker pull exercism/unison-test-runner

Después modifica la función run_tests para usar docker run y ejecutar el test runner sobre el ejercicio actual (que está en el directorio de trabajo), seguido de un comando jq para comprobar que el estado sea el correcto:

run_tests() {
    local slug

    slug="${1}"

    docker run \
        --rm \
        --network none \
        --mount type=bind,src="${PWD}",dst=/solution \
        --mount type=bind,src="${PWD}",dst=/output \
        --tmpfs /tmp:rw \
        exercism/unison-test-runner "${slug}" "/solution" "/output"
    jq -e '.status == "pass"' "${PWD}/results.json" >/dev/null 2>&1
}

Por último, tenemos que modificar la forma en que se llama al comando run_tests, ya que ahora requiere el slug:

run_tests "${slug}"

Implementa el flujo de trabajo de test

Ahora que el script verify-exercises está terminado, es momento de finalizar el flujo de trabajo test.yml. Cómo hacerlo depende de la opción que hayas elegido para implementar el script verify-exercises.

Opción 1: usar las herramientas del lenguaje

Si el script verify-exercises usa directamente las herramientas del lenguaje, el flujo de trabajo de test tendrá que instalar:

  • Las dependencias de las herramientas del lenguaje, como openssh o un compilador de C/C++.
  • Las herramientas del lenguaje, como un SDK o un binario. Si la instalación de las herramientas del lenguaje no agrega el binario o los binarios instalados al path, asegúrate de agregarlo al path del sistema de GitHub Actions.

Una vez hecho esto, verify-exercises debería funcionar como se espera, ¡y habrás configurado la CI con éxito!

Para ver un ejemplo, consulta el flujo de trabajo test.yml del track de Arturo:

name: Test

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  workflow_dispatch:

jobs:
  ci:
    runs-on: ubuntu-22.04

    steps:
      - name: Checkout repository
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332

      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install libgtk-3-dev libwebkit2gtk-4.0-dev libmpfr-dev

      - name: Install Arturo
        run: bin/install-arturo
        env:
          GH_TOKEN: ${{ github.token }}

      - name: Verify all exercises
        run: bin/verify-exercises

Opción 2: usar la imagen Docker del test runner

La segunda opción es verificar los ejercicios ejecutando el test runner del track. Esta opción requiere que se cumplan dos cosas:

  1. El track tiene un test runner que funciona
  2. El script verify-exercises usa la imagen Docker del test runner para ejecutar los tests de un ejercicio

Si tu track todavía no tiene un test runner, puedes hacer una de estas dos cosas:

  • crear un test runner que funcione, o
  • usar la opción 1 y usar directamente las herramientas del lenguaje

Este enfoque tiene un par de ventajas:

  1. No necesitas instalar dependencias ni herramientas dentro del flujo de trabajo de test (ya que se habrán instalado dentro de la imagen Docker)
  2. El enfoque imita mejor cómo se ejecutan los tests en producción (en el sitio web), lo que reduce la probabilidad de problemas en producción.

La principal desventaja es que probablemente sea más lento, debido a que hay que hacer pull de la imagen Docker y al overhead de Docker.

Hay un par de formas de hacer pull de la imagen Docker del test runner:

  1. Descargar la imagen dentro del archivo verify-exercises. Este es el enfoque que usa el track de Unison.
  2. Descargar la imagen dentro del flujo de trabajo. Este es el enfoque que usa el track de Standard ML.
  3. Compilar la imagen dentro del flujo de trabajo. Este es el enfoque que usa el track de 8th.

Entonces, ¿qué enfoque usar? Recomendamos implementar al menos la opción número 1, para que el script verify-exercises sea autónomo. Si tu imagen es especialmente grande, puede ser útil implementar también la opción 3, que guardará la imagen Docker compilada en la caché de GitHub Actions. Las ejecuciones posteriores pueden entonces leer la imagen Docker de la caché en lugar de descargarla, lo que puede ser mejor para el rendimiento (mídelo para asegurarte).

Opción 3: ejecutar el script de verificación de ejercicios dentro de la imagen Docker del test runner

Una tercera opción alternativa es un híbrido de las dos anteriores. Aquí también usamos la imagen Docker del test runner, solo que esta vez ejecutamos el script verify-exercises dentro de esa imagen Docker. Para habilitar esta opción, tenemos que establecer el contenedor del flujo de trabajo como el test runner:

container:
  image: exercism/vimscript-test-runner

Después podemos omitir los pasos de instalación de dependencias y herramientas (ya que se habrán instalado dentro de la imagen Docker del test runner) y pasar a ejecutar el script bin/verify-exercises file.

Ejemplo

El flujo de trabajo test.yml del track de vimscript usa esta opción:

name: Verify Exercises

on:
  push:
    branches: [main]
  pull_request:
  workflow_dispatch:

jobs:
  ci:
    runs-on: ubuntu-24.04
    container:
      image: exercism/vimscript-test-runner

    steps:
      - name: Checkout repository
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332

      - name: Verify all exercises
        run: bin/verify-exercises