# CLI, bootstrap e infraestructura

Referencia de cómo funciona el ecosistema Boogiepop para **crear proyectos nuevos**: qué hace la CLI, qué ejecuta el Bootstrap MS, qué recursos AWS crea Terraform y cómo mantenerlo **barato y predecible**.

---

## Arquitectura (vista rápida)

```mermaid
flowchart LR
  subgraph local ["Tu máquina"]
    CLI["boogiepop CLI\nboogiepop new"]
    CFG["~/.boogiepop/bootstrap.json"]
  end

  subgraph ms ["Bootstrap MS\nNest · puerto 3100"]
    API["POST /bootstrap/projects\nSSE progreso"]
    CORE["boogiepop-bootstrap-core\nrunProjectBootstrap()"]
  end

  subgraph gh ["GitHub"]
    REPO["Repo nuevo\nboogiepop-&lt;nombre&gt;"]
    GHA["GitHub Actions\ndeploy.yml generado"]
  end

  subgraph aws ["AWS · boogiepop-infra"]
    TF["Terraform\nscaffold + apply -target"]
    ALB["ALB compartido\npath + host"]
    ECS["ECS Fargate ARM64\n1 task por app"]
    ECR["ECR por app"]
    S3["S3 bucket app"]
    SM["Secrets Manager"]
  end

  CLI --> CFG
  CLI -->|HTTP + API key| API
  API --> CORE
  CORE -->|terraform apply| TF
  CORE -->|create + push| REPO
  TF --> ALB & ECS & ECR & S3 & SM
  REPO --> GHA
  GHA -->|OIDC push image| ECR
  GHA -->|force-new-deployment| ECS
```

**Principio:** la CLI **no** tiene tu token de GitHub ni credenciales AWS. Solo habla con el Bootstrap MS. El MS usa `GITHUB_TOKEN`, corre Terraform en `boogiepop-infra/terraform` y hace push al repo creado.

---

## Comandos del CLI

| Comando | Rol |
|---------|-----|
| `boogiepop bootstrap config` | Guarda URL del MS y API key en `~/.boogiepop/bootstrap.json` |
| `boogiepop new` | Wizard: nombre, tipo, visibilidad, infra AWS, colaboradores → llama al MS |
| `boogiepop new --dry-run` | Muestra el JSON que se enviaría, sin crear nada |
| `boogiepop new --no-clone` | Crea repo/infra pero no clona en el directorio actual |
| `boogiepop register-app [name]` | Registra en DB una app **ya creada** (pública, `hubVisible=false`) |
| `boogiepop publish [name]` | Muestra la app en el hub (`hubVisible=true`) |
| `boogiepop hide [name]` | Oculta la app del hub (`hubVisible=false`) |
| `boogiepop config` | Configura seed para **updates** en un proyecto existente (`boogiepop.json`) |
| `boogiepop update` / `version` / `versions` / `abort` | Ciclo de vida del seed en repos ya creados (no crean infra) |

Los comandos de update viven en el README principal; este doc se centra en **`boogiepop new`**.

---

## Setup único (antes del primer `new`)

### 1. Bootstrap MS

```bash
cd boogiepop-bootstrap-core && npm install && npm run build
cd ../boogiepop-bootstrap
cp .env.example .env   # completar variables
npm install && npm run start:dev
```

Variables críticas en `.env` del MS:

| Variable | Descripción |
|----------|-------------|
| `GITHUB_TOKEN` | PAT con `repo`, acceso a las orgs destino |
| `BOOTSTRAP_API_KEY` | Clave que la CLI envía en `Authorization: Bearer …` |
| `GITHUB_ORG_BOOGIEPOP` | Org para **next** (ej. `blanck1945`) |
| `GITHUB_ORG_REMOTES` | Org para **streamlit** y **vite** hub apps (ej. `Boogiepop-remotes`) |
| `INFRA_TERRAFORM_DIR` | Ruta absoluta a `boogiepop-infra/terraform` |
| `BOOGIEPOP_API_URL` | Base del backend Nest (ej. `http://localhost:3000` o `https://boogiepop.cloud`) |
| `BOOGIEPOP_ADMIN_EMAIL` | Usuario admin con workspace `admin` (para `POST /api/applications`) |
| `BOOGIEPOP_ADMIN_PASSWORD` | Password del admin |

