Files
fn_registry/dev/issues/completed/0022-init-pipelines.md
T
egutierrez 7cddf872e5 docs: cerrar issue 0022
Los 4 init pipelines (init_api_app, init_web_app, init_desktop_app,
init_cli_app) estan implementados, verificados end-to-end con `fn run`, e
indexados en registry.db como kind=pipeline + tag=launcher. Guia consolidada
en docs/init-pipelines.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:59:04 +02:00

22 KiB

0022 — Init Pipelines

Metadata

Campo Valor
ID 0022
Estado pendiente
Prioridad alta
Tipo feature

Dependencias

ID Titulo Estado Requerido
0009 HTTP Server Foundation pendiente si
0010 Auth System pendiente si
0015 Database Migrations pendiente si

Bloqueada por: #0009 (http_serve, http_router, http_json_response, http_middleware_chain), #0010 (jwt_middleware, password_hash, session_create), #0015 (migration_create, migration_up, migration_status). Los pipelines de init generan boilerplate que importa y compone estas funciones — sin ellas, el codigo scaffoldeado no compilaria.

Desbloquea: cualquier app nueva del registry se puede crear con un solo comando en vez de copiar y adaptar una app existente.


Objetivo

Cuatro bash pipelines que scaffold apps completas en apps/ con un solo comando. Cada uno genera la estructura de directorios, archivos boilerplate, app.md con frontmatter correcto, y verifica que el resultado compila. Misma filosofia que init_jupyter_analysis_bash_pipelines: componer funciones atomicas del registry para producir un entorno listo para trabajar.

Contexto

  • Existen tres init pipelines: init_go_project_bash_pipelines (repo Go generico), init_go_module_bash_pipelines (modulo Go simple), init_jupyter_analysis_bash_pipelines (analysis Jupyter). Todos scaffoldean estructuras basicas.
  • No existe ningun pipeline para scaffoldear apps del registry con HTTP server, auth, DB, frontend, Wails o TUI. Cada app nueva se construye copiando otra y adaptando manualmente.
  • Las apps existentes (deploy_server, sqlite_api, rapid_dashboards, pipeline_launcher) comparten patrones repetitivos: main.go con graceful shutdown, config desde env vars, health check, migrations, app.md.
  • Con las funciones de 0009 (HTTP), 0010 (auth) y 0015 (migrations) disponibles, el boilerplate de una app API es predecible y automatizable.
  • El registry ya tiene funciones Wails completas (scaffold_wails_app_go_infra, install_wails_bash_infra, wails_build_go_infra, hooks use_wails_*_ts_ui, wails_provider_ts_ui) y TUI (new_base_model_go_tui, run_fullscreen_go_tui, temas, spinners, listas).

Arquitectura

bash/functions/pipelines/
├── init_api_app.sh              — NEW: scaffold Go HTTP API app
├── init_api_app.md              — NEW
├── init_web_app.sh              — NEW: scaffold full-stack app (Go API + React)
├── init_web_app.md              — NEW
├── init_desktop_app.sh          — NEW: scaffold Wails desktop app
├── init_desktop_app.md          — NEW
├── init_cli_app.sh              — NEW: scaffold Go CLI/TUI app
├── init_cli_app.md              — NEW

Todas son kind: pipeline, purity: impure, lang: bash, domain: pipelines.

Patron de composicion

Cada pipeline sigue el mismo patron que init_jupyter_analysis:

  1. Source funciones atomicas del registry via source "$REGISTRY_ROOT/bash/functions/..."
  2. Parsear argumentos (nombre obligatorio, flags opcionales)
  3. Crear estructura de directorios con mkdir -p
  4. Escribir archivos boilerplate con heredocs
  5. Generar app.md con frontmatter correcto
  6. Ejecutar fn index para registrar la app
  7. Verificar con go vet / pnpm build / wails build segun corresponda

Diseno

Pipeline 1: init_api_app

Scaffold de Go HTTP API app en apps/.

Uso:

fn run init_api_app my_service
fn run init_api_app my_service --port 8080 --with-auth --with-db

Archivos generados:

apps/{nombre}/
├── main.go              — Entry point: config → router → middleware → http_serve con graceful shutdown
├── handlers.go          — Handler GET /health + handler de ejemplo GET /api/v1/status
├── config.go            — Struct Config con tags + carga desde .env / env vars
├── migrations/
│   └── 001_initial.sql  — CREATE TABLE ejemplo con id, created_at, updated_at
├── app.md               — Frontmatter con tag service, uses_functions, dir_path
├── Makefile             — Targets: build, run, test, vet, clean
├── .env.example         — Variables de entorno documentadas (PORT, DB_PATH, etc.)
└── .gitignore           — Binario, .env, *.db-shm, *.db-wal

