# Bitácora de instalación y resolución de incidencias

Este documento registra incidencias encontradas durante la preparación real del servidor FourZ. Complementa el runbook; no sustituye los procedimientos de respaldo, despliegue y rollback.

## Reglas permanentes

- Ejecutar instalaciones y cambios IIS desde PowerShell como Administrador.
- Antes de `-Apply`, ejecutar siempre el modo de planeación.
- No copiar `bin`, `obj`, `.vs` ni artefactos NuGet al mover el proyecto; se regeneran al compilar.
- Para desplegar Control Center, publicar sólo el resultado de `dotnet publish` hacia `C:\inetpub\wwwroot\controlcenter`.
- Nunca guardar PATs, tokens ni contraseñas en JSON, documentación, capturas o repositorios. Si un secreto se expone, revocarlo y crear uno nuevo.

## Incidencias y resolución

### Build Tools y TypeScript

**Síntoma:** el rol BuildAgent mostraba `TypeScript 2.3 build targets Missing`.

**Aprendizaje:** los Web Build Tools no siempre incluyen los targets históricos requeridos por el código KidZania. Validar con un build real del proyecto, no sólo con la existencia del ejecutable MSBuild.

### Java y Jenkins

**Síntoma:** Java se instaló, pero `java` no era reconocido en la consola.

**Resolución:** detectar el JDK instalado, definir `JAVA_HOME` de máquina y agregar `JAVA_HOME\bin` a `Path`; abrir una nueva consola y verificar con `java -version`.

### Visual Studio Build Tools

**Síntoma:** el instalador devolvía código 1.

**Resolución:** ejecutar desde consola elevada, revisar el log del instalador y usar argumentos de workload MSBuild/Web Build Tools. No asumir que una instalación parcial incluye todos los componentes heredados.

### Hosting Bundle incorrecto

**Síntoma:** IIS devolvía `HTTP 500.31 - Failed to load ASP.NET Core runtime`.

**Causa:** el archivo llamado `DotNet8HostingBundle.exe` reportó versión 10.0.10. La aplicación Control Center compila para `net8.0` y requiere `Microsoft.AspNetCore.App 8.x`.

**Resolución:** reemplazar el instalador por un ASP.NET Core Hosting Bundle 8.0.x aprobado, instalarlo, reiniciar Windows y confirmar:

```powershell
dotnet --list-runtimes
.\ServerBootstrap\Scripts\Test-ServerReadiness.ps1 -Role IisServer
```

### IIS: 404 al acceder por IP

**Síntoma:** `http://10.1.2.62/` devolvía `404 Not Found` de IIS.

**Causa:** la petición entraba a `Default Web Site`, no a `FourZ.ControlCenter`.

**Resolución:** crear el binding IP `10.1.2.62:80:` para `FourZ.ControlCenter`, detener sólo el sitio IIS predeterminado si no hospeda otra aplicación y validar con `Get-WebBinding -Name FourZ.ControlCenter`.

### IIS: 403.14 al acceder a Control Center

**Síntoma:** IIS mostraba `403.14 Forbidden` y la ruta física era `C:\inetpub\wwwroot\controlcenter`.

**Causa:** IIS llegaba al sitio correcto, pero la carpeta no contenía la publicación ASP.NET Core.

**Resolución:** publicar el proyecto a la ruta física. Deben existir `web.config`, `FourZ.ControlCenter.Web.dll`, `appsettings.json` y `Config\topology.production.json`.

### IIS: 500.30 al iniciar Control Center

**Síntoma:** `HTTP 500.30 - ASP.NET Core app failed to start`.

**Causa real encontrada:** `appsettings.json` tenía JSON inválido. El Event Viewer indicó la línea y columna de la falla.

**Diagnóstico:**

```powershell
Get-WinEvent -FilterHashtable @{ LogName = 'Application'; StartTime = (Get-Date).AddMinutes(-15) } |
  Where-Object { $_.ProviderName -match 'AspNetCore|IIS' } |
  Select-Object -First 10 TimeCreated, ProviderName, Id, LevelDisplayName, Message |
  Format-List

Get-Content C:\inetpub\wwwroot\controlcenter\appsettings.json -Raw |
  ConvertFrom-Json | Out-Null
```

**Resolución:** restaurar un `appsettings.json` válido desde el proyecto, validar JSON y reiniciar el App Pool.

