Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

FND-004: Infraestructura de ingeniería: pipeline de CI/CD y automatización de releases

Compatibilidad con v0.7.0 → v1.0.0 · Tipo: Arquitectura · Rev. 8

Referencia canónica · Ratificada por el equipo · Rev. 8 Discusión original del RFC: #5579


Una nota para el equipo antes de que lean esto.

Este documento trata sobre el andamiaje que rodea al código: la automatización que lo compila, lo prueba, lo audita y lo publica. Ese andamiaje es invisible cuando funciona bien y doloroso cuando no. La mayoría de los equipos no piensan en él hasta que se vuelve doloroso, y para entonces ya se ha convertido en algo que nadie comprende del todo. Este RFC es un intento de adelantarse a eso. Si nunca has reflexionado a fondo sobre CI/CD, este es un buen punto de partida. Si ya lo has hecho, reconocerás los patrones. En cualquier caso, el objetivo es el mismo: un pipeline que dé confianza al equipo sin convertirse en un estorbo.


Tabla de contenidos

  1. Context: Las tuberías son arquitectura
  2. Evaluación honesta: dónde estamos hoy
  3. El diseño de la canalización objetivo
  4. Escaneo de seguridad como un ciclo de vida
  5. Automatización de lanzamientos alineada con el modelo de distribución
  6. Estándares que deberíamos adoptar
  7. Hoja de ruta por fases
  8. Lo que esto significa para los colaboradores

Historial de revisiones