Funciones del registry compuestas:

Funcion Para que
assert_command_exists_bash_shell Verificar que go esta instalado
http_serve_go_infra (0009) Codigo de graceful shutdown en main.go
http_router_go_infra (0009) Registro de rutas en main.go
http_json_response_go_infra (0009) Helper en handlers.go
http_error_response_go_infra (0009) Helper en handlers.go
http_middleware_chain_go_infra (0009) Composicion de middlewares en main.go
http_logger_middleware_go_infra (0009) Logging en main.go
http_cors_middleware_go_infra (0009) CORS en main.go
migration_up_go_infra (0015) Aplicar migrations en main.go al arrancar
config_load_go_infra (0018) Carga de config en config.go (si existe)

Flags opcionales:

Flag Efecto
--port N Puerto por defecto en config y .env.example (default: 8080)
--with-auth Anade jwt_middleware, handlers de login/register, tabla users en migration
--with-db Anade operations.db setup, store.go con helpers CRUD basicos
--with-ops Anade fn ops init para crear operations.db con schema completo

main.go generado (esquema):

package main

import (
    "context"
    "log"
    "os"
    "os/signal"

    "fn_registry/functions/infra"
)

func main() {
    cfg := LoadConfig()

    // Migrations
    if err := infra.MigrationUp(cfg.DBPath, "migrations"); err != nil {
        log.Fatal(err)
    }

    // Routes
    routes := []infra.Route{
        {Method: "GET", Path: "/health", Handler: healthHandler},
        {Method: "GET", Path: "/api/v1/status", Handler: statusHandler},
    }
    mux := infra.HttpRouter(routes)

    // Middleware
    middleware := infra.HttpMiddlewareChain(
        infra.HttpCorsMiddleware(cfg.CORSOrigins, []string{"GET", "POST", "PUT", "DELETE"}),
        infra.HttpLoggerMiddleware(os.Stdout),
    )

    // Serve
    ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
    defer cancel()

    log.Printf("starting %s on :%s", cfg.AppName, cfg.Port)
    if err := infra.HttpServe(":"+cfg.Port, middleware(mux), ctx); err != nil {
        log.Fatal(err)
    }
}

Pipeline 2: init_web_app

Scaffold de full-stack app: Go API backend + React frontend con Mantine.

Uso:

fn run init_web_app my_dashboard
fn run init_web_app my_dashboard --port 8080 --with-auth

Archivos generados:

apps/{nombre}/
├── main.go              — Igual que init_api_app + serve static files del frontend build
├── handlers.go          — Health + API handlers de ejemplo
├── config.go            — Config con FRONTEND_DIR
├── migrations/
│   └── 001_initial.sql
├── app.md               — tag service, uses frontend
├── Makefile             — Targets: build, build-frontend, run, dev, test, clean
├── .env.example
├── .gitignore
├── docker-compose.yml   — Dev: Go API hot-reload + frontend dev server
└── frontend/
    ├── package.json     — pnpm, vite, react, @mantine/core, @mantine/charts, @fn_library
    ├── vite.config.ts   — API proxy a localhost:${port}
    ├── tsconfig.json
    ├── index.html
    ├── postcss.config.cjs
    └── src/
        ├── main.tsx     — FnMantineProvider + App mount
        ├── App.tsx      — Router basico con pagina de ejemplo
        ├── theme.ts     — createTheme() con colores del proyecto
        └── pages/
            └── Home.tsx — Pagina de ejemplo usando crud_page_ts_ui o dashboard_layout_ts_ui

Funciones adicionales compuestas (sobre init_api_app):

Funcion Para que
mantine_provider_ts_ui Provider raiz en main.tsx
crud_page_ts_ui Pagina de ejemplo funcional
app_shell_ts_ui Layout con navbar y header
data_table_ts_ui Tabla de datos en la pagina de ejemplo

vite.config.ts generado:

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      "@fn_library": path.resolve(__dirname, "../../frontend/functions/ui"),
    },
  },
  server: {
    proxy: {
      "/api": {
        target: "http://localhost:${PORT}",
        changeOrigin: true,
      },
      "/health": {
        target: "http://localhost:${PORT}",
      },
    },
  },
});

docker-compose.yml generado:

services:
  api:
    build: .
    ports:
      - "${PORT}:${PORT}"
    env_file: .env
    volumes:
      - ./migrations:/app/migrations
  frontend:
    image: node:20-alpine
    working_dir: /app
    command: sh -c "corepack enable && pnpm install && pnpm dev"
    ports:
      - "5173:5173"
    volumes:
      - ./frontend:/app

Pipeline 3: init_desktop_app

Scaffold de Wails desktop app con Go backend y React frontend con @fn_library.

Uso:

fn run init_desktop_app my_tool
fn run init_desktop_app my_tool --with-db

Archivos generados:

apps/{nombre}/
├── main.go              — Wails entry point con embed del frontend
├── app.go               — Struct App con bindings base (Greet, GetVersion)
├── wails.json           — Config Wails apuntando a frontend/
├── go.mod
├── app.md               — framework: wails, uses wails hooks
├── .gitignore
└── frontend/
    ├── package.json     — pnpm, vite, react, @mantine/core, @fn_library
    ├── vite.config.ts
    ├── tsconfig.json
    ├── index.html
    └── src/
        ├── main.tsx     — WailsProvider + FnMantineProvider + App
        ├── App.tsx      — Ejemplo usando useWailsQuery + data_table
        └── theme.ts     — createTheme()

Funciones del registry compuestas:

Funcion Para que
scaffold_wails_app_go_infra Genera estructura base Wails (main.go, app.go, wails.json, go.mod)
install_wails_bash_infra Verifica/instala Wails CLI y deps de sistema
wails_provider_ts_ui Provider React para IPC cache
use_wails_query_ts_ui Hook de ejemplo en App.tsx
mantine_provider_ts_ui Provider Mantine
wails_bind_crud_go_infra Genera bindings CRUD si --with-db

Flags opcionales:

Flag Efecto
--with-db Anade SQLite con migrations, bindings CRUD generados por wails_bind_crud_go_infra

Pipeline 4: init_cli_app

Scaffold de Go CLI app con subcomandos y componentes TUI de Bubbletea.

Uso:

fn run init_cli_app my_cli
fn run init_cli_app my_cli --with-tui

Archivos generados:

apps/{nombre}/
├── main.go              — Entry point con subcommand routing (os.Args)
├── cmd_version.go       — Subcomando: version
├── cmd_status.go        — Subcomando de ejemplo: status (imprime info)
├── app.md               — framework vacio (CLI puro) o bubbletea (con --with-tui)
├── Makefile             — Targets: build, run, install, test, clean
├── .gitignore
└── go.mod

Con --with-tui:

apps/{nombre}/
├── main.go              — Entry point con run_fullscreen o run_model
├── model.go             — BaseModel + Update + View con tema oscuro
├── cmd_version.go       — Subcomando no-TUI
├── app.md               — framework: bubbletea
├── Makefile
├── .gitignore
└── go.mod

Funciones del registry compuestas:

Funcion Para que
assert_command_exists_bash_shell Verificar Go
new_base_model_go_tui Modelo base en model.go
dark_styles_go_tui Tema oscuro por defecto
run_fullscreen_go_tui Arranque fullscreen en main.go
new_spinner_go_tui Componente de ejemplo
new_filtered_list_go_tui Componente de ejemplo

main.go generado (sin TUI):

package main

import (
    "fmt"
    "os"
)

var version = "dev"

func main() {
    if len(os.Args) < 2 {
        printUsage()
        os.Exit(1)
    }

    switch os.Args[1] {
    case "version":
        cmdVersion()
    case "status":
        cmdStatus()
    default:
        fmt.Fprintf(os.Stderr, "unknown command: %s\n", os.Args[1])
        printUsage()
        os.Exit(1)
    }
}

func printUsage() {
    fmt.Println("Usage: {nombre} <command>")
    fmt.Println()
    fmt.Println("Commands:")
    fmt.Println("  version   Print version")
    fmt.Println("  status    Show status")
}

Tareas

Fase 1: init_api_app (pipeline base)

  • 1.1 Crear bash/functions/pipelines/init_api_app.sh con source de funciones atomicas, parseo de argumentos, y generacion de estructura
  • 1.2 Escribir heredocs para main.go, handlers.go, config.go que importen funciones de 0009 y 0015
  • 1.3 Generar app.md con frontmatter correcto (tag service, uses_functions con IDs reales, dir_path)
  • 1.4 Generar Makefile, .env.example, .gitignore, migrations/001_initial.sql
  • 1.5 Flag --with-auth: anadir imports de 0010, handlers de login/register, tabla users en migration
  • 1.6 Flag --with-db: anadir store.go con helpers CRUD, setup de SQLite al arrancar
  • 1.7 Ejecutar go vet -tags fts5 al final como verificacion
  • 1.8 Crear init_api_app.md con frontmatter de pipeline