### Settings no puede guardar

**Síntoma:** la pantalla Settings devuelve error al guardar.

**Causa:** la identidad `IIS AppPool\FourZ.ControlCenter` sólo tenía permiso de lectura sobre `appsettings.json`.

**Resolución:** otorgar Modify solamente a ese archivo, no a toda la publicación:

```powershell
$appPoolIdentity = 'IIS AppPool\FourZ.ControlCenter'
$settingsFile = 'C:\inetpub\wwwroot\controlcenter\appsettings.json'
icacls $settingsFile /grant "${appPoolIdentity}:(M)"
iisreset /restart
```

### Importación Azure DevOps → Gitea no disponible

**Causa frecuente:** se colocó el valor secreto directamente en `AzureDevOpsPatEnvironmentVariable` o `GiteaTokenEnvironmentVariable`.

**Configuración correcta:** esos campos guardan nombres, no secretos:

```json
"AzureDevOpsPatEnvironmentVariable": "FOURZ_AZURE_DEVOPS_PAT",
"GiteaTokenEnvironmentVariable": "FOURZ_GITEA_TOKEN"
```

Los valores reales se establecen como variables de entorno de máquina. Después de modificarlas, ejecutar `iisreset /restart` para que WAS y los App Pools reciban el entorno nuevo.

### Documentación en Control Center

Los documentos se leen desde `C:\FourZ.Deployment\docs`. Al copiar el proyecto completo al servidor, incluir esa carpeta; de lo contrario, Documentation no podrá abrir los archivos.

### Jenkins: 403 `No valid crumb was included`

**Sintoma:** al crear `FourZ-Deployment`, Jenkins responde `403 No valid crumb was included in the request` en `/createItem`.

**Causa:** Jenkins usa proteccion CSRF. El crumb se obtuvo en una sesion HTTP distinta de aquella que intento crear el job.

**Resolucion:** usar la version actual de `ServerBootstrap\Scripts\New-JenkinsDeploymentJob.ps1`; conserva la sesion HTTP al validar el usuario, pedir el crumb y crear o actualizar el job.

```powershell
.\ServerBootstrap\Scripts\New-JenkinsDeploymentJob.ps1 `
  -JenkinsUrl 'http://localhost:8080' `
  -PromptForCredential
```

Si el error cambia a `401`, el usuario o API Token de Jenkins no es valido. Si autentica pero no permite crear, asigne `Overall/Read`, `Job/Create` y `Job/Configure` al usuario de Jenkins.

### Control Center: acciones sobre Windows Services

**Comportamiento:** la pantalla muestra si el servicio esta instalado, si esta `Running` y si el ZIP de su paquete ya existe. La instalacion usa el script de despliegue existente y no inventa rutas o nombres de servicio.

**Permisos:** si `Instalar`, `Iniciar`, `Detener` o `Reiniciar` devuelve acceso denegado, la identidad `IIS AppPool\FourZ.ControlCenter` no tiene permiso para administrar servicios. No otorgue permisos a todos los usuarios: configure una identidad de servicio administrativa restringida o delegue los permisos solamente a los servicios autorizados.

### Gitea bloquea el webhook hacia Control Center

**Sintoma:** la entrega muestra `webhook can only call allowed HTTP servers` y rechaza la IP privada de Control Center.

**Causa:** Gitea protege los webhooks contra SSRF. Su configuracion por defecto permite destinos externos, pero no loopback.

**Resolucion:** en el `app.ini` activo de Gitea agregar o ajustar:

```ini
[webhook]
ALLOWED_HOST_LIST = external,loopback,10.1.2.62
```

Reiniciar el servicio `gitea` y usar **Redelivery** sobre la entrega fallida. La IP debe coincidir exactamente con la URL configurada en `-ControlCenterWebhookUrl`. Si persiste el rechazo, confirme el archivo usado por el servicio antes de cambiar nada:

```powershell
Get-CimInstance Win32_Service -Filter "Name='gitea'" | Select-Object Name, PathName
```

Abra el `app.ini` indicado por `PathName`, no otro archivo de instalación o respaldo. El relay recibe el evento en Control Center y es el único componente que conoce las credenciales de Jenkins.

### Gitea: `context deadline exceeded` al entregar un webhook

**Causa:** Gitea espera la respuesta HTTP mientras Control Center consulta crumb CSRF y encola el job en Jenkins. Esa operación puede tardar más que el timeout del cliente de webhook.

**Resolución:** la versión actual de Control Center responde `202 Accepted` después de validar la firma y procesa la solicitud de Jenkins en una cola local de segundo plano. Publique la versión actualizada y haga un push nuevo. La entrega debe quedar exitosa en Gitea; confirme el job en la cola o historial de Jenkins.

### Jenkins: restore no encuentra `Microsoft.ApplicationInsights.Wcf`

**Respaldo permanente:** antes de depender del pipeline, ejecutar una sola vez `.\ServerBootstrap\Scripts\Save-ApplicationInsightsArtifact.ps1` mientras MyGet estÃ© disponible. Guarda el paquete y sus dependencias en `C:\FourZ.Deployment\Artifacts\NuGet\packages`, con un manifiesto SHA-256. El restore consulta esa carpeta primero; respaldarla junto con FourZ.Deployment.

**ConfiguraciÃ³n vigente:** `Config\NuGet.Config` usa primero `C:\FourZ.Deployment\Artifacts\NuGet\packages` y luego nuget.org. MyGet se usa Ãºnicamente durante la descarga inicial del artefacto, no durante cada build.

**SÃ­ntoma:** el build falla durante NuGet restore con `Unable to find version '0.28.0-build06820' of package 'Microsoft.ApplicationInsights.Wcf'`.

**Causa:** esa versiÃ³n preliminar no estÃ¡ en nuget.org; se publica en el feed de laboratorios de Application Insights. Esto no depende de la opciÃ³n `INITIALIZE_IIS`: esa casilla Ãºnicamente crea/configura IIS y extrae el ZIP base.

**ResoluciÃ³n:** FourZ.Deployment incluye `Config\NuGet.Config` con nuget.org y `https://www.myget.org/F/applicationinsights-sdk-labs/`. El script de build lo pasa explÃ­citamente a `nuget restore`, por lo que Jenkins no depende del perfil `systemprofile`. Copiar este archivo junto con los cambios de scripts al servidor y actualizar el Jenkinsfile antes de relanzar la liberaciÃ³n.

### Un solo backup por liberación

Jenkins delega el backup a `Deploy.ps1`; no debe ejecutar `Backup.ps1` inmediatamente antes porque generaría dos puntos de restauración distintos. `Deploy.ps1` crea el backup IIS, el espejo de archivos y `Backup.json` con el mismo identificador. En una falla restaura ese backup y después inicia el App Pool.

### Métricas locales del dashboard

El Dashboard obtiene disco, CPU y memoria física directamente del servidor Windows. CPU es un promedio local de 250 ms; memoria y disco cambian a advertencia a partir de 90 %, mientras CPU cambia a advertencia a partir de 85 %. Estas métricas no requieren agente ni exponen datos fuera de la máquina.

Redis se considera saludable solo cuando el servicio Windows esta en ejecucion **y** el puerto local `127.0.0.1:6379` acepta una conexion TCP. Esto evita mostrar un falso estado verde cuando el proceso existe pero no esta atendiendo el puerto.

## Checklist de salida

- [ ] `Test-ServerReadiness` saludable para BuildAgent, Platform e IisServer según corresponda.
- [ ] `dotnet --list-runtimes` incluye `Microsoft.AspNetCore.App 8.x`.
- [ ] Control Center responde por `http://10.1.2.62/`.
- [ ] `appsettings.json` es JSON válido y no contiene secretos.
- [ ] Gitea y Jenkins están en ejecución.
- [ ] Los bindings IIS de Web, WCF y Control Center son correctos.
- [ ] La bitácora se actualiza con cualquier nueva incidencia relevante.

### Webhooks durables de Gitea

Control Center persiste cada entrega de Gitea en `C:\FourZ.Deployment\Queue\GiteaRelease` antes de responder `202 Accepted`. El registro conserva los estados `Pending`, `Processing`, `Completed` y `Failed`; al iniciar el App Pool recupera las entregas pendientes o interrumpidas. La cabecera `X-Gitea-Delivery` evita que un redelivery dispare el mismo evento dos veces.

La identidad `IIS AppPool\FourZ.ControlCenter` requiere permiso **Modify** solamente sobre `C:\FourZ.Deployment\Queue\GiteaRelease` y `C:\FourZ.Deployment\Logs`; no sobre todo el directorio de despliegue.