En la máquina del MS deben existir **`terraform`**, **`git`**, credenciales **AWS** válidas y acceso al **remote state** de Terraform (GitLab HTTP backend en `versions.tf`).

### 2. CLI

```bash
cd boogiepop-cli && npm run build
boogiepop bootstrap config --url http://localhost:3100 --key <BOOTSTRAP_API_KEY>
```

Tras cambiar templates en `boogiepop-bootstrap-core`, siempre: `npm run build` en core → `npm install` en bootstrap → **reiniciar el MS**.

### 3. Plataforma compartida (`boogiepop-infra`)

Debe existir **antes** el stack base (una sola vez por cuenta/región):

- VPC + subnets públicas (**sin NAT** → ahorro)
- ALB HTTP/HTTPS + certificado ACM
- Cluster ECS + roles IAM (ejecución, task, **OIDC GitHub Actions**)
- Backend API Nest (opcional según tfvars)

Cada app nueva **no** crea un ALB ni un cluster: **reutiliza** el existente y agrega recursos incrementales.

---

## `boogiepop new` — flujo paso a paso

### Lo que ve el usuario (CLI)

1. Pregunta **tipo**, **visibilidad** y **¿crear infra AWS?**
2. Pregunta **nombre** — si ya existe repo, carpeta local o infra (con infra = Sí), **muestra el motivo y vuelve a pedir otro nombre** (sin abortar con error)
3. Colaboradores GitHub opcionales
4. Muestra resumen y confirma
5. Progreso en tiempo real (SSE)

Comprobaciones de nombre (`GET /bootstrap/projects/availability`):

| Conflicto | Cuándo |
|-----------|--------|
| Repo GitHub | Siempre |
| Carpeta `./<nombre>` local | Si va a clonar (no `--no-clone`) |
| Archivos `locals_<app>.tf` | Solo si **infra = Sí** |
| `enable_<app>_ecs = true` en tfvars | Solo si **infra = Sí** |

Con **infra = No**, puede reutilizar un slug que ya tenga Terraform (reintento tras apply OK sin repo).

### Lo que ejecuta el MS (`runProjectBootstrap`)

Orden **fijo** en `boogiepop-bootstrap-core/src/orchestrator/github-only.ts`:

| # | Paso | `provisionInfra = true` | `provisionInfra = false` |
|---|------|-------------------------|---------------------------|
| 1 | Validar token GitHub, org, ruta Terraform | ✓ | solo token/org |
| 2 | **Generar Terraform** — escribe `*_&lt;app&gt;.tf` en infra | ✓ | omitido |
| 3 | **Actualizar `terraform.tfvars`** — `enable_&lt;app&gt;_ecs = true`, `&lt;app&gt;_app_env = {}` | ✓ | omitido |
| 4 | **`terraform apply -target=…`** — solo recursos de esa app | ✓ | omitido |
| 5 | Crear repo GitHub `&lt;org&gt;/boogiepop-&lt;nombre&gt;` | ✓ | ✓ |
| 6 | Clonar seed (`next` / `vite` / `streamlit`) | ✓ | ✓ |
| 7 | Transformar código (rename, workflow CI, Dockerfile next) | ✓ | ✓ |
| 8 | `npm install` + lockfile (next/vite) | ✓ | ✓ |
| 9 | Push inicial a `main` | ✓ | ✓ |
| 10 | **Registrar app en backend** (`POST /api/applications`, pública + `hubVisible=false`) | ✓* | ✓* |
| 11 | Invitar colaboradores | si hay | si hay |

\* Requiere `BOOGIEPOP_API_URL`, `BOOGIEPOP_ADMIN_EMAIL`, `BOOGIEPOP_ADMIN_PASSWORD` en el Bootstrap MS. Sin esas vars el paso se omite con aviso.