Fase 2: init_web_app (extiende init_api_app)

  • 2.1 Crear bash/functions/pipelines/init_web_app.sh que primero invoca la logica de init_api_app y luego anade el frontend
  • 2.2 Generar frontend/ con package.json (pnpm, vite, react, mantine, @fn_library alias)
  • 2.3 Generar vite.config.ts con proxy al backend y alias @fn_library
  • 2.4 Generar src/main.tsx con FnMantineProvider, src/App.tsx con AppShell, src/pages/Home.tsx con ejemplo
  • 2.5 Generar docker-compose.yml para desarrollo
  • 2.6 Actualizar main.go para servir static files del frontend build
  • 2.7 Ejecutar pnpm install && pnpm build como verificacion del frontend
  • 2.8 Crear init_web_app.md con frontmatter de pipeline

Fase 3: init_desktop_app

  • 3.1 Crear bash/functions/pipelines/init_desktop_app.sh que invoca scaffold_wails_app_go_infra y anade frontend React
  • 3.2 Verificar/instalar Wails con install_wails_bash_infra
  • 3.3 Generar frontend con WailsProvider + FnMantineProvider y ejemplo con useWailsQuery
  • 3.4 Flag --with-db: invocar wails_bind_crud_go_infra para generar bindings
  • 3.5 Ejecutar wails build como verificacion
  • 3.6 Crear init_desktop_app.md con frontmatter de pipeline

Fase 4: init_cli_app

  • 4.1 Crear bash/functions/pipelines/init_cli_app.sh con generacion de estructura CLI basica
  • 4.2 Generar main.go con routing de subcomandos, cmd_version.go, cmd_status.go
  • 4.3 Flag --with-tui: generar model.go con new_base_model, dark_styles, run_fullscreen
  • 4.4 Ejecutar go vet como verificacion
  • 4.5 Crear init_cli_app.md con frontmatter de pipeline

Fase 5: Integracion

  • 5.1 fn index y verificar que los 4 pipelines aparecen en registry.db con kind=pipeline, purity=impure
  • 5.2 Verificar fn run init_api_app test_app end-to-end: genera, compila, limpia
  • 5.3 Verificar fn run init_web_app test_web end-to-end
  • 5.4 Verificar fn run init_desktop_app test_desktop end-to-end
  • 5.5 Verificar fn run init_cli_app test_cli end-to-end

Fase 6: Documentacion de uso rapido

Cada pipeline debe ser usable sin leer el issue completo. La documentacion va en dos niveles: el .md de cada funcion (fuente de verdad para fn show) y una guia consolidada.

  • 6.1 En cada .md de pipeline (init_api_app.md, etc.) documentar en la seccion documentation del frontmatter:
    • Sinopsis: fn run init_api_app <nombre> [--port N] [--with-auth] [--with-db]
    • Descripcion de cada flag y su efecto concreto (que archivos anade, que imports genera)
    • Listado de archivos generados con una linea de descripcion cada uno
    • Post-setup: que comandos ejecutar despues (make run, make dev, wails dev, etc.)
    • Ejemplo rapido: un bloque copy-paste de 3-4 lineas que crea la app y la arranca
  • 6.2 En el campo params del frontmatter de cada pipeline, documentar cada argumento y flag con name y desc semantico para que fn check params pase y la info sea buscable via FTS5
  • 6.3 En el campo example del frontmatter, poner el caso de uso mas comun (una linea):
    • init_api_app: fn run init_api_app my_service --with-db
    • init_web_app: fn run init_web_app my_dashboard --with-auth
    • init_desktop_app: fn run init_desktop_app my_tool
    • init_cli_app: fn run init_cli_app my_cli --with-tui
  • 6.4 Crear docs/init-pipelines.md como guia consolidada de referencia rapida con:
    • Tabla resumen de los 4 pipelines (nombre, que genera, flags disponibles)
    • Arbol de decision: "quiero una API" → init_api_app, "quiero frontend" → init_web_app, "quiero desktop" → init_desktop_app, "quiero CLI" → init_cli_app
    • Seccion de combinaciones comunes (API + auth + DB, web dashboard, desktop con SQLite, CLI con TUI)
    • FAQ: como anadir auth despues, como cambiar el puerto, como anadir operations.db, como agregar mas paginas al frontend
  • 6.5 Verificar que fn show init_api_app_bash_pipelines (y los otros 3) muestra la documentacion completa con params, ejemplo y notas de uso

