Danger System + Github Actions: Guardianes del código en Flutter

Carlos Daniel
5 min readApr 17, 2024
Photo by Denys Nevozhai on Unsplash

Si te interesa el contenido en inglés, acá puedes encontrarlo.

Podrías estarte preguntando: y qué es el Danger System?, y la respuesta es sencilla: Un “Danger System” es como un vigilante automático para nuestro código en Ruby, JS, Kotlin y otros frameworks/lenguajes. Aunque en este escrito nos enfocaremos en Flutter (gracias a Ruby).

Imagina que cada vez que alguien envía un cambio al código (un “pull request”), Danger salta a la acción para revisarlo a fondo.

¿… y qué busca Danger?

En particular 3 elementos, aunque podríamos extenderlos o reducirlos, pero me gusta apuntar a estos:

  • Errores de estilo: Danger se asegura de que tu código siga las mejores prácticas de Flutter, como la indentación correcta, el uso de nombres de variables descriptivos y la organización del código clara. Entonces, si tenemos un documento con el Code Style, podemos referenciarlo.
  • Problemas de linting: Danger detecta errores comunes de programación que podrían causar problemas en el futuro, como variables no utilizadas, código redundante o excepciones no manejadas.
  • Pruebas unitarias incompletas: Danger verifica que cada parte del código esté cubierta por pruebas unitarias, lo que garantiza que el código funcione como se espera y sea resistente a cambios futuros.

¿Qué pasa si Danger encuentra algo?

Danger simplemente publica un comentario en el pull request, explicando qué encontró y cómo solucionarlo. De esta manera, todos en el equipo pueden aprender de los errores y mejorar la calidad del código en conjunto.

¿Qué necesito para configurar el Danger System para mi app?

Para comenzar con Danger en una aplicación Flutter, primero necesitamos instalar Danger en tu entorno de desarrollo.

Aquí están los pasos generales que debes seguir:

  1. Actualizar Ruby: si estamos en Mac, tenemos por defecto instalado Ruby, pero podemos actualizar su versión desde brew y actualizar nuestro PATH con este nuevo install.
  2. Instalar Bundler: Bundler es una herramienta para gestionar dependencias en proyectos Ruby. Podemos hacerlo ejecutando gem install bundler en la terminal.
  3. Crear un Gemfile: En la raíz delproyecto Flutter, crea un archivo llamado Gemfile. Personalmente, como trabajo con Github, me gusta crear un folder .github y allí crear el Gemfile.
  4. Especificar las dependencias de Danger: Dentro del Gemfile, definir las dependencias de gemas necesarias para Danger. Por ejemplo:
source 'https://rubygems.org'

gem 'danger', '9.2.0'

Para instalar estas dependencias, vamos a la ubicación del Gemfile y ejecutamos: bundle install Para confirmar la instalación, vemos que se generó un archivo Gemfile.lock que contiene las gemas y versiones exactas que Bundle ha instalado.

Luego continuamos con:

  1. Crear un archivo Dangerfile: Este archivo contiene las reglas que Danger utilizará para revisar el código. Pueden definirse aquí las reglas de estilo de código, requerimientos para la revisión de pull requests, y otros controles automatizados. Esté iría igualmente en el folder .github
    No es más que un archivo de Ruby en el cual definimos qué queremos chequear.
def warn_lint
system("flutter analyze --no-pub --no-fatal-infos --no-fatal-warnings > flutter_analyze_report.txt")

flutter_lint.report_path = "flutter_analyze_report.txt"
flutter_lint.lint(inline_mode: false)
end

def warn_code_style
# check_format.sh runs "dart format" over our codebase
result = system("bash scripts/check_format.sh")
is_code_style_good = result == true

link = "<<Code Style Doc. Link>>"

if is_code_style_good
code_style = "The code style does follow [Code Style](#{link})"

messages = [
"#{code_style}, Great!, a brilliant PR!",
]

message messages.sample
else
code_style = "Your code style **does not** follow [Code Style](#{link})"

messages = [
"#{code_style}, it is possible that someone will not sleep fixing a bug you introduced in this PR, check the format.",
]

fail messages.sample
end
end

begin
warn_lint
rescue => e
warn "Something bad has happened while running the linter checker :( #{e}"
end

begin
warn_code_style
rescue => e
warn "Something bad happened while running the code style checker :( #{e}"
end

Acá usamos en el warn_lint el paquete danger-flutter_lint que está disponible en Github y que debemos agregar en el Gemfile

source 'https://rubygems.org'

gem 'danger', '9.2.0'
gem 'danger-flutter_lint'

¿Cómo integrarlo al CI?

Hasta ahora solamente tenemos configuraciones que “no hacen nada” aún, pero que serán llamadas desde un flujo de Github Actions de la siguiente manera:

  1. Debemos crear un archivo de workflow llamadodanger.yml. Este archivo lo ubicamos en el folder de .github/workflows y será la base para las ejecuciones del Danger en el CI.
  2. Definir el workflow en el archivo YML: En el archivo danger.yml, definimos un workflow que instale Ruby, las gemas necesarias y ejecute Danger. Aquí, un ejemplo básico de cómo podría lucir:
name: Run Danger

on: [pull_request]

jobs:
run-danger:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
working-directory: .github/
ruby-version: '3.3.0'
bundler: '2.5.4'

- name: Install dependencies
run: |
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3

- name: Run Danger
run: bundle exec danger

El código anterior quiere decir:

  • on: [pull_request]: Significa que el workflow se disparará cada vez que se cree un pull request en el repositorio.
  • runs-on: ubuntu-latest: Especifica que el job se ejecutará en un runner de Ubuntu.
  • steps: Define los pasos que se realizarán como parte de este job.
  • Checkout code: Usa la acción actions/checkout@v3 para clonar nuestro repositorio.
  • Set up Ruby: Usa la acción ruby/setup-ruby@v1 para configurar el entorno Ruby especificado.
  • Install dependencies: Instala las gemas necesarias usando Bundler.
  • Run Danger: Ejecuta Danger con bundle exec danger.

Podemos encontrar igualmente disponibles como open source algunos jobs que ya empaquetan muchas de estas tareas, por ejemplo, podemos reemplazar los jobs de Install Dependencies y Run Danger con el job danger-action@v5:

  name: Check Lint and Code Style
uses: MeilCli/danger-action@v5
with:
plugins_file: '.github/Gemfile'
install_path: 'vendor/bundle'
danger_file: '.github/DangerfileStyle'
danger_id: 'danger-style'
danger_version: 9.2.0
env:
DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}

En este escenario, este job hace uso del GITHUB_TOKEN que es autogenerado y al cual accedemos directamente con secrets.GITHUB_TOKEN y permite ejecutar todos los comandos requeridos. Para ello debemos dejar configurados los permisos del token en los settings del repositorio en la sección Actions > General.

Ahora, solo falta hacerle commit y push a nuestro código:

git add .github/workflows/danger.yml
git commit -m "Add Danger GitHub Actions workflow"
git push

Y así, una vez que hayamos hecho push de la configuración de GitHub Actions al repositorio, deberíamos poder ver que se ejecuta automáticamente en los pull requests. Esto puede verificarse en la pestaña “Actions” del repositorio en GitHub.

Hemos visto entonces cómo de una forma sencilla y rápida, configuramos un Danger System para nuestro codebase Flutter y lo integramos con nuestro CI, en particular Github Actions.

¿Qué te ha parecido? Lo conocías?

--

--

Carlos Daniel

Android & Flutter Developer. GDE for Android & Mobile Engineer.