RevisarFechaResumen
12026-04-09Borrador inicial
22026-06-04Se reemplazó el bloqueo secuencial de formato y lint por un bloqueo únicamente de formato, seguido de trabajos paralelos obligatorios de Rust (#7111)
32026-06-10Ejecuciones de confianza de master necesarias para inicializar las cachés utilizadas por las solicitudes de extracción (#7355)
42026-06-21Se cambió el destino de compilación y lanzamiento del complemento de wasm32-wasip1 a wasm32-wasip2 (#8061)
52026-06-30Se eliminaron el artefacto de escritorio y las obligaciones de su canalización de versiones (#8544)
62026-07-04Se restauraron el artefacto de escritorio y sus obligaciones de la canalización de lanzamiento (#8565)
72026-08-07Se reemplazaron las instrucciones de actions/attest-build-provenance por la atestación directa de artefactos de actions/attest (#9717)
82026-08-20Se eliminó la clase retirada de bibliotecas de hardware de la guía de lanzamientos independientes después de que aardvark-sys y zeroclaw-robot-kit abandonaran el espacio de trabajo (#10152)

1. Contexto: Las tuberías son arquitectura

El RFC de arquitectura (#5574) estableció un principio: las dependencias fluyen hacia adentro, y la estructura es aplicada por el compilador. El mismo principio se aplica al pipeline que rodea el código. Un pipeline no es solo automatización: es un conjunto de decisiones arquitectónicas sobre en qué confías, qué verificas, cuándo lo verificas y cómo lo entregas.

Esas decisiones tienen consecuencias. Una canalización diseñada para un monolito se resistirá activamente a un microkernel. Una puerta de seguridad sin un proceso de triaje bloqueará todo o será eludida. Un flujo de trabajo de lanzamiento basado en un único binario no sobrevivirá a un modelo de distribución con cinco tipos de artefactos. Estos no son problemas de configuración. Son problemas de diseño y merecen el mismo tratamiento intencional que la arquitectura del código.

El pipeline actual creció de forma reactiva, de la misma manera que loop_.rs creció hasta las 9,500 líneas. Nadie eligió el estado actual. Se fue acumulando. El PR #5559, el primer paso importante de la transición al microkernel, expuso varios lugares donde las suposiciones del pipeline ya no se cumplen. Esa es una señal útil. Significa que ahora es exactamente el momento adecuado para detenerse, evaluar y diseñar de forma intencional.

Esta RFC hace por la canalización lo que la RFC de arquitectura hace por la base de código: nombra lo que existe, identifica los problemas estructurales y propone un camino hacia adelante que sea coherente con la dirección del proyecto.


2. Evaluación honesta: dónde estamos hoy

Esta sección no es una crítica. Es un diagnóstico. El pipeline actual refleja las decisiones que tenían sentido en ese momento. El objetivo es comprenderlo lo suficientemente bien para mejorarlo.

2.1 Dos flujos de trabajo realizando el mismo trabajo

El repositorio actualmente tiene dos flujos de trabajo separados que se ejecutan en las solicitudes de extracción contra master:

  • checks-on-pr.yml, denominado “Quality Gate”
  • ci-run.yml, denominado “CI”

Tanto Lint, Build, Test como Security se ejecutan de forma independiente en cada PR. Esto significa que cada PR desencadena dos ejecuciones completas del pipeline en paralelo. Para un monolito con una única unidad de compilación, esto era costoso pero manejable. Para un espacio de trabajo con múltiples crates, duplica un presupuesto de CI ya significativo sin aportar señal adicional.

La duplicación tiene un costo más sutil que los minutos de cómputo: cuando una verificación falla en un flujo de trabajo pero no en el otro, los colaboradores no saben en cuál resultado confiar. Cuando se necesita agregar una nueva verificación, debe agregarse en dos lugares. Cuando se necesita cambiar el comportamiento, debe modificarse en dos lugares. Tener dos fuentes de verdad es el mismo problema que tener dos fuentes de verdad en el código.

2.2 Las suposiciones de binario único están integradas en todas partes

La automatización de releases, release-stable-manual.yml, release-beta-on-push.yml, publish-crates.yml, pub-aur.yml, pub-homebrew-core.yml, pub-scoop.yml, discord-release.yml, tweet-release.yml, fue diseñada en torno a la suposición de que un release es un solo binario. Lo compilas, lo firmas, lo publicas en los gestores de paquetes y lo anuncias.

El RFC de arquitectura define un modelo de distribución con cinco tipos de artefactos distintos: el binario del kernel (múltiples plataformas de destino), el binario del kernel para variantes de hardware, el binario del gateway, los archivos de plugins WASM y el instalador de escritorio de Tauri. Ninguno de los flujos de trabajo de lanzamiento actuales contempla esta estructura. Cuando la transición de arquitectura llegue a la Fase 3 y la Fase 4, cada uno de estos flujos de trabajo tendrá que cambiar, a menos que se rediseñen ahora teniendo en cuenta ese modelo.

2.3 Análisis de seguridad sin un ciclo de vida

El trabajo de seguridad ejecuta cargo audit como una verificación estricta. Si hay alguna advertencia presente en el árbol de dependencias, la verificación falla y la PR no puede fusionarse. La intención es correcta. La implementación tiene un problema estructural.

cargo audit informa de todas las advertencias en el árbol de dependencias: vulnerabilidades activas, crates sin mantenimiento y avisos informativos. No distingue entre:

  • Una vulnerabilidad crítica en un paquete que el proyecto llama activamente
  • Una vulnerabilidad en una dependencia transitiva a tres niveles de profundidad en una función opcional
  • Un aviso de “no mantenido” para una dependencia indirecta de un crate a través de una biblioteca de terceros que no se puede controlar
  • Un aviso preexistente que estaba presente antes de que se abriera este PR

Cuando todas estas situaciones provocan un fallo crítico, el filtro se convierte en ruido. La respuesta realista ante el ruido es reducir el filtro, ignorar los fallos o suprimir las comprobaciones. Las tres de estas respuestas hacen que el proyecto sea menos seguro, no más. Un filtro de seguridad que no se pueda mantener no se mantendrá.

El PR #5559 generó doce avisos RUSTSEC-2026 simultáneamente. Sin herramientas para distinguir entre un “nuevo aviso introducido por este PR” y un “aviso preexistente presente en master”, el autor del PR y los revisores no pueden saber si este PR empeoró la postura de seguridad.

2.4 El script de lint estricto de delta

ci-run.yml incluye un job que ejecuta scripts/ci/rust_strict_delta_gate.sh, un script personalizado que compara la salida de clippy con el SHA base del PR. El concepto es sólido: quieres saber si este PR introdujo nuevas advertencias, no solo si existen advertencias en el código base. La implementación funciona bien para PRs pequeños y enfocados contra un crate monolítico.

Un PR que mueve 260,000 líneas de código a través de 10 crates nuevos, tocando cientos de archivos, coloca este script en un territorio para el que no fue diseñado. La superficie de archivos modificados es demasiado grande para que una comparación incremental produzca una señal significativa. El script necesita entender la estructura del workspace: específicamente, que un cambio en un archivo en crates/zeroclaw-channels/ debe evaluarse en el contexto de ese crate, no de la raíz.

2.5 Sin almacenamiento en caché ni ámbito consciente del espacio de trabajo

La configuración actual del caché de Rust (Swatinem/rust-cache) es adecuada para un solo crate. Para un espacio de trabajo con múltiples crates, la eficacia del caché depende de comprender qué crates han cambiado y qué artefactos compilados pueden reutilizarse. Sin un alcance explícito del espacio de trabajo, un cambio en cualquier crate puede invalidar los cachés de los que dependen otros crates, provocando una recompilación completa en cada PR.

Más significativamente, no hay un mecanismo para ejecutar CI solo contra los crates afectados por un cambio dado. Un PR que corrige un error tipográfico en zeroclaw-tool-call-parser no necesita reconstruir y volver a probar la puerta de enlace. A medida que el espacio de trabajo crece hacia el modelo de más de 30 crates que la RFC de arquitectura prevé, el costo de ejecutar el pipeline completo en cada PR se convierte en un obstáculo significativo para la contribución.

2.6 Fijar las Acciones es Bueno: Pero No Está Documentado

Los flujos de trabajo existentes sí fijan las acciones a SHAs de commit completos, lo cual es una práctica de seguridad correcta y digna de reconocimiento. Pero no hay una política documentada que explique por qué, ni un proceso para revisar cuándo deben actualizarse esos SHAs, ni automatización para mantenerlos al día. El buen comportamiento sin una política es frágil: el próximo colaborador que añada un paso al flujo de trabajo puede no saber por qué es importante fijar los SHAs y usará una etiqueta mutable en su lugar.


3. El diseño de la tubería de destino

3.1 Un único pipeline, una única fuente de verdad

Los dos flujos de trabajo paralelos deberían consolidarse en un único pipeline bien estructurado. La distinción entre “Quality Gate” y “CI” no es significativa para los contribuidores: ambos son verificaciones que un PR debe pasar. La consolidación crea un único lugar donde encontrar los resultados de las verificaciones, un único lugar que actualizar cuando cambia el comportamiento y un único lugar donde documentar qué hace cada verificación y por qué.

El pipeline consolidado sigue una estructura por etapas donde primero se ejecuta una verificación de formato muy económica, y luego los trabajos intensivos en Rust se distribuyen en paralelo. Lint sigue siendo obligatorio, pero no debería retener innecesariamente el precalentamiento de la caché de compilación y pruebas cuando el objetivo es acortar la ruta crítica en verde:

Stage 1: Format (cheap serial gate)
  └── cargo fmt --check

Post-format quality gate (parallel, required)
  └── cargo clippy --workspace --all-targets -- -D warnings
  └── Docs quality gate

Post-format Build + Check (parallel, 5–15 min)
  └── Build matrix (Linux x86_64, macOS ARM, Windows)
  └── cargo check --features ci-all
  └── cargo check --no-default-features (kernel profile)
  └── cargo check --target i686 (32-bit)

Post-format Test (parallel, 10–30 min)
  └── cargo nextest run --workspace

Post-format Security (parallel)
  └── cargo deny check (licenses, sources, advisories)
  └── Advisory triage gate (see §4)

Required Gate
  └── Composite status — branch protection requires only this job

Los trabajos posteriores al formateo se ejecutan en paralelo después de que pasa el formateo. Esto significa que un error de formateo falla rápido sin gastar cómputo en una compilación que será descartada, mientras que clippy, build, test y security pueden avanzar juntos en PRs correctamente formateados. El trabajo Required Gate agrega todos los resultados de modo que la protección de ramas solo necesita rastrear un único nombre de trabajo, un patrón ya presente en ambos flujos de trabajo actuales.

3.2 Clippy consciente del espacio de trabajo

La invocación actual de clippy se ejecuta contra el conjunto de características predeterminado del crate raíz. La invocación correcta para un espacio de trabajo con múltiples crates es:

sh

cargo clippy --workspace --all-targets -- -D warnings

La opción --workspace garantiza que se analice con clippy cada crate del espacio de trabajo, no solo la raíz. La opción --all-targets incluye pruebas, puntos de referencia y ejemplos. Combinado con --features ci-all para la comprobación condicionada por características, esto proporciona una visión completa.

El concepto de lint delta estricto, que verifica si este PR introdujo nuevas advertencias en lugar de si existen advertencias en absoluto, vale la pena preservarlo. La implementación debería pasar de un script de shell que compara la salida de diff a una invocación adecuada consciente del workspace que evalúe cada crate afectado de forma independiente. Un enfoque más simple y confiable: exigir que --workspace -D warnings pase limpio en todo momento, haciendo que el concepto de delta sea implícito. Si la línea base siempre está limpia, cualquier PR que introduzca una advertencia falla. Esto elimina por completo la necesidad de un script de comparación personalizado.

3.3 Detección de crates modificados

Para un espacio de trabajo que crece hacia 30+ crates, ejecutar el conjunto completo de pruebas en cada PR, independientemente de lo que haya cambiado, es un desperdicio. El pipeline debería detectar qué crates fueron afectados por el PR y ajustar la ejecución de las pruebas en consecuencia.

El mecanismo es sencillo: compara los archivos modificados en la PR con la lista de miembros del espacio de trabajo, identifica qué crates contienen archivos modificados, amplía el conjunto para incluir todos los crates que dependen de cualquier crate modificado (impacto hacia abajo) y ejecuta las pruebas solo para ese conjunto.

PR changes: crates/zeroclaw-tool-call-parser/src/lib.rs

Affected crates:
  zeroclaw-tool-call-parser     ← directly changed
  zeroclaw-misc                 ← depends on it
  zeroclaw (root)               ← depends on it

Not affected:
  zeroclaw-channels             ← no dependency path
  zeroclaw-memory               ← no dependency path
  zeroclaw-providers            ← no dependency path

Esto se implementa utilizando cargo metadata para extraer el grafo de dependencias y un breve script para recorrerlo. El conjunto completo de pruebas sigue ejecutándose en los envíos a master y en las ramas de lanzamiento. Las PRs ejecutan el subconjunto de crates afectados.

3.4 Estrategia de Caché

Swatinem/rust-cache admite el almacenamiento en caché consciente del espacio de trabajo a través de su configuración workspaces. La clave de caché debe incluir la lista de miembros del espacio de trabajo para que la adición de un nuevo crate invalide adecuadamente sin invalidar las cachés de crates no relacionados.

- utiliza: Swatinem/rust-cache@<sha>
  con:
    espacios de trabajo: |
      . -> target
    cache-on-failure: true
    save-if: ${{ github.ref == 'refs/heads/master' }}

Debido a que los guardados de caché están limitados a refs/heads/master, el flujo de trabajo debe ejecutarse en pushes confiables a master. Los PRs leen la caché sembrada desde master pero no escriben artefactos de ramas en competencia. Esto evita la sobrecarga de la caché (cache thrashing) cuando hay múltiples PRs abiertos simultáneamente, mientras que sigue permitiendo que las ejecuciones posteriores al merge precalienten las cachés de compilación de Linux, macOS y Windows para el siguiente ciclo de revisión.


4. Escaneo de seguridad como un ciclo de vida

4.1 El problema con una puerta binaria

Un filtro de seguridad que bloquea ante cualquier aviso, sin contexto, entrena al equipo para tratar los fallos de seguridad como ruido. Eso es lo opuesto al efecto deseado. El objetivo es un filtro que sea:

  • Alta señal: los fallos significan algo real que este PR afectó
  • Accionable: el colaborador sabe qué hacer y por qué
  • Sostenible: la puerta puede mantenerse sin intervención manual constante

cargo audit por sí solo no logra esto. cargo deny sí lo hace.

4.2 cargo-deny como la herramienta de seguridad principal

cargo deny es un sucesor más capaz de cargo audit para la política de dependencias a nivel de proyecto. Aplica:

  • Advisories: base de datos RUSTSEC, con la capacidad de denegar, advertir o ignorar explícitamente avisos específicos con una justificación documentada
  • Licencias: garantiza que todas las dependencias usen licencias aceptables (importante a medida que el workspace crece y nuevos colaboradores agregan dependencias)
  • Fuentes: garantiza que las dependencias provengan únicamente de registros aprobados (crates.io, path, git con hosts específicos)
  • Duplicados: advierte cuando aparecen múltiples versiones del mismo crate en el árbol de dependencias

La capacidad clave es la sección [advisories] de deny.toml, que permite ignorados explícitos y justificados. Este enfoque transforma el escaneo de seguridad de un aprobado/fallido binario en una política documentada y auditable. Cada aviso ignorado tiene una justificación escrita y un issue de seguimiento. Los revisores pueden ver exactamente qué avisos se están suprimiendo y por qué. Cuando un aviso suprimido se agrava (se encuentra un nuevo exploit, hay una corrección disponible), el issue de seguimiento es el recordatorio.

4.3 Proceso de Triaje de Asesoría

Cuando aparece un nuevo aviso de seguridad en el árbol de dependencias, ya sea desde un PR o desde la actualización diaria de la base de datos de avisos, el proceso es:

  1. Clasificar la advertencia: ¿El crate afectado es una dependencia directa o transitiva? ¿ZeroClaw llama a la ruta de código vulnerable? ¿Hay una versión corregida disponible?
  2. Determinar la respuesta:
    • Vulnerabilidad en una dependencia directa con una solución disponible → actualiza la dependencia, no es necesario ignorarla
    • Vulnerabilidad en una dependencia transitiva con una solución disponible → fija la versión de la dependencia transitiva o espera a que la dependencia directa se actualice; abre un seguimiento del problema
    • Aviso de no mantenimiento, sin exploit activo → agregar a la lista de ignorados en deny.toml con justificación y número de seguimiento
    • Vulnerabilidad crítica sin solución → evaluar solución alternativa; puede bloquear el PR
  3. Registra la decisión en deny.toml con el ID de la advertencia, una breve justificación y un enlace al problema de seguimiento.

Este proceso significa que una PR como #5559, que muestra doce avisos preexistentes, no falla el control sin contexto. Los avisos se trian, los preexistentes se documentan, y el control informa solo sobre nuevos avisos no triados introducidos por la PR.

4.4 Escaneo diario de avisos

Los avisos de seguridad se publican de forma continua. Una solicitud de extracción (PR) que pasó el control de seguridad al fusionarse puede contener una vulnerabilidad publicada la semana siguiente. El pipeline debe incluir una ejecución programada diaria contra master que verifique la base de datos de avisos y abra un Issue en GitHub si se encuentran nuevos avisos sin triaje.

on:
  horario:
    - cron: '0 9 * * *'  # 09:00 UTC diariamente

Esto separa el ciclo de triaje de las advertencias del ciclo de fusión de las solicitudes de extracción (PR). Los colaboradores no se ven bloqueados por las advertencias que aparecieron después de que se escribió su PR. El equipo de seguridad (o quien esté en turno) gestiona la salida del análisis diario como una tarea de mantenimiento regular.


5. Automatización de lanzamientos alineada con el modelo de distribución

5.1 La discrepancia actual

La sección 4.4.2 del RFC de arquitectura define los siguientes artefactos de lanzamiento:

ArtefactoObjetivo de compilaciónPublicado en
Binario del kernel (estándar)x86_64-linux-musl, aarch64-linux-gnu, armv7-linux-gnueabihf, x86_64-darwin, aarch64-darwin, x86_64-windowsReleases de GitHub
Binario del kernel (hardware)aarch64-linux-gnu, armv7-linux-gnueabihfReleases de GitHub
Binario de la puerta de enlaceMisma matriz de plataformaReleases de GitHub
Archivos de complementos WASMwasm32-wasip2Registro de complementos
Instalador de escritoriox86_64 + aarch64, macOS/Windows/LinuxReleases de GitHub, tiendas de la plataforma

Los flujos de trabajo de lanzamiento actuales conocen exactamente uno de estos: el binario estándar. El resto aún no existe en la automatización. Esto es apropiado por ahora: el sistema de plugins aún no está completo. Pero los flujos de trabajo de lanzamiento deben diseñarse teniendo en cuenta este modelo para que no sea necesario reescribirlos a medida que se introduzca cada nuevo tipo de artefacto.

5.2 Estructura de una Pipeline de Lanzamiento

El pipeline de lanzamiento objetivo es un grafo dirigido de trabajos, no un flujo de trabajo monolítico:

version-bump (release-plz PR merged)
    │
    ├── build-kernel-standard (matrix: 6 targets)
    ├── build-kernel-hardware (matrix: 2 ARM targets + hardware flags)
    ├── build-gateway (matrix: 6 targets)
    ├── build-plugins-wasm (matrix: all plugin crates → wasm32-wasip2)
    └── build-desktop (matrix: macOS, Windows, Linux AppImage/deb)
            │
            ├── publish-github-release (attaches all kernel + gateway binaries)
            ├── publish-plugin-registry (uploads WASM files)
            ├── publish-aur (kernel binary for Arch Linux)
            ├── publish-homebrew (kernel binary for macOS)
            ├── publish-scoop (kernel binary for Windows)
            └── announce (Discord, social)

Cada trabajo de compilación es independiente y puede activarse por separado para las versiones de corrección urgente. Los trabajos de publicación dependen de que todos los trabajos de compilación relevantes se completen con éxito. El trabajo de anuncio se ejecuta al final.

Esta estructura significa que una versión solo de complementos (una nueva versión de channel-discord.wasm) puede ejecutar únicamente los trabajos build-plugins-wasm y publish-plugin-registry sin desencadenar una reconstrucción completa del núcleo. Una versión de parche del núcleo ejecuta los trabajos build-kernel-* y los trabajos de publicación posteriores sin modificar el registro de complementos.

5.3 Release-plz para la gestión de versiones consciente del espacio de trabajo

La RFC de arquitectura §4.4.1 especifica release-plz como la herramienta de automatización de lanzamientos. release-plz se integra directamente con este modelo de pipeline:

  • Al hacer un push a master, release-plz abre un “PR de lanzamiento” que actualiza la versión del espacio de trabajo, actualiza los registros de cambios a partir del historial de commits convencionales y enumera todos los crates que han cambiado desde el último lanzamiento.
  • Cuando se fusiona el PR de la versión, la canalización de la versión se activa automáticamente.
  • Los crates con version.workspace = true se actualizan conjuntamente; el crate zeroclaw-api, cuya versión se gestiona de forma independiente, se trata por separado según la política de versionado

El PR de lanzamiento actúa como un punto de control de revisión: el equipo ve exactamente qué versión se publicará y qué dice el registro de cambios antes de que se publique nada. Esto reemplaza los incrementos manuales de versión y el flujo de trabajo version-sync.yml.

5.4 Política de fijación de acciones

Los flujos de trabajo actuales ya fijan las acciones a SHAs de commit completos. Esto es correcto y debería formalizarse como una política explícita para que sobreviva a la rotación de colaboradores:

Política: Todas las referencias uses: en los archivos de flujo de trabajo deben estar vinculadas a un SHA de commit completo con un comentario de versión. No se permiten etiquetas mutables (@v4, @main, @latest). Sin excepciones.

# Correcto
- utiliza: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

# No permitido
- utiliza: actions/checkout@v4
- utiliza: actions/checkout@main

Rationale: Una etiqueta mutable es una promesa de un tercero de que el comportamiento de la acción no cambiará. Esa promesa se ha roto repetidamente en el ecosistema de GitHub Actions. Fijar un SHA significa que el flujo de trabajo se ejecutará exactamente como fue revisado, independientemente de lo que haga el autor de la acción después. Esto es especialmente importante para las acciones que tienen permisos de escritura o acceso a secretos.

El proceso de actualización: utiliza dependabot o renovate configurado para GitHub Actions para abrir PRs cuando estén disponibles nuevas versiones de SHA. El equipo revisa y fusiona esos PRs. Esto mantiene las acciones actualizadas sin necesidad de monitoreo manual.


6. Estándares que deberíamos adoptar

6.1 SLSA: Marco de Seguridad de la Cadena de Suministro

SLSA (Supply-chain Levels for Software Artifacts, pronunciado “salsa”) es un marco desarrollado por Google y adoptado en toda la industria para asegurar la cadena de suministro de software. Define cuatro niveles de integridad de compilación, desde básico hasta hermético.

Para la escala y el tamaño del equipo actuales de ZeroClaw, SLSA Nivel 2 es el objetivo adecuado:

  • Las compilaciones se ejecutan en una plataforma de CI alojada (ya es así, GitHub Actions)
  • Los scripts de compilación están controlados por versiones (ya es cierto)
  • La procedencia de la compilación se genera y se adjunta a los artefactos de la versión (el paso para agregar)

La procedencia SLSA de nivel 2 significa que cada artefacto de lanzamiento incluye una atestación firmada criptográficamente que registra: qué commit de origen lo generó, qué flujo de trabajo lo produjo y que el flujo de trabajo se ejecutó en la plataforma esperada. Los usuarios y los gestores de paquetes pueden verificar esta atestación. Cierra la brecha entre “decimos que este binario proviene de esta fuente” y “este binario demuestra que proviene de esta fuente”.

GitHub Actions admite de forma nativa la generación de procedencia de nivel 2 de SLSA mediante la acción actions/attest. El coste de añadirla es un paso por cada trabajo de compilación.

6.2 Conventional Commits (Ya implícito, formalízalo)

La política de versionado del RFC de arquitectura y la integración con release-plz dependen del formato de commits convencionales para la generación del registro de cambios. El RFC de gobernanza ya hace referencia a las convenciones de los títulos de las PR. Este RFC formaliza la conexión: el formato de commits convencionales en los mensajes de los commits y los títulos de las PR es un requisito, no una sugerencia, ya que es la entrada que impulsa la generación automatizada del registro de cambios.

Las categorías que importan para el registro de cambios de ZeroClaw:

PrefijoSección de cambiosImpacto de la versión
feat:Nuevas característicasMENOR
fix:Corrección de erroresPATCH
feat!: o fix!:Cambios importantesPRINCIPAL
chore:MantenimientoNo hay entrada de lanzamiento
docs:DocumentaciónNo hay entrada de lanzamiento
perf:RendimientoPATCH
seguridad:Correcciones de seguridadPATCH (como mínimo)

CI aplica esto mediante un trabajo de validación del título del PR que verifica que el título coincida con el formato de commit convencional antes de que se ejecute cualquier otra comprobación.

6.3 Flujos de trabajo reutilizables

A medida que crece el número de crates y tipos de artefactos, la duplicación de workflows se convierte en un problema de mantenimiento. GitHub Actions admite workflows reutilizables: un workflow que puede ser invocado desde otro workflow como si fuera una función. La matriz de compilación, el escaneo de seguridad y el ejecutor de pruebas deberían extraerse cada uno como workflows reutilizables.

.github/
  workflows/
    ci.yml               ← PR checks (calls reusable workflows)
    release.yml          ← Release pipeline (calls reusable workflows)
    daily-audit.yml      ← Scheduled security scan
  _workflows/            ← Reusable workflow definitions
    build-rust.yml       ← Parameterised build job
    test-workspace.yml   ← Parameterised test job
    security-scan.yml    ← cargo-deny invocation + triage
    publish-release.yml  ← Parameterised publish job

Un flujo de trabajo reutilizable se llama con parámetros:

trabajos:
  compilar-kernel:
    utiliza: ./.github/_workflows/build-rust.yml
    con:
      objetivo: x86_64-unknown-linux-musl
      características: ""
      perfil: dist

Esto significa que el flujo de trabajo de CI y el flujo de trabajo de lanzamiento comparten la misma definición de compilación. Una corrección al proceso de compilación se aplica en todas partes al mismo tiempo.


7. Hoja de ruta por fases

La migración del pipeline sigue el mismo enfoque de la Higuera estranguladora que la migración del código: construir junto con, migrar de manera constante, sin romper nunca la puerta de entrada existente.


Fase 1 · v0.7.0: “Rationalise”

Tema: Un único pipeline, señal limpia, sin duplicación.

Por qué esta fase: La transición arquitectónica ya está en curso. La tubería debe dejar de luchar contra ella antes de que haga que el trabajo de implementación sea más difícil de lo necesario.

Entregables de la Fase 1

D1: Consolidar checks-on-pr.yml y ci-run.yml en un único flujo de trabajo

Fusiona los dos flujos de trabajo de PR en uno solo. El flujo de trabajo consolidado mantiene la estructura escalonada definida en §3.1. La distinción de nombres entre Quality Gate y CI desaparece. Hay un solo flujo de trabajo, un solo conjunto de resultados y un único lugar donde consultar.

El trabajo del filtro de puerta compuesta (CI Required Gate) se conserva. La protección de la rama sigue requiriendo únicamente ese trabajo. Esto significa que la estructura interna del pipeline puede cambiar sin necesidad de actualizar las reglas de protección de la rama.

D2: Reemplazar cargo audit con cargo deny

Añade deny.toml a la raíz del repositorio. Configura las secciones [advisories], [licenses] y [sources]. Resuelve todas las advertencias de RUSTSEC actuales en master: actualiza lo que se pueda actualizar y documenta lo que no se pueda, con la justificación y los problemas de seguimiento correspondientes. El control de seguridad pasa correctamente en master antes de que se complete esta fase.

D3: Corregir la invocación de clippy con reconocimiento de workspace

Cambia cargo clippy --all-targets -- -D warnings por cargo clippy --workspace --all-targets -- -D warnings en el flujo de trabajo consolidado. Elimina el script rust_strict_delta_gate.sh: con --workspace -D warnings siempre aplicado de forma estricta y limpia, el concepto de delta queda implícito.

D4: Formalizar la política de fijación de acciones

Agregue una nota en SECURITY.md y una comprobación de CI que valide que todas las referencias uses: en los archivos de flujo de trabajo estén fijadas mediante SHA. Agregue la configuración de dependabot para las actualizaciones de GitHub Actions.

D5: Agregar flujo de trabajo de escaneo diario de avisos

Añade daily-audit.yml como un flujo de trabajo programado que ejecuta cargo deny check advisories contra master a las 09:00 UTC. En caso de fallo, abre un Issue de GitHub con los detalles de la advertencia utilizando gh issue create.

Métricas de éxito para la Fase 1

  • Archivo de flujo de trabajo de PR único, sin duplicación
  • Los pases de seguridad en master son limpios, con la triaje documentada para todas las advertencias preexistentes.
  • cargo clippy --workspace se ejecuta y pasa sin errores
  • No hay referencias a etiquetas de acción mutable en ningún archivo de flujo de trabajo
  • Escaneo diario de advertencias operativo

Fase 2 · v0.8.0: “Compatible con espacios de trabajo”

Tema: La canalización comprende el espacio de trabajo. Retroalimentación rápida para cambios enfocados.

Por qué esta fase: Con la versión v0.8.0, el espacio de trabajo habrá crecido aún más. Ejecutar el pipeline completo en cada PR será cada vez más costoso. Los colaboradores de zeroclaw-tool-call-parser no deberían esperar 30 minutos por una reconstrucción del gateway.

Entregables de la Fase 2

D1: Detección de crates modificados

Añade un script scripts/ci/affected_crates.sh que utilice cargo metadata para construir el grafo de dependencias y devuelva el conjunto de crates afectadas por los archivos modificados del PR. El flujo de trabajo de CI utiliza esta salida para limitar la ejecución de las pruebas.

D2: Alcance de pruebas por crate

Agregue las banderas --package a cargo nextest en función de la salida de crate afectado. Las pruebas completas del espacio de trabajo continúan ejecutándose en los envíos a master y en nightly. Las PRs ejecutan el subconjunto afectado.

D3: Configuración de caché con reconocimiento de espacios de trabajo

Actualiza la configuración de Swatinem/rust-cache con un ámbito de espacio de trabajo explícito y save-if: ${{ github.ref == 'refs/heads/master' }} para evitar la inestabilidad del caché causada por PRs concurrentes.

D4: Extraer definiciones de flujos de trabajo reutilizables

Extrae los trabajos de compilación, pruebas y seguridad en archivos de flujo de trabajo reutilizables bajo .github/_workflows/. Actualiza ci.yml y el esqueleto de release.yml para llamarlos.

Métricas de éxito para la Fase 2

  • Un PR que solo toca zeroclaw-tool-call-parser ejecuta las pruebas de ese crate y sus dependencias, no del espacio de trabajo completo.
  • Tasa de aciertos en caché en CI superior al 80% para compilaciones incrementales
  • Flujos de trabajo reutilizables en su lugar para trabajos de compilación, prueba y seguridad

Fase 3 · v0.9.0: “Pipeline de Lanzamiento”

Tema: Automatización de lanzamientos que se ajusta al modelo de distribución.

Por qué esta fase: La fase 3 de la RFC de arquitectura extrae zeroclaw-gw como un binario independiente. Aquí se produce el primer lanzamiento con múltiples artefactos. El pipeline de lanzamiento debe estar listo antes de que sea necesario.

Entregables de la Fase 3

D1: Introducir release-plz y eliminar version-sync.yml

Configura release-plz para el espacio de trabajo. Los crates de aplicación del espacio de trabajo usan version.workspace = true. El crate zeroclaw-api, versionado de forma independiente, usa su propia configuración de lanzamiento. El flujo de trabajo version-sync.yml se retira.

D2: Construye el pipeline de releases estructurado en release.yml

Implementa el grafo dirigido de lanzamiento de §5.2: build-kernel-standard, build-kernel-hardware, build-gateway, con trabajos de publicación descendentes. Los trabajos de compilación de plugins están como stubs: se completan con éxito sin realizar ninguna operación hasta la Fase 4.

D3: Agregar procedencia SLSA Level 2

Añade actions/attest a cada trabajo de compilación. Las atestaciones de procedencia se adjuntan a los activos de las versiones de GitHub. Documenta las instrucciones de verificación en SECURITY.md.

D4: Retirar flujos de trabajo de lanzamiento redundantes

Consolide release-stable-manual.yml, release-beta-on-push.yml, pub-aur.yml, pub-homebrew-core.yml, pub-scoop.yml, discord-release.yml, tweet-release.yml en el pipeline estructurado release.yml. Estos flujos de trabajo crecieron de forma independiente; el pipeline estructurado los reemplaza con un único flujo auditable.

Métricas de éxito para la Fase 3

  • release-plz abre y gestiona las PR de lanzamiento en master
  • Los binarios del kernel y de la puerta de enlace se construyen y publican desde un único flujo de trabajo release.yml.
  • Procedencia SLSA de nivel 2 adjunta a todos los activos de la versión
  • Flujos de trabajo de lanzamiento redundantes retirados

Fase 4 · v1.0.0: “Platform Pipeline”

Tema: La canalización entrega la plataforma, no solo el binario.

Por qué esta fase: v1.0.0 es cuando los complementos WASM se vuelven publicables. La canalización debe gestionar la publicación de complementos, la carga en el registro y el instalador de escritorio de Tauri como artefactos de lanzamiento de primera clase.

Entregables de la Fase 4

D1: Activar los trabajos de compilación del plugin WASM

Implemente build-plugins-wasm en la canalización de lanzamiento. Cada crate de plugin se compila para wasm32-wasip2 en un job dedicado. Los manifiestos de plugin se generan y se firman. El job publish-plugin-registry sube los archivos WASM firmados al registro de plugins.

D2: Compilación y publicación del instalador de escritorio

Completa los trabajos de compilación de Tauri para macOS, Windows y Linux. El instalador empaqueta los binarios del kernel y del gateway. Las credenciales de firma de código para macOS y Windows se documentan como secretos de repositorio requeridos, con una guía de configuración.

D3: Publicar los estándares de CI/CD en docs/book/src/maintainers/ci-and-actions.md

La política de fijación de acciones, el proceso de triaje de avisos, los requisitos de commits convencionales y la estructura del pipeline de lanzamiento definidos en esta RFC se han extraído a docs/book/src/maintainers/ci-and-actions.md como referencia permanente. Esta RFC sigue siendo el registro histórico de las decisiones; el documento extraído es el que los colaboradores consultan en el día a día.

D4: Incorporación de colaboradores para el pipeline

Añade una sección de Ejecución de CI localmente a la documentación de contribución que muestre a los colaboradores cómo replicar las comprobaciones de CI en su propia máquina antes de realizar el push:

sh

# Qué ejecuta CI — ejecuta esto antes de hacer push
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo nextest run --workspace
cargo deny check

Métricas de éxito para la Fase 4

  • Los archivos del plugin WASM se publican en el registro como parte del pipeline de lanzamiento.
  • El instalador de escritorio de Tauri se construye y publica automáticamente en cada lanzamiento.
  • docs/book/src/maintainers/ci-and-actions.md existe y cubre la fijación de acciones, la triaje de avisos y los commits convencionales
  • Un colaborador puede replicar todas las comprobaciones de CI localmente con cuatro comandos

8. Qué significa esto para los colaboradores

Para los colaboradores que abren PRs

El pipeline consolidado significa un solo lugar donde consultar los resultados. La Etapa 1 (formato y lint) falla rápido: si tienes un error de formato, lo sabes en dos minutos sin esperar a una compilación. Si la Etapa 1 pasa, las etapas de compilación y pruebas se ejecutan en paralelo y tienes un resultado completo en menos de 30 minutos para la mayoría de los cambios.

El requisito de commits convencionales en los títulos de los PR se aplica mediante CI. Si tu título no coincide con el formato, el trabajo de lint falla inmediatamente con un mensaje claro. Esto no es burocracia: es la entrada que genera el changelog automáticamente, lo que significa que las versiones se publican más rápido y con menos trabajo manual.

Para los colaboradores que añaden dependencias

Cada nueva dependencia pasa por cargo deny. Si la dependencia tiene una vulnerabilidad conocida, una licencia inaceptable o proviene de una fuente no confiable, el control de seguridad falla y te indica el motivo. Esto es intencional. La respuesta adecuada es investigar la dependencia, no suprimir la verificación.

Si una dependencia incluye una advertencia que no se puede solucionar (una dependencia transitiva sin actualizaciones disponibles), el proceso de triaje descrito en §4.3 es el método para documentarlo. Abre un problema de seguimiento, añade la entrada de ignorado en deny.toml con tu justificación y continúa. La postura de seguridad se mantiene mediante documentación, no esperando que la advertencia desaparezca.

Para los colaboradores que añaden archivos de flujo de trabajo

Los nuevos archivos de flujo de trabajo siguen tres reglas sin excepción:

  1. Todas las referencias uses: están fijadas a un SHA con un comentario de versión.
  2. Los nuevos trabajos se extraen como flujos de trabajo reutilizables si duplican la lógica de un trabajo existente.
  3. Se han añadido nuevos trabajos relacionados con la versión a release.yml, no como nuevos archivos de flujo de trabajo.

En caso de duda, pregunta antes de agregar. Los archivos de workflow son cambios de alto riesgo: se ejecutan con permisos elevados en la infraestructura de CI y pueden afectar la seguridad de la cadena de suministro. Merecen el mismo estándar de revisión que src/security/.

Para los mantenedores

El análisis diario de avisos significa que la seguridad es una tarea de mantenimiento regular, no una crisis. Cuando se activa un nuevo aviso, el proceso de triaje está bien definido y el resultado se documenta en deny.toml y en un problema de seguimiento. Los revisores pueden auditar el historial completo de decisiones sobre avisos en el historial de git.

El PR de lanzamiento de release-plz es el punto de control de revisión del lanzamiento. Antes de que se publique cualquier cosa, el equipo ve la versión, el registro de cambios y la lista de crates modificados. Los lanzamientos no ocurren por accidente.


Apéndice A: Glosario

SLSA (Supply-chain Levels for Software Artifacts): Un marco de seguridad que define niveles de integridad de compilación, desde la procedencia básica hasta compilaciones completamente herméticas. Desarrollado por Google y adoptado por la OpenSSF. El nivel 2 es el objetivo práctico para la mayoría de los proyectos de código abierto: plataforma de compilación alojada, scripts de compilación con control de versiones, procedencia firmada adjunta a los artefactos.

Procedencia: Un registro firmado criptográficamente sobre el origen de un artefacto de compilación: qué commit de origen, qué flujo de trabajo, qué plataforma. Permite a los usuarios y gestores de paquetes verificar que un binario fue producido a partir del código fuente declarado mediante el proceso declarado.

cargo deny: Un plugin de Cargo que aplica políticas de dependencias en tres dimensiones: avisos de seguridad (de la base de datos RustSec), licencias de software (contra una lista de permitidos definida) y registros de origen (asegurando que las dependencias provengan solo de ubicaciones aprobadas). Más configurable que cargo audit y mejor adaptado a la gestión de políticas a gran escala.

release-plz: Una herramienta de automatización de lanzamientos del ecosistema Rust que crea “Release PRs” al hacer push a la rama predeterminada, incrementando versiones y generando changelogs a partir del historial de commits convencionales. Compatible con workspaces; entiende qué crates cambiaron y cuáles necesitan nuevas versiones.

Flujo de trabajo reutilizable: Un flujo de trabajo de GitHub Actions que puede invocarse como un job desde otro flujo de trabajo, con parámetros. Permite definir la lógica de compilación, pruebas y seguridad una sola vez e invocarla tanto desde el pipeline de PR como desde el pipeline de lanzamiento.

Conventional commits: Una convención de mensajes de commit (feat:, fix:, chore:, etc.) que permite la generación automatizada de changelogs y la determinación de versiones. La entrada que utilizan herramientas como release-plz para decidir si un lanzamiento es un incremento patch, minor o major.

Strangler Fig (en el contexto de pipelines): La misma estrategia de migración aplicada a los flujos de trabajo: construir la nueva estructura del pipeline junto a la existente, migrar los jobs uno a uno, y retirar los archivos antiguos solo cuando la nueva estructura esté completa y verificada.


Apéndice B: Lecturas adicionales

  • SLSA Framework: La especificación completa y las guías de implementación para los niveles de seguridad de la cadena de suministro.

  • Documentación de cargo deny: Referencia de configuración para el archivo de políticas deny.toml, incluyendo todas las opciones de avisos, licencias y fuentes.

  • Documentación de release-plz: Configuración del workspace, personalización del formato del changelog y guía de integración con GitHub Actions.

  • Refuerzo de seguridad de GitHub Actions: Guía oficial sobre el anclaje de SHA, permisos de tokens y riesgos de la cadena de suministro en los flujos de trabajo de Actions.

  • Especificación de Conventional Commits: La especificación completa del formato de mensajes de commit y su relación con el versionado semántico.

  • OpenSSF Scorecard: Una herramienta automatizada que puntúa proyectos de código abierto según sus prácticas de seguridad, incluyendo el anclaje de dependencias, la protección de ramas, los requisitos de revisión de código y más. Útil como evaluación inicial y como métrica continua del estado del proyecto.