Ejemplo de uso

# API service con auth y database
fn run init_api_app billing_api --port 8090 --with-auth --with-db
cd apps/billing_api
make run
# → starting billing_api on :8090
# → curl localhost:8090/health → {"status":"ok"}

# Full-stack dashboard
fn run init_web_app inventory_dashboard --with-auth
cd apps/inventory_dashboard
make dev
# → API en :8080, frontend en :5173 con proxy

# Desktop app con base de datos
fn run init_desktop_app data_explorer --with-db
cd apps/data_explorer
wails dev
# → App de escritorio con React + SQLite

# CLI con TUI
fn run init_cli_app deploy_helper --with-tui
cd apps/deploy_helper
make run -- status
# → TUI fullscreen con lista filtrable

Cada pipeline genera su app.md listo para fn index:

---
name: billing_api
lang: go
domain: tools
description: "API de facturacion."
tags: [service]
uses_functions:
  - http_serve_go_infra
  - http_router_go_infra
  - http_middleware_chain_go_infra
  - http_cors_middleware_go_infra
  - http_logger_middleware_go_infra
  - http_json_response_go_infra
  - http_error_response_go_infra
  - migration_up_go_infra
uses_types: []
framework: "net/http"
entry_point: "main.go"
dir_path: "apps/billing_api"
---

Decisiones de diseno

  • Bash, no Go: los init pipelines generan archivos con heredocs — bash es el lenguaje natural para esto. Go seria overengineering para scaffolding de texto. Coherente con init_jupyter_analysis y los demas init existentes.
  • Composicion sobre monolito: cada pipeline sourcea funciones atomicas del registry (assert_command_exists, scaffold_wails_app, etc.) en vez de reimplementar. Si una funcion atomica mejora, todos los pipelines se benefician.
  • init_web_app extiende init_api_app: el pipeline web reutiliza la logica del API (misma estructura backend) y anade la capa frontend encima. No duplica codigo.
  • Verificacion al final: cada pipeline termina con go vet, pnpm build, o wails build para garantizar que el scaffold compila. Si falla, el pipeline reporta el error antes de declarar exito.
  • Flags opcionales con defaults sensatos: el caso base (sin flags) genera una app funcional minima. --with-auth, --with-db, --with-tui anaden capas incrementales. El usuario no necesita decidir todo upfront.
  • @fn_library como alias, no copia: el frontend generado referencia @fn_library via alias en vite.config.ts apuntando a frontend/functions/ui/ del registry. Los componentes se comparten, no se duplican.
  • app.md generado automaticamente: el frontmatter incluye uses_functions con los IDs reales de las funciones que el boilerplate importa. fn index los valida al registrar la app.
  • Sin framework CLI externo para init_cli_app: routing de subcomandos con os.Args y switch — consistente con las apps existentes del registry que no usan cobra/urfave. Para TUI se usa Bubbletea que ya esta en el registry.

Riesgos

  • Dependencias no implementadas: los tres issues de dependencia (0009, 0010, 0015) estan pendientes. Si alguna funcion cambia de firma durante su implementacion, los heredocs de los pipelines necesitaran ajuste. Mitigacion: implementar los pipelines despues de que las dependencias esten merged, o mantener los heredocs parametricos para absorber cambios menores.
  • Heredocs fragiles: generar Go/TS/YAML con heredocs bash es propenso a errores de indentacion, escape de variables y quoting. Mitigacion: cada pipeline incluye verificacion final (go vet / pnpm build) que detecta errores de sintaxis inmediatamente. Tests end-to-end en fase 5.
  • Frontend desactualizado respecto a @fn_library: si los componentes de frontend/functions/ui/ evolucionan, el boilerplate generado puede quedar desactualizado. Mitigacion: el boilerplate es minimo (un Provider, un AppShell, una pagina de ejemplo) — el usuario lo extiende con los componentes actuales del registry.
  • Wails como dependencia de sistema: init_desktop_app requiere GTK3 + WebKit2GTK instalados en Linux. install_wails_bash_infra lo maneja, pero puede fallar en distros no soportadas. Mitigacion: el pipeline verifica la instalacion al inicio y falla rapido con mensaje descriptivo.
  • Colision de nombres: si el usuario elige un nombre que ya existe en apps/, el pipeline sobreescribiria archivos. Mitigacion: verificar si apps/{nombre}/ existe al inicio y abortar con error si ya existe.