Saltar al contenido principal
Version: 0.59.0-rc.2

Sistema de Compilación

Lana utiliza Nix Flakes para compilaciones reproducibles y Cachix para almacenamiento en caché binario. Si no estás familiarizado con Nix, la versión corta es: es un sistema de compilación que garantiza que las mismas entradas siempre produzcan la misma salida, sin importar en qué máquina estés compilando. Esto elimina el problema de "funciona en mi máquina" tanto para desarrollo como para CI.

Esta página cubre cómo funcionan las compilaciones localmente, cómo CI utiliza la caché de Nix y cómo se producen las imágenes de Docker para los lanzamientos.

Estructura del Nix Flake

Todo comienza con el archivo flake.nix en la raíz del repositorio. Define todos los objetivos de compilación, entornos de desarrollo y puntos de entrada de CI:

flake.nix
├── packages
│ ├── lana-cli # El binario principal del servidor/CLI (compilación de lanzamiento, optimizada)
│ ├── lana-cli-debug # Una compilación de depuración (más rápida de compilar, útil en desarrollo)
│ └── lana-deps # Solo el árbol de dependencias de Rust, precompilado (usado para almacenamiento en caché)
├── devShells
│ └── default # El entorno de desarrollo con todas las herramientas
├── checks
│ └── flake-check # Valida el flake mismo
└── apps
├── nextest # Ejecuta la suite de pruebas de Rust mediante cargo nextest
├── bats # Ejecuta las pruebas de extremo a extremo de BATS
└── simulation # Ejecuta simulaciones de escenarios de instalaciones

La sección packages es lo que compila CI. La sección apps proporciona puntos de entrada convenientes para ejecutar pruebas — CI llama a nix run .#nextest en lugar de manipular cargo nextest manualmente, porque la aplicación Nix asegura que todas las variables de entorno y dependencias correctas estén configuradas.

El paquete lana-deps merece mención especial: precompila solo el árbol de dependencias de Rust (todos los crates en Cargo.lock) sin ningún código propio de lana. Esta es una optimización de almacenamiento en caché — dado que las dependencias cambian con mucha menos frecuencia que el código de la aplicación, compilarlas por separado significa que pueden almacenarse en caché y reutilizarse en muchas compilaciones.

Entorno de Desarrollo

Cuando ejecutas nix develop, Nix configura un entorno con todas las herramientas que necesitas para el desarrollo:

nix develop

Esto te proporciona: el toolchain estable de Rust, Node 20, pnpm 10, Python 3.13, un cliente de PostgreSQL, sqlx-cli y todas las demás utilidades que usa el proyecto. No necesitas instalar ninguna de estas herramientas globalmente en tu máquina — Nix las gestiona por ti y no interfieren con otros proyectos.

Si has configurado Cachix (ver más abajo), este comando es casi instantáneo porque el entorno precompilado se descarga desde la caché en lugar de compilarse en tu máquina.

Compilar el Binario de Producción

Hay dos formas de compilar el binario lana-cli:


# Compilación de producción — optimizada, usada en CI para imágenes Docker

nix build --impure .#lana-cli-release

# Compilación de depuración — más rápida de compilar, incluye símbolos de depuración

nix build .#lana-cli-debug

La compilación de release utiliza la bandera --impure porque lee la variable de entorno RELEASE_BUILD_VERSION que se integra en el binario en tiempo de compilación. Esto lo establece el pipeline de CI para que la aplicación en ejecución sepa qué versión es. En desarrollo local normalmente usarías la compilación de debug, que omite esto y compila mucho más rápido.

Imágenes Docker

La imagen Docker lana-bank está construida completamente por Nix usando dockerTools.buildImage — sin Dockerfile ni BuildKit involucrados. La imagen es mínima, equivalente a scratch, y contiene únicamente el binario enlazado estáticamente, /tmp y /etc/passwd. Todas las raíces CA de TLS y los datos de zona horaria están compilados en el binario en tiempo de construcción.

Debido a que la imagen es una derivación pura de Nix, es completamente determinista: las mismas entradas siempre producen un digest de imagen idéntico independientemente del host de construcción (Mac, CI de Linux, etc.). Puedes construirla localmente con:

make build-docker-image

La imagen de lanzamiento

Cada lanzamiento produce una imagen de Docker, enviada a Google Artifact Registry:

ImagenQué contieneRegistro
lana-bankEl binario principal del servidor lana-cli, el runtime de dbt, el proyecto dbt de Postgres y los assets empaquetados del panel de administracióngcr.io/galoyorg/lana-bank

Metadatos de compilación

La versión de la aplicación se integra en el binario en tiempo de compilación de Rust mediante RELEASE_BUILD_VERSION. Las imágenes de Docker en sí mismas no contienen metadatos de tiempo de compilación — esto las mantiene reproducibles (el mismo binario + la misma imagen base siempre produce el mismo digest de imagen).


Almacenamiento en Caché Binario de Cachix

Este es el problema que Cachix resuelve: Las compilaciones de Nix son perfectamente reproducibles, pero son lentas cuando estás compilando desde cero. Compilar la cadena de herramientas de Rust, todas las dependencias y el binario de la aplicación puede llevar mucho tiempo. Si cada ejecución de CI y cada desarrollador tuviera que hacer esto desde cero, sería doloroso.

Cachix es una caché binaria para Nix. Cuando alguien compila una derivación de Nix y la envía a Cachix, todos los demás que necesiten la misma derivación pueden descargar el resultado precompilado en lugar de compilarlo ellos mismos. Dado que las derivaciones de Nix están direccionadas por contenido (la salida está determinada completamente por las entradas), esto es seguro — siempre obtendrás exactamente el mismo resultado ya sea que compiles localmente o descargues desde la caché.

Detalles de la caché

Nombre de cachégaloymoney
URLhttps://galoymoney.cachix.org
Quién escribe en ellaTrabajos de CI configurados con un token de escritura de Cachix
Quién lee de ellaFlujos de trabajo de GitHub Actions, trabajos de Concourse y cualquier desarrollador que ejecute cachix use galoymoney

El diseño de almacenamiento en caché de dos sistemas

Existe una separación intencional entre quién escribe en la caché y quién lee de ella:

  • Los constructores de CI realizan el trabajo pesado. Trabajos de CI seleccionados construyen derivaciones de Nix relevantes y las envían a Cachix.

  • GitHub Actions es el consumidor. Cada flujo de trabajo configura Cachix con skipPush: true, lo que significa que descargará binarios preconstruidos de la caché pero nunca cargará nada. Los ejecutores de GitHub Actions son efímeros, y tener muchos ejecutores paralelos enviando a la caché crearía cargas redundantes y posibles condiciones de carrera.

Este diseño mantiene la caché limpia y garantiza que las compilaciones sean rápidas en ambos sistemas de CI.

Cómo funciona el llenado de caché

Los pasos de compilación de CI que usan cachix watch-exec cargan derivaciones a medida que se construyen, por lo que trabajos posteriores pueden obtener esos artefactos en lugar de recompilar. Esto mantiene el calentamiento de caché incremental y reduce la compilación repetida entre pipelines y desarrollo local.

Usando Cachix como desarrollador

Puedes acelerar tus comandos locales nix develop y nix build configurando Cachix:


# Configuración única — añade galoymoney como caché binaria

cachix use galoymoney

# Ahora esto descarga herramientas precompiladas en lugar de compilarlas

nix develop

Después de esto, Nix verificará la caché galoymoney antes de compilar cualquier cosa. Si la derivación que necesitas ya está allí, se descarga en segundos en lugar de compilarse en minutos.

Qué sucede cuando la caché está vacía

Si la caché no tiene lo que necesitas (un "fallo de caché"), Nix simplemente lo compila localmente. Esto es más lento pero siempre funciona — la caché es una optimización de rendimiento, no un requisito. El script de utilidad wait-cachix-paths está disponible en CI para casos donde un trabajo necesita esperar a que la caché sea poblada por un pipeline paralelo antes de continuar.


Modo Offline de SQLx

Lana utiliza SQLx para consultas de base de datos, que proporciona verificación SQL en tiempo de compilación — el compilador de Rust verifica tus consultas SQL contra el esquema real de la base de datos durante la compilación. Esto es excelente para detectar errores, pero significa que necesitas una base de datos en ejecución para compilar el código.

Eso es un problema en CI, donde no hay ninguna base de datos disponible durante el paso de compilación. La solución es el modo offline de SQLx: guardas los metadatos de las consultas en un directorio .sqlx/ (incluido en git), y las compilaciones de CI usan esos metadatos en caché en lugar de conectarse a una base de datos real.


# Cuando tengas una base de datos ejecutándose localmente, regenera los metadatos

make sqlx-prepare

# En CI o al compilar sin una base de datos

SQLX_OFFLINE=true cargo build

Si cambias una consulta SQL y olvidas ejecutar make sqlx-prepare, la compilación en CI fallará porque los metadatos sin conexión no coincidirán con la consulta real. Esto es intencional: mantiene los metadatos sincronizados con el código.

Objetivos Comunes del Makefile

ObjetivoQué hace
make check-code-rustCompila todo el código Rust con SQLX_OFFLINE=true para verificar que se compile
make check-code-appsAnaliza, verifica tipos y compila las aplicaciones frontend
make sqlx-prepareRegenera los metadatos de consultas sin conexión .sqlx (requiere una base de datos en ejecución)
make sdlRegenera los archivos de esquema GraphQL a partir del código Rust
make start-depsInicia las dependencias de desarrollo locales (PostgreSQL, Keycloak, etc.)
make reset-depsDetiene las dependencias, limpia las bases de datos y reinicia todo desde cero