Files

570 lines
22 KiB
Markdown

---
id: "0022"
title: "Init Pipelines"
status: completado
type: feature
domain: []
scope: multi-app
priority: alta
depends: []
blocks: []
related: []
created: 2026-05-17
updated: 2026-05-17
tags: []
---
# 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:**
```bash
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):**
```go
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:**
```bash
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:**
```ts
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:**
```yaml
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:**
```bash
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:**
```bash
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):**
```go
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
```bash
# 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`:**
```yaml
---
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.