Spaces:
Runtime error
Runtime error
feat(deploy): despliegue a Hugging Face Spaces (Dockerfile raíz + deploy_hf.ps1 + frontmatter)
Browse files- .env.example +15 -10
- Dockerfile +49 -0
- README.md +14 -0
- scripts/deploy_hf.ps1 +64 -0
.env.example
CHANGED
|
@@ -29,21 +29,26 @@ CONFIDENCE_THRESHOLD=0.25
|
|
| 29 |
MAX_UPLOAD_MB=50
|
| 30 |
|
| 31 |
# -----------------------------------------------------------------------------------
|
| 32 |
-
# 3) Despliegue
|
| 33 |
-
#
|
| 34 |
-
#
|
| 35 |
-
#
|
| 36 |
-
#
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
|
| 41 |
# -----------------------------------------------------------------------------------
|
| 42 |
-
#
|
| 43 |
-
#
|
|
|
|
|
|
|
| 44 |
# -----------------------------------------------------------------------------------
|
| 45 |
RENDER_API_KEY=
|
| 46 |
RENDER_SERVICE_ID=
|
|
|
|
|
|
|
|
|
|
| 47 |
|
| 48 |
# -----------------------------------------------------------------------------------
|
| 49 |
# 5) Modelo de conteo + Hugging Face Hub
|
|
|
|
| 29 |
MAX_UPLOAD_MB=50
|
| 30 |
|
| 31 |
# -----------------------------------------------------------------------------------
|
| 32 |
+
# 3) Despliegue -> Hugging Face Spaces (SDK Docker) [VÍA ACTIVA — ver docs/ejecucion.md §5]
|
| 33 |
+
# HF construye el Dockerfile de la raíz (gateway FastAPI: UI / + API /api).
|
| 34 |
+
# Token (write) en: https://huggingface.co/settings/tokens
|
| 35 |
+
# Crea el Space (una vez, web): https://huggingface.co/new-space -> SDK "Docker".
|
| 36 |
+
# Deploy: .\scripts\deploy_hf.ps1
|
| 37 |
+
# -----------------------------------------------------------------------------------
|
| 38 |
+
HF_SPACE_ID= # <usuario>/<space>, p. ej. alexprieto/agrovision
|
| 39 |
+
# (HF_TOKEN se define en la sección 5; sirve tanto para el Space como para el modelo.)
|
| 40 |
|
| 41 |
# -----------------------------------------------------------------------------------
|
| 42 |
+
# 3b) Alternativas de despliegue (opcionales):
|
| 43 |
+
# - Render (Docker, free): backend/Dockerfile + render.yaml. API en dashboard.render.com/u/settings
|
| 44 |
+
# - Posit Connect (de pago): rsconnect deploy fastapi (scripts/deploy_prod.ps1).
|
| 45 |
+
# OJO: shinyapps.io NO hospeda FastAPI (solo Shiny); estas vars solo aplican a Posit Connect.
|
| 46 |
# -----------------------------------------------------------------------------------
|
| 47 |
RENDER_API_KEY=
|
| 48 |
RENDER_SERVICE_ID=
|
| 49 |
+
SHINYAPPS_ACCOUNT=
|
| 50 |
+
SHINYAPPS_TOKEN=
|
| 51 |
+
SHINYAPPS_SECRET=
|
| 52 |
|
| 53 |
# -----------------------------------------------------------------------------------
|
| 54 |
# 5) Modelo de conteo + Hugging Face Hub
|
Dockerfile
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# =====================================================================================
|
| 2 |
+
# Dockerfile para Hugging Face Spaces (SDK: docker) — gateway AgroVisión.
|
| 3 |
+
# HF construye ESTE archivo (debe estar en la raíz del repo) y lo corre como el
|
| 4 |
+
# usuario 1000 (HOME=/home/user). El gateway FastAPI sirve la UI Astro compilada en /
|
| 5 |
+
# y la API en /api. Puerto: 8000 (declarado en README.md con `app_port: 8000`).
|
| 6 |
+
#
|
| 7 |
+
# Multi-stage: (1) Node compila Astro; (2) Python/uv corre el gateway con el build
|
| 8 |
+
# de Astro copiado a backend/static. BYOK: sin secretos en la imagen (las llaves de
|
| 9 |
+
# datos las pone el usuario por sesión vía cabeceras X-User-*).
|
| 10 |
+
#
|
| 11 |
+
# Local/Render usan backend/Dockerfile; este es el equivalente endurecido para HF.
|
| 12 |
+
# =====================================================================================
|
| 13 |
+
|
| 14 |
+
# --- Stage 1: build de la UI Astro ---
|
| 15 |
+
FROM node:22-bookworm-slim AS ui
|
| 16 |
+
WORKDIR /ui
|
| 17 |
+
RUN corepack enable
|
| 18 |
+
COPY frontend/package.json frontend/pnpm-lock.yaml frontend/pnpm-workspace.yaml ./
|
| 19 |
+
RUN pnpm install --frozen-lockfile
|
| 20 |
+
COPY frontend/ ./
|
| 21 |
+
RUN pnpm build
|
| 22 |
+
|
| 23 |
+
# --- Stage 2: backend FastAPI (CPU), como usuario no-root (requisito de HF Spaces) ---
|
| 24 |
+
FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim
|
| 25 |
+
|
| 26 |
+
# HF Spaces ejecuta el contenedor como UID 1000: crear el usuario ANTES de copiar.
|
| 27 |
+
RUN useradd -m -u 1000 user
|
| 28 |
+
ENV HOME=/home/user \
|
| 29 |
+
PATH=/home/user/.local/bin:$PATH \
|
| 30 |
+
UV_PROJECT_ENVIRONMENT=/home/user/app/.venv \
|
| 31 |
+
UV_LINK_MODE=copy \
|
| 32 |
+
UV_CACHE_DIR=/home/user/.cache/uv \
|
| 33 |
+
COUNTING_ENABLED=false \
|
| 34 |
+
EVENTS_PERSIST=false
|
| 35 |
+
|
| 36 |
+
WORKDIR /home/user/app
|
| 37 |
+
|
| 38 |
+
# Dependencias (capa cacheable) — desde el lockfile, sin dev.
|
| 39 |
+
COPY --chown=user pyproject.toml uv.lock ./
|
| 40 |
+
USER user
|
| 41 |
+
RUN uv sync --no-dev --frozen
|
| 42 |
+
|
| 43 |
+
# Código + UI Astro compilada (servida por el gateway en /).
|
| 44 |
+
COPY --chown=user backend/ ./backend/
|
| 45 |
+
COPY --chown=user --from=ui /ui/dist/ ./backend/static/
|
| 46 |
+
|
| 47 |
+
EXPOSE 8000
|
| 48 |
+
# --no-sync: usa el venv ya construido en el build (no re-resuelve en runtime).
|
| 49 |
+
CMD ["uv", "run", "--no-sync", "uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
README.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# AgroVisión — Plataforma de Monitoreo Agronómico
|
| 2 |
|
| 3 |
Plataforma de [AgroVisión](docs/reference/description_proyecto_agrovision.md) para monitoreo agronómico de precisión: **gestión de parcelas**, **teledetección NDVI** (Sentinel-2, 5 años), **agente conversacional (RAG)** y **conteo de plantas por dron** (este último **en desarrollo**). Backend **FastAPI** (monolito modular), persistencia **Supabase (PostGIS) BYOK**, y UI en **migración de Shiny → Astro + Tailwind** (Agro-Stack).
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: AgroVisión
|
| 3 |
+
emoji: 🫐
|
| 4 |
+
colorFrom: green
|
| 5 |
+
colorTo: blue
|
| 6 |
+
sdk: docker
|
| 7 |
+
app_port: 8000
|
| 8 |
+
pinned: false
|
| 9 |
+
license: agpl-3.0
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
<!-- ↑ Frontmatter requerido por Hugging Face Spaces (SDK Docker). En GitHub se ve
|
| 13 |
+
como una tabla. El runbook de despliegue está en docs/ejecucion.md §5. -->
|
| 14 |
+
|
| 15 |
# AgroVisión — Plataforma de Monitoreo Agronómico
|
| 16 |
|
| 17 |
Plataforma de [AgroVisión](docs/reference/description_proyecto_agrovision.md) para monitoreo agronómico de precisión: **gestión de parcelas**, **teledetección NDVI** (Sentinel-2, 5 años), **agente conversacional (RAG)** y **conteo de plantas por dron** (este último **en desarrollo**). Backend **FastAPI** (monolito modular), persistencia **Supabase (PostGIS) BYOK**, y UI en **migración de Shiny → Astro + Tailwind** (Agro-Stack).
|
scripts/deploy_hf.ps1
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# scripts/deploy_hf.ps1 — Despliegue de AgroVisión a Hugging Face Spaces (SDK Docker).
|
| 2 |
+
#
|
| 3 |
+
# El Space es un repo git en huggingface.co. HF construye el `Dockerfile` de la raíz
|
| 4 |
+
# (gateway FastAPI: UI Astro en / + API en /api) y lo sirve en el puerto `app_port`
|
| 5 |
+
# del README.md (8000). NO se compila ni empaqueta en local: solo se hace `git push`
|
| 6 |
+
# y HF construye en su lado.
|
| 7 |
+
#
|
| 8 |
+
# Requisitos (en .env, NO se versiona):
|
| 9 |
+
# HF_TOKEN=hf_xxx # token con permiso de escritura (huggingface.co/settings/tokens)
|
| 10 |
+
# HF_SPACE_ID=<usuario>/<space> # p. ej. alexprieto/agrovision (crea el Space ANTES, SDK=Docker)
|
| 11 |
+
#
|
| 12 |
+
# Crear el Space una vez (web): huggingface.co/new-space -> SDK "Docker" -> Blank.
|
| 13 |
+
# BYOK: no se ponen secretos de datos en el Space; el usuario pega sus llaves por sesión.
|
| 14 |
+
#
|
| 15 |
+
# Uso: .\scripts\deploy_hf.ps1
|
| 16 |
+
param(
|
| 17 |
+
[string]$SpaceId = "", # si se omite, se toma de HF_SPACE_ID (.env)
|
| 18 |
+
[switch]$Force # fuerza el push (sobrescribe el historial del Space)
|
| 19 |
+
)
|
| 20 |
+
$ErrorActionPreference = "Stop"
|
| 21 |
+
$root = Split-Path -Parent $PSScriptRoot
|
| 22 |
+
Set-Location $root
|
| 23 |
+
|
| 24 |
+
Write-Host "=== Deploy AgroVisión -> Hugging Face Spaces ===" -ForegroundColor Cyan
|
| 25 |
+
|
| 26 |
+
# Cargar HF_TOKEN / HF_SPACE_ID del .env (sin pisar lo que ya exista en la sesión).
|
| 27 |
+
if (Test-Path ".env") {
|
| 28 |
+
Get-Content ".env" | ForEach-Object {
|
| 29 |
+
if ($_ -match '^\s*(HF_TOKEN|HF_SPACE_ID)\s*=\s*(.+)$') {
|
| 30 |
+
$k = $matches[1]; $v = $matches[2].Trim().Trim('"').Trim("'")
|
| 31 |
+
if ($v -and -not [Environment]::GetEnvironmentVariable($k)) { Set-Item "env:$k" $v }
|
| 32 |
+
}
|
| 33 |
+
}
|
| 34 |
+
}
|
| 35 |
+
if (-not $SpaceId) { $SpaceId = $env:HF_SPACE_ID }
|
| 36 |
+
|
| 37 |
+
if (-not $env:HF_TOKEN) { Write-Error "Falta HF_TOKEN en .env (token de escritura de Hugging Face)."; exit 1 }
|
| 38 |
+
if (-not $SpaceId -or $SpaceId -notmatch '^[^/]+/[^/]+$') {
|
| 39 |
+
Write-Error "Falta HF_SPACE_ID válido (formato <usuario>/<space>). Pon HF_SPACE_ID en .env o pasa -SpaceId."
|
| 40 |
+
exit 1
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
# El dueño del Space (parte antes de '/') hace de usuario en la URL autenticada.
|
| 44 |
+
$owner = $SpaceId.Split("/")[0]
|
| 45 |
+
$remote = "https://${owner}:$($env:HF_TOKEN)@huggingface.co/spaces/$SpaceId"
|
| 46 |
+
|
| 47 |
+
# Aviso: el Space debe existir (SDK Docker). El primer push suele necesitar -Force
|
| 48 |
+
# porque el Space arranca con un commit inicial (README) distinto a nuestro historial.
|
| 49 |
+
Write-Host "Space: huggingface.co/spaces/$SpaceId (rama main)" -ForegroundColor Yellow
|
| 50 |
+
Write-Host "Empujando el repo (HEAD -> main); HF construirá el Dockerfile..." -ForegroundColor Yellow
|
| 51 |
+
|
| 52 |
+
$pushArgs = @("push")
|
| 53 |
+
if ($Force) { $pushArgs += "--force" }
|
| 54 |
+
$pushArgs += @($remote, "HEAD:main")
|
| 55 |
+
|
| 56 |
+
# Nota: el token va en la URL solo para este push (no se guarda como remote).
|
| 57 |
+
& git @pushArgs
|
| 58 |
+
if ($LASTEXITCODE -ne 0) {
|
| 59 |
+
Write-Warning "El push falló. Si es el primer deploy, reintenta con: .\scripts\deploy_hf.ps1 -Force"
|
| 60 |
+
exit 1
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
Write-Host "`n=== Push completado. Sigue el build en huggingface.co/spaces/$SpaceId ===" -ForegroundColor Green
|
| 64 |
+
Write-Host "Cuando termine, abre la URL del Space: la UI carga en / y la API en /api." -ForegroundColor Green
|