**Importante:** si el paso 4 falla, los pasos 5–10 **no** corren. Puede quedar Terraform generado en infra sin repo. Ver [Recuperación ante fallos](#recuperación-ante-fallos).

### Destinos GitHub por tipo

| Tipo | Org (env) | Seed clonado | Repo ejemplo |
|------|-----------|--------------|--------------|
| `next` | `GITHUB_ORG_BOOGIEPOP` | `boogiepop-next-seed` | `blanck1945/boogiepop-my-app` |
| `streamlit` | `GITHUB_ORG_REMOTES` | `boogiepop-streamlit-seed` | `Boogiepop-remotes/boogiepop-my-app` |
| `vite` | `GITHUB_ORG_REMOTES` | `boogiepop-react-seed` | `Boogiepop-remotes/boogiepop-my-remote` |

El slug del repo siempre es `boogiepop-&lt;nombre-normalizado&gt;`. La carpeta local suele ser solo el nombre (`my-app`).

---

## `boogiepop register-app` / `publish` / `hide`

**Default: API desplegada** (`https://boogiepop.cloud`), sin Bootstrap MS local.

```bash
boogiepop platform config   # una vez: URL + admin email/password → ~/.boogiepop/platform.json

cd remotes/argentina-temperature
boogiepop register-app -y
boogiepop publish -y
```

| Cómo se elige la app | Prioridad |
|----------------------|-----------|
| Argumento `[name]` | Siempre gana |
| Repo actual (cwd) | `git remote origin` → `package.json` → carpeta |
| Interactivo | Pregunta slug/tipo si no hay proyecto |

| Comportamiento | Detalle |
|----------------|---------|
| **Destino** | `POST/PATCH /api/applications` en la plataforma |
| **URLs** | Convención `https://boogiepop.cloud/<slug>/` (sin Terraform local) |
| **`--via-bootstrap`** | Ruta legacy: Bootstrap MS + Terraform en el servidor |

`boogiepop new` sigue requiriendo Bootstrap MS (`boogiepop bootstrap config`).

---

## Cómo se crea la infra (Terraform)

### Generación de archivos

El MS renderiza templates desde `boogiepop-bootstrap-core/templates/terraform/&lt;tipo&gt;/` y escribe en `boogiepop-infra/terraform/`:

| Archivo generado | Contenido |
|------------------|-----------|
| `variables_&lt;tf_name&gt;.tf` | Flags, CPU/memoria, puerto, path HTTP, dominio opcional |
| `locals_&lt;tf_name&gt;.tf` | Paths ALB, health check, URLs embed, secrets ECS |
| `ecr_&lt;tf_name&gt;.tf` | Repositorio ECR + lifecycle (conserva últimas imágenes) |
| `ecs_&lt;tf_name&gt;.tf` | Log group, task definition Fargate **ARM64**, ECS service |
| `alb_&lt;tf_name&gt;.tf` | Target group + reglas listener HTTP y HTTPS |
| `s3_&lt;tf_name&gt;.tf` | Bucket dedicado de la app |
| `secrets_&lt;tf_name&gt;.tf` | Secret Secrets Manager + permiso al execution role |
| `iam_&lt;tf_name&gt;.tf` | Task role con acceso al bucket S3 |

`&lt;tf_name&gt;` = nombre en snake_case (ej. `argentina_temperature`).

Placeholders clave (`buildScaffoldVars`):

| Placeholder | Ejemplo | Uso |
|-------------|---------|-----|
| `HTTP_PATH` | `my-app` | Path ALB `/my-app/*` |
| `CONTAINER_PORT` | `8080` (next/vite) · `8501` (streamlit) | Puerto contenedor |
| `ALB_PRIORITY_HTTP` / `HTTPS` | auto | Siguiente prioridad libre en listeners |
| `ECR_REPO` | `boogiepop-my-app` | Nombre repo ECR |
| `ECS_SERVICE` | `boogiepop-api-fe-my-app-svc` | Nombre servicio ECS |

### `terraform apply` acotado (eficiencia)

No se corre un `apply` completo del stack. Solo targets de la app nueva (`buildTerraformApplyTargets`):

```
aws_ecr_repository.*
aws_ecr_lifecycle_policy.*
aws_cloudwatch_log_group.*
aws_iam_role.*_task + policy S3
aws_s3_bucket.* (+ versioning, encryption, public access block)
aws_secretsmanager_secret.* (+ version + policy execution)
aws_lb_target_group.*
aws_lb_listener_rule.http_*
aws_lb_listener_rule.https_*    ← regla HTTPS en boogiepop.cloud
aws_ecs_task_definition.*
aws_ecs_service.*
```

Ventajas:

- **Rápido** — no replanifica todo el estado
- **Seguro** — no toca servicios ajenos
- **Barato en operación** — un apply acotado por app

### Routing ALB (HTTPS producción)

Con `enable_https = true` y `frontend_host_domain = "boogiepop.cloud"` (en `terraform.tfvars` global):

- Regla HTTPS: **host** `boogiepop.cloud` + **path** `/&lt;app&gt;/*`
- Health check con prefijo de path (no `/health` suelto en el listener)
- Dominio por app (`frontend_&lt;app&gt;_domain`) es **opcional**; si es `null`, se usa `frontend_host_domain` vía `coalesce(...)`

URL pública típica: `https://boogiepop.cloud/&lt;app&gt;/`

### Diferencias por template

| | Vite (MF) | Next.js | Streamlit |
|---|-----------|---------|-----------|
| Puerto | 8080 | 8080 | 8501 |
| Health ALB | `/&lt;path&gt;/health` | `/&lt;path&gt;/health` | `/&lt;path&gt;/_stcore/health` |
| Env clave | `VITE_REMOTE_BASE` | `NEXT_PUBLIC_BASE_PATH` | `STREAMLIT_SERVER_BASE_URL_PATH` |
| Org GitHub | remotes | boogiepop | boogiepop |
| Deploy CI | buildx ARM64 + ECR + ECS | igual | imagen Python + ECS |

---

## Coste: defaults y buenas prácticas

Diseño orientado a **mínimo coste incremental por app**:

| Decisión | Por qué |
|----------|---------|
| **Un ALB compartido** | No hay ALB nuevo por proyecto (~$16/mes ahorrados c/u) |
| **Sin NAT Gateway** | Subnets públicas; tasks con IP pública |
| **Fargate ARM64** | Graviton suele ser más barato que x86 |
| **1 task por defecto** (`desired_count = 1`) | Sin HA multi-AZ en POC |
| **512 CPU / 1024 MiB** por app | Suficiente para seeds; bajar a 256/512 si la app es liviana |
| **Logs 14 días** | Retención acotada en CloudWatch |
| **ECR lifecycle** | Expira imágenes viejas (no llenar storage) |
| **S3 app bucket** | Solo la app; sin CloudFront salvo dashboard |
| **Apply `-target`** | Menos riesgo de cambios accidentales en prod |

### Almacenamiento S3 (upload / list / download)

Las apps con bucket (bootstrap con infra) usan el **API central** (`boogiepop-backend`, módulo `storage`), no credenciales AWS en el frontend.

- Guía completa: **[APP-STORAGE.md](./APP-STORAGE.md)**
- Contrato backend: **`boogiepop-backend/AGENTS.md`**
- Referencia UI: repo **`boogiepop-sql-validator`**

Cada app nueva solo necesita su **`applicationSlug`** (= `HTTP_PATH`) y cliente HTTP; el IAM global del API (`ecs_task_app_storage`) ya cubre `boogiepop-*-{account}/uploads/*`.

Requisitos globales en `terraform.tfvars`:

```hcl
enable_frontend_ecs = true   # obligatorio para que ecs_ready de apps nuevas sea true
```

Para POC sin TLS: `enable_https = false` (reglas solo en listener :80). Producción Boogiepop usa HTTPS en el ALB compartido.

---

## Después del bootstrap: CI/CD

El MS genera `.github/workflows/deploy.yml` en el repo nuevo:

- **OIDC** → role `boogiepop-api-github-actions` (sin access keys en GitHub)
- Build **linux/arm64** con `docker/build-push-action` (no `docker buildx` crudo en shell)
- Push a ECR `boogiepop-&lt;app&gt;`
- `aws ecs update-service --force-new-deployment`

**Primer deploy real:** push a `main` con código que buildee (imagen placeholder hasta entonces).

**Hub manifest:** no se publica automáticamente. Solo si el equipo lo pide explícitamente.

---

## Recuperación ante fallos

| Situación | Qué pasó | Qué hacer |
|-----------|----------|-----------|
| Falló `terraform apply` | Archivos `.tf` + tfvars actualizados; repo **no** creado | Corregir error (ver abajo), `terraform apply -target=…` manual o repetir `boogiepop new` con **infra No** si ya aplicaste a mano |
| Falló después del repo | Repo existe; infra puede estar incompleta | Completar infra manualmente o re-ejecutar apply; clonar repo desde GitHub |
| Mismo nombre, otro tipo | Colisión de `tf_name` (vite vs streamlit) | **No reutilizar** el mismo slug; limpiar infra vieja o usar nombre distinto |
| Carpeta local ya existe | CLI aborta antes de llamar al MS | Vaciar carpeta o usar `--no-clone` |

### Errores Terraform frecuentes

| Error | Causa | Fix |
|-------|-------|-----|
| `trimspace(null)` / dominio HTTPS | Template viejo sin `coalesce(..., frontend_host_domain)` | Rebuild `bootstrap-core` + reiniciar MS |
| `Target Group already exists` | Apply parcial o cambio de puerto/tipo (vite 8080 → streamlit 8501) con `name` fijo en TG | Templates usan `name_prefix` (máx. 6 chars); re-aplicar. Si persiste: `terraform state rm 'aws_lb_target_group.<tf_name>[0]'` + borrar TG huérfano en AWS |
| `Nombre no disponible` / repo o infra ya existen | Mismo slug usado antes (vite/streamlit) o apply parcial | La CLI vuelve a pedir otro nombre antes de crear; con infra existente usá **infra No** o elegí nombre nuevo |
| `enable_frontend_ecs` false | App no despliega ECS | Poner `enable_frontend_ecs = true` en tfvars global |
| Prioridad ALB duplicada | Conflicto manual en `alb_*.tf` | Ajustar `priority` en regla HTTPS (prioridades son por listener) |

Para diagnóstico manual:

```bash
cd boogiepop-infra/terraform
terraform plan -target='aws_ecs_service.<tf_name>'
```

---

## API del Bootstrap MS (referencia)

| Método | Ruta | Descripción |
|--------|------|-------------|
| GET | `/bootstrap/meta/targets` | Orgs por tipo |
| GET | `/bootstrap/meta/orgs` | Orgs visibles con el PAT |
| GET | `/bootstrap/projects/availability?name=&type=&provisionInfra=` | Comprueba repo + infra antes de crear |
| POST | `/bootstrap/projects` | Crear proyecto (SSE) |

Eventos SSE:

```
event: step
data: {"step":"Terraform apply","status":"running","detail":"..."}

event: done
data: {"repoName":"...","htmlUrl":"...","infraApplied":true,"albPath":"/my-app/","ecsService":"...","ecrRepository":"..."}

event: error
data: {"message":"..."}
```

---

## Checklist “debería funcionar”

- [ ] MS corriendo con `.env` completo y `bootstrap-core` rebuild reciente
- [ ] `boogiepop bootstrap config` apunta al MS correcto
- [ ] AWS creds + Terraform init OK en `boogiepop-infra/terraform`
- [ ] `enable_frontend_ecs = true` y HTTPS configurado si usás producción
- [ ] Org GitHub del PAT coincide con `GITHUB_ORG_*`
- [ ] Nombre de proyecto **único** (sin infra previa de otro tipo)
- [ ] Push a `main` del repo nuevo para primera imagen ECS

---

## Repos relacionados

| Repo | Rol |
|------|-----|
| `boogiepop-cli` | Cliente interactivo |
| `boogiepop-bootstrap` | Microservicio Nest |
| `boogiepop-bootstrap-core` | Orquestación + templates Terraform/workflows |
| `boogiepop-infra` | Estado Terraform compartido |
| `boogiepop-*-seed` | Templates de código fuente |
