feat: agregar auto update

spec: separar responsabilidades
feat: agregra pipeline de construccion en gitea
This commit is contained in:
Carlos Sandoval
2026-09-02 20:01:35 +00:00
parent c1a060312f
commit 55eff5df89
9 changed files with 316 additions and 33 deletions
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-02
@@ -0,0 +1,62 @@
# Design: split-main-into-files
## Context
El proyecto `outline-mcp` vive íntegramente en `main.go` (425 líneas, paquete `main`), según la decisión D2 del change `create-outline-mcp-server`, que explícitamente anticipaba la extracción a múltiples archivos como refactor futuro. El archivo mezcla tres dominios independientes (cliente HTTP de Outline, handlers de tools MCP, auto-update vía Gitea). No hay tests aún; la verificación actual es `go build`, `golangci-lint run` y prueba manual por stdio.
## Goals / Non-Goals
**Goals:**
- Repartir `main.go` en archivos por responsabilidad dentro del mismo paquete `main`, sin ningún cambio de comportamiento.
- Dejar el código preparado para añadir tests y nuevas tools sin fricción.
- Cumplir la promesa de D2: extracción sin impacto en specs de comportamiento ni en el pipeline de release.
**Non-Goals:**
- Introducir paquetes `internal/` (descartado: no hay necesidad de ocultar API entre dominios con el alcance actual).
- Añadir tests (cambio posterior; solo se deja el terreno listo).
- Renombrar o refactorizar funciones, tipos o lógica.
- Modificar `go.mod`, dependencias, pipeline de release o contrato de nombres de assets.
## Decisions
### D1: Múltiples archivos en paquete `main` (opción B) frente a `internal/` (opción C)
- **Elección**: dividir en archivos del mismo paquete `main`. Estructura resultante:
- `main.go` — función `main()`, variables de build (`Version`, `GiteaURL`, `RepoOwner`, `RepoName`) y registro de tools.
- `client.go` — tipo `OutlineClient`, `newOutlineClient`, método `post`.
- `tools.go` — los cuatro handlers de tools MCP.
- `updater.go` — `giteaRelease`, `parseVersion`, `versionNewer`, `doUpdate`.
- **Alternativa** (`internal/outline`, `internal/tools`, `internal/selfupdate`): descartada porque obliga a decidir superficie exportada acoplando los handlers al SDK (`mcp.CallToolResult` en firmas) y no aporta ocultación real con el alcance actual. Queda como refactor futuro si el número de tools crece de forma significativa.
- **Alternativa** (mantener un solo archivo): descartada; el argumento de bootstrap de D2 ya se cumplió y la mezcla de dominios penaliza la legibilidad y los futuros diffs.
### D2 (revisión): se actualiza la decisión del change `create-outline-mcp-server`
La D2 original ("todo el código en `main.go`") queda sustituida por esta organización multi-archivo en paquete único. El carácter de monolito deliberado (un único binario, un único paquete) se mantiene; lo que cambia es la distribución física en archivos.
### D3: Movimiento mecánico de código, sin reescritura
Las funciones y tipos se trasladan íntegras (mismos nombres, firmas, comentarios y directivas `//nolint`). Solo se ajustan los bloques `import` de cada archivo nuevo. Esto minimiza el riesgo del refactor y hace el diff auditable como puro movimiento.
### D4: Las variables de build permanecen en `main.go`
`Version`, `GiteaURL`, `RepoOwner` y `RepoName` se inyectan con `-ldflags -X main.Version=...` y el paquete sigue siendo `main`, por lo que no hay razón para moverlas. Permanecen junto al entrypoint, que es su punto natural de referencia.
## Risks / Trade-offs
- [División incorrecta de imports causa fallo de compilación] → Verificación inmediata con `go build ./...` en el contenedor tras cada movimiento; es un error de compilación, no un error silencioso.
- [`golangci-lint` señala imports sin usar o duplicados] → Ejecutar `golangci-lint run` en el contenedor como parte de la verificación.
- [Confusión temporal por familiaridad con el archivo único] → El diff es puro movimiento de código; los nombres de archivo (`client.go`, `tools.go`, `updater.go`) se corresponden directamente con los dominios.
- [Regresión funcional no detectada por compilación] → Prueba manual por stdio del servidor MCP (handshake + tools/list) tras el refactor, igual que en el change original.
## Migration Plan
1. Crear los tres archivos nuevos moviendo bloques completos desde `main.go`.
2. Reducir `main.go` a entrypoint + variables de build + registro de tools.
3. Compilar, lintear y probar por stdio en el contenedor DevPod.
4. Rollback trivial: `git revert` del commit único del refactor.
## Open Questions
(ninguna)
@@ -0,0 +1,38 @@
# Proposal: split-main-into-files
## Why
Todo el código del servidor reside en un único archivo `main.go` (425 líneas) que mezcla tres responsabilidades sin relación entre sí: el cliente HTTP de Outline, los handlers de tools MCP y el mecanismo de auto-update. La decisión de diseño D2 (arquitectura de un solo archivo) se tomó para facilitar el bootstrap del proyecto; ese objetivo ya se cumplió y la pendiente de crecimiento (cada tool nueva agrega ~35-40 líneas) hará el archivo difícil de mantener y de testear a corto plazo.
## What Changes
- Dividir `main.go` en varios archivos dentro del **mismo paquete `main`** (sin introducir paquetes `internal/`):
- `main.go`: únicamente la función `main()` (wiring del servidor MCP y registro de tools) y las variables inyectadas por `-ldflags`.
- `client.go`: tipo `OutlineClient`, constructor `newOutlineClient` y método `post`.
- `tools.go`: los cuatro handlers de tools MCP (`handleListCollections`, `handleSearch`, `handleGetDocument`, `handleCreateDocument`).
- `updater.go`: `giteaRelease`, `parseVersion`, `versionNewer` y `doUpdate`.
- Se trata de un refactor mecánico: se mueve código, no se modifica. No hay cambios de comportamiento, de API MCP ni de contrato de nombres de assets (`outline-mcp_{GOOS}_{GOARCH}`).
## Capabilities
### New Capabilities
- `code-structure`: Convención de organización del código fuente del proyecto: un paquete `main` distribuido en archivos por responsabilidad (cliente, tools, updater, entrypoint), manteniendo el carácter deliberado de monolito de paquete único.
### Modified Capabilities
(ninguna — no hay cambios a nivel de requisitos de comportamiento)
## Impact
- **Código**: único archivo afectado `main.go`, que se reparte en 4 archivos nuevos dentro de la raíz del módulo. `go.mod` y `go.sum` no cambian.
- **Build/release**: sin impacto. El pipeline de release (`.gitea/workflows/release.yml`) compila `.` y el contrato de nombres de assets no depende de la estructura de archivos.
- **Auto-update**: sin impacto funcional; `doUpdate` se traslada íntegro a `updater.go`.
- **Dependencias**: no se añaden ni eliminan dependencias.
## No objetivos
- No se introducen paquetes `internal/` (opción C descartada por ahora; queda como refactor futuro si el número de tools crece significativamente).
- No se añaden tests en este change (la estructura elegida los facilita, pero son alcance de un cambio posterior).
- No se modifica ningún comportamiento observable: protocolo MCP, manejo de errores, variables de entorno ni lógica de actualización.
- No se renombran funciones ni tipos; solo cambian de archivo.
@@ -0,0 +1,27 @@
## ADDED Requirements
### Requirement: Organización del código fuente por archivos
El código fuente del proyecto SHALL estar distribuido en múltiples archivos dentro del paquete único `main`, agrupando cada archivo una única responsabilidad: entrypoint (`main.go`), cliente HTTP de Outline (`client.go`), handlers de tools MCP (`tools.go`) y auto-update (`updater.go`).
#### Scenario: Distribución de archivos
- **WHEN** se revisa la raíz del módulo `outline-mcp`
- **THEN** existen los archivos `main.go`, `client.go`, `tools.go` y `updater.go`, todos declarando `package main`
#### Scenario: Contenido del entrypoint
- **WHEN** se revisa `main.go`
- **THEN** contiene únicamente la función `main()`, las variables de build inyectadas por `-ldflags` (`Version`, `GiteaURL`, `RepoOwner`, `RepoName`) y el registro de tools en el servidor MCP
#### Scenario: Aislamiento del auto-update
- **WHEN** se revisa `updater.go`
- **THEN** contiene `giteaRelease`, `parseVersion`, `versionNewer` y `doUpdate`, sin referencias al cliente de Outline ni a los handlers de tools MCP
### Requirement: Refactor sin cambio de comportamiento
La distribución del código en archivos SHALL preservar íntegramente el comportamiento observable del servidor: nombres y esquemas de las tools MCP, manejo de errores de configuración, protocolo MCP y lógica de auto-update.
#### Scenario: El servidor funciona tras el refactor
- **WHEN** se compila el binario resultante y se ejecuta el handshake MCP por stdio seguido de una petición `tools/list`
- **THEN** el servidor responde correctamente y lista las mismas tools con los mismos esquemas que antes del refactor
#### Scenario: Sin cambios en dependencias ni pipeline
- **WHEN** se compara el estado del repositorio antes y después del refactor
- **THEN** `go.mod`, `go.sum`, `.gitea/workflows/release.yml` y el contrato de nombres de assets (`outline-mcp_{GOOS}_{GOARCH}`) permanecen inalterados
@@ -0,0 +1,36 @@
## 1. Extracción de dominios a archivos nuevos
- [ ] 1.1 Crear `client.go` con el tipo `OutlineClient`, `newOutlineClient` y el método `post` movidos íntegros desde `main.go` (mismos nombres, firmas, comentarios y directivas `//nolint`), ajustando solo el bloque `import`.
- **Given** el código del cliente en `main.go`
- **When** se traslada a `client.go` y se compila en el contenedor
- **Then** `go build ./...` compila sin errores y el archivo declara `package main`
- [ ] 1.2 Crear `updater.go` con `giteaRelease`, `parseVersion`, `versionNewer` y `doUpdate` movidos íntegros desde `main.go`, ajustando solo el bloque `import`.
- **Given** el código del auto-update en `main.go`
- **When** se traslada a `updater.go` y se compila en el contenedor
- **Then** `go build ./...` compila sin errores y `updater.go` no contiene referencias al cliente de Outline ni a los handlers MCP
- [ ] 1.3 Crear `tools.go` con los cuatro handlers MCP (`handleListCollections`, `handleSearch`, `handleGetDocument`, `handleCreateDocument`) movidos íntegros desde `main.go`, ajustando solo el bloque `import`.
- **Given** el código de los handlers en `main.go`
- **When** se traslada a `tools.go` y se compila en el contenedor
- **Then** `go build ./...` compila sin errores y los cuatro handlers conservan nombre y firma
## 2. Reducción del entrypoint
- [ ] 2.1 Reducir `main.go` a la función `main()`, las variables de build (`Version`, `GiteaURL`, `RepoOwner`, `RepoName`) y el registro de tools, eliminando el código ya trasladado y los imports no utilizados.
- **Given** `main.go` tras las extracciones
- **When** se elimina el código duplicado/movido y se limpian los imports
- **Then** `main.go` contiene únicamente entrypoint + variables de build + registro de tools, y `go build ./...` compila sin errores
## 3. Verificación
- [ ] 3.1 Ejecutar `golangci-lint run` dentro del contenedor DevPod y corregir cualquier incidencia derivada del movimiento de código.
- **Given** el refactor completado
- **When** se ejecuta `golangci-lint run` en el contenedor
- **Then** el linter no reporta errores nuevos respecto al estado previo al refactor
- [ ] 3.2 Verificar que el binario se comporta igual: compilar con `-ldflags` de prueba y ejecutar handshake MCP + `tools/list` por stdio.
- **Given** el binario compilado a partir del código reorganizado
- **When** se envía por stdio el handshake `initialize` y una petición `tools/list`
- **THEN** el servidor responde correctamente y lista las mismas cuatro tools con los mismos esquemas que antes del refactor
- [ ] 3.3 Confirmar que no cambiaron `go.mod`, `go.sum`, `.gitea/workflows/release.yml` ni el contrato de nombres de assets.
- **Given** el repositorio con el refactor aplicado
- **When** se revisa `git diff` para esos archivos
- **Then** no aparecen modificaciones en ellos