Forgejo Actions — CI/CD de SmallCountry¶
Sistema de integración continua y despliegue automatizado¶
Cada push a main con cambios en la documentación lanza un pipeline que compila, valida y despliega el sitio en Caddy. Todo corre dentro de una imagen Docker personalizada alojada en el registro OCI de Forgejo, sin depender de internet.
1. Arquitectura¶
flowchart LR
Push["git push<br/>rama main"] --> Forgejo["Forgejo instance<br/>forgejo-sc.duckdns.org"]
Forgejo --> Workflow["Busca workflow<br/>.forgejo/workflows/docs-ci.yml"]
Workflow --> Runner["Asigna tarea al<br/>casita-runner"]
Runner --> Docker["Arranca contenedor<br/>forgejo-sc.duckdns.org/smallcountry/ci-docs:latest"]
Docker --> Steps["Ejecuta 6 pasos<br/>Checkout → Build → Deploy → Links → Metadata → Huérfanas"]
Steps --> Deploy["Copia web/site/*<br/>al directorio servido por Caddy"]
Deploy --> Caddy["Caddy sirve el sitio<br/>https://tonatiuh.duckdns.org"]
Steps --> Report["Reporta resultado<br/>a Forgejo"]
| Componente | Dónde está | Qué hace |
|---|---|---|
| Forgejo | Docker (localhost:3000) |
Recibe pushes, asigna tareas al runner |
| Runner | /home/tonatiuh/forgejo/forgejo-runner |
Ejecuta las tareas en contenedores Docker |
| Config runner | /home/tonatiuh/forgejo/runner-data/config.yml |
Define cómo ejecuta los jobs |
| Registro runner | /home/tonatiuh/forgejo/runner-data/.runner |
Token y UUID que vinculan runner ↔ Forgejo |
| Imagen CI | forgejo-sc.duckdns.org/smallcountry/ci-docs:latest |
Contenedor con Python, MkDocs y lychee preinstalados |
| Workflow | .forgejo/workflows/docs-ci.yml |
Define los pasos del CI (versionado en git) |
| Scripts CI | scripts/validate_metadata.py, scripts/check_orphan_pages.py |
Validaciones ejecutadas en el CI |
2. El workflow (docs-ci.yml)¶
El archivo que define el pipeline está en .forgejo/workflows/docs-ci.yml. Es versionado en git, por lo que cualquier cambio que se le haga se guarda en el historial.
Contenido actual¶
name: Docs Validation & Deploy
on:
push:
branches:
- main
paths:
- 'docs/**'
- 'mkdocs.yml'
- 'overrides/**'
- '.forgejo/**'
pull_request:
paths:
- 'docs/**'
- 'mkdocs.yml'
- 'overrides/**'
- '.forgejo/**'
jobs:
validate:
runs-on: ubuntu-latest
container:
image: forgejo-sc.duckdns.org/smallcountry/ci-docs:latest
options: --volume /home/tonatiuh/SmallCountry/web:/web
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build MkDocs
run: properdocs build --strict
- name: Deploy to Caddy
run: |
rm -rf /web/site/*
cp -r site/* /web/site/
- name: Check links with lychee
continue-on-error: true
run: lychee --config lychee.toml --base https://tonatiuh.duckdns.org 'site/**/*.html' || echo "Link check incomplete"
- name: Validar metadatos
run: python3 /usr/local/bin/validate_metadata.py docs
- name: Buscar páginas huérfanas
run: python3 /usr/local/bin/check_orphan_pages.py
Los 6 pasos¶
| # | Paso | Comando | Falla si... |
|---|---|---|---|
| 1 | Checkout | actions/checkout@v4 |
El repo no es accesible |
| 2 | Build MkDocs | mkdocs build --strict |
Hay enlaces rotos internos, errores de compilación |
| 3 | Deploy a Caddy | rm -rf /web/site/* && cp -r site/* /web/site/ |
El volumen /web no está montado o valid_volumes no incluye el directorio |
| 4 | Lychee | lychee --config web/lychee.toml --base https://tonatiuh.duckdns.org 'web/site/**/*.html' |
(continue-on-error) Hay links externos rotos |
| 5 | Metadatos | python3 /usr/local/bin/validate_metadata.py web/docs |
Algún .md carece de status: o layer: válido |
| 6 | Huérfanas | python3 /usr/local/bin/check_orphan_pages.py |
Hay .md en docs/ no incluidos en el nav de mkdocs.yml |
Notas importantes¶
- El paso Deploy a Caddy monta el volumen
/home/tonatiuh/SmallCountry/webdentro del contenedor como/web. Esto permite copiar los archivos directamente desde el workspace al directorio servido por Caddy. Requiere que/home/tonatiuh/SmallCountry/webesté listado encontainer.valid_volumesdelconfig.ymldel runner (ya lo está). - El usuario
caddynecesita poder leer los archivos desplegados. Se añadió al grupotonatiuhpara ello:sudo usermod -aG tonatiuh caddy.
3. Imagen Docker personalizada¶
Para que el CI funcione sin internet, todo lo necesario está preinstalado en una imagen Docker alojada en el registro OCI de Forgejo.
Contenido de la imagen¶
| Componente | Versión | Propósito |
|---|---|---|
| Python | 3.12-slim | Base del contenedor |
| MkDocs | 1.6.1 | Generador del sitio estático |
| Material for MkDocs | 9.7.6 | Tema del sitio |
| lychee | 0.24.2 | Verificador de enlaces |
validate_metadata.py |
— | Valida frontmatter de los .md |
check_orphan_pages.py |
— | Detecta páginas no incluidas en el nav |
Dockerfile¶
FROM node:22-bookworm
RUN apt-get update -qq && apt-get install -y -qq curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir \
"mkdocs-material[imaging]" \
properdocs \
mkdocs-material==9.7.6 \
mkdocs-material-extensions==1.3.1 \
mkdocs-git-revision-date-localized-plugin==1.5.2 \
mkdocs-get-deps==0.2.2
RUN curl -sSfL https://github.com/lycheeverse/lychee/releases/latest/download/lychee-x86_64-unknown-linux-musl.tar.gz \
-o /tmp/lychee.tar.gz \
&& tar xzf /tmp/lychee.tar.gz -C /usr/local/bin/ --strip-components=1 lychee-x86_64-unknown-linux-musl/lychee \
&& rm /tmp/lychee.tar.gz
COPY scripts/validate_metadata.py /usr/local/bin/
COPY scripts/check_orphan_pages.py /usr/local/bin/
WORKDIR /workspace
Cómo construir y subir la imagen¶
# 1. Generar token en Forgejo:
# https://forgejo-sc.duckdns.org → Settings → Applications → Generate Token
# (permiso: packages:write o all)
# 2. Autenticarse en el registro OCI
docker login forgejo-sc.duckdns.org -u tonatiuh
# 3. Construir la imagen
docker build -t forgejo-sc.duckdns.org/smallcountry/ci-docs:latest web/
# 4. Subir al registro
docker push forgejo-sc.duckdns.org/smallcountry/ci-docs:latest
La imagen se almacena en el registro OCI interno de Forgejo, accesible solo desde la red local. No necesita conexión a internet ni a Docker Hub.
4. Cómo ver los resultados¶
Desde Forgejo (interfaz web)¶
Cada push en main con cambios en los path filters genera un nuevo run. Cada run muestra:
- ✅ Verde: todos los pasos pasaron
- ❌ Rojo: al menos un paso falló
- 🟡 Amarillo: el CI sigue corriendo
Haciendo clic en un run se despliegan los logs de cada paso.
Desde el API¶
# Último run
curl -s "https://forgejo-sc.duckdns.org/api/v1/repos/SmallCountry/infra-core/actions/runs?limit=1"
# Últimos 5 en main
curl -s "https://forgejo-sc.duckdns.org/api/v1/repos/SmallCountry/infra-core/actions/runs?branch=main&limit=5"
Desde el runner (localhost)¶
# Logs del runner
tail -100 /home/tonatiuh/forgejo/runner-data/runner.log
# Logs de jobs anteriores
cat /home/tonatiuh/forgejo/runner-data/runner.log.old
5. El runner (config.yml)¶
El runner es un binario que corre en la máquina y ejecuta los jobs en contenedores Docker. Su configuración está fuera del repositorio (contiene tokens secretos) en /home/tonatiuh/forgejo/runner-data/config.yml.
Contenido actual¶
log:
level: trace
job_level: debug
runner:
file: .runner
capacity: 1
envs: {}
env_file: .env
timeout: 3h
shutdown_timeout: 3h
insecure: false
fetch_timeout: 30s
fetch_interval: 2s
report_interval: 1s
labels:
- "docker:docker"
- "ubuntu-latest:docker"
cache:
enabled: true
port: 0
dir: ""
external_server: ""
secret: ""
secret_url: ""
host: ""
proxy_port: 0
actions_cache_url_override: ""
container:
network: "host"
enable_ipv6: false
privileged: true
options: "--dns 8.8.8.8 --dns 1.1.1.1"
workdir_parent: /home/tonatiuh/forgejo/runner-data/workspace
valid_volumes:
- /home/tonatiuh/forgejo/runner-data/workspace
- /home/tonatiuh/SmallCountry/web
docker_host: "automount"
force_pull: false
force_rebuild: false
host:
workdir_parent: /home/tonatiuh/forgejo/runner-data/workspace
server:
connections:
forgejo-instance:
url: https://forgejo-sc.duckdns.org
uuid: 0f9f24fd-0028-450d-9c97-203822b2d5bc
token: 0638064ed2427ef548ae9251668ee3474a0b7550
labels:
- "docker:docker"
- "ubuntu-latest:docker"
Parámetros importantes¶
| Clave | Valor actual | Significado |
|---|---|---|
capacity |
1 | Solo un job a la vez |
labels |
docker:docker, ubuntu-latest:docker |
Etiquetas que ofrece el runner |
container.privileged |
true |
Permite Docker-in-Docker dentro del contenedor de CI |
container.network |
host |
El contenedor comparte la red del host |
container.valid_volumes |
workspace + /home/tonatiuh/SmallCountry/web |
Directorios que el contenedor puede montar como volúmenes |
/home/tonatiuh/SmallCountry/webdebe estar envalid_volumespara que el paso "Deploy to Caddy" pueda montar el directorio de salida y copiar los archivos.
6. Cómo se lanza¶
Automático¶
El CI se lanza solo al hacer git push si los cambios tocan:
| Path | Ejemplos |
|---|---|
docs/** |
Cualquier página .md, imagen, o archivo en documentación |
mkdocs.yml |
Configuración del sitio, navegación |
overrides/** |
Plantillas HTML (main.html) |
.forgejo/** |
El propio workflow de CI |
Manual (forzar re-ejecución)¶
cd /home/tonatiuh/SmallCountry/web
git commit --allow-empty -m "ci: trigger re-run"
git push origin main
7. Cómo gestionar el runner¶
Estado¶
Parar¶
Arrancar¶
cd /home/tonatiuh/forgejo/runner-data
setsid /home/tonatiuh/forgejo/forgejo-runner daemon --config config.yml > runner.log 2>&1 &
disown
Ver en Forgejo (admin)¶
Aquí ves: nombre (casita-runner), estado (🟢/🔴), última conexión, y puedes generar nuevos tokens de registro.
8. Solución de problemas frecuentes¶
| Síntoma | Causa | Solución |
|---|---|---|
| CI no se lanza al hacer push | Los archivos tocados no coinciden con los path filters | Verifica que docs/, mkdocs.yml, overrides/ o .forgejo/ estén incluidos |
status=failure sin job_ids |
El runner no está corriendo | pgrep forgejo-runner. Si no devuelve nada, arrancarlo |
failed to pull image |
La imagen CI no se ha subido al registro | docker push forgejo-sc.duckdns.org/smallcountry/ci-docs:latest |
volume /deploy not allowed |
Falta /home/tonatiuh/SmallCountry/web en valid_volumes del runner |
Añadirlo al config.yml y reiniciar el runner |
| 403 Forbidden al servir la web | caddy no puede leer los archivos desplegados |
sudo usermod -aG tonatiuh caddy && sudo systemctl restart caddy |
| Runner se para solo | El proceso no sobrevive al terminal | Usar setsid + disown (ver comandos arriba) |
| Forgejo API no responde | forgejo-sc.duckdns.org no alcanzable |
Verificar Caddy + contenedor Docker Forgejo: docker ps |