Mueve los reusable workflows a .gitea/workflows/ (restriccion real de Gitea: workflow_call no puede vivir en una ruta arbitraria, confirmado en vivo). Corrige todos los 'uses:' cross-repo a URL absoluta (https://gitea.cruzcloud.net/...) -- la forma corta de GitHub se resuelve contra github.com en esta instancia, confirmado en vivo con un test real (reusable workflow + composite action ejecutando gitleaks de punta a punta). Limpia los archivos de prueba.
8.7 KiB
platform-templates
Patrones reutilizables para dar de alta una app nueva en el lab CruzCloud (ZimaOS + k3d + Gitea + Argo CD) sin escribir el pipeline y los manifiestos K8s desde cero cada vez. Copiás una plantilla, completás unos pocos valores, y tenés: pipeline de CI con el gate completo de DevSecOps (Gitleaks, Trivy, Semgrep, Syft, Cosign) + manifiestos K8s con buenas prácticas por default (resource limits, probes, non-root).
Para quién es esto: si estás dando de alta una app nueva en el lab, seguí el ejemplo de abajo. Si estás migrando una app existente, mirá el punto "Migración de apps existentes" más abajo, y andá con más cuidado — tocás algo que ya está en producción.
Mapa del repo
pipelines/ -- documentación de cada arquetipo de pipeline
(el archivo real que se invoca vive en
.gitea/workflows/, ver por qué más abajo)
.gitea/workflows/ -- los reusable workflows reales (workflow_call)
dockerfiles/ -- Dockerfiles genéricos, un ARG por cosa a cambiar
k8s-manifests/base/ -- Kustomize base: Deployment+Service+Ingress
genéricos, con securityContext/probes/resources
ya puestos
k8s-manifests/overlays-ejemplo/ -- el ejemplo de abajo, ya armado
devsecops-snippets/ -- los 6 composite actions que arman cada pipeline
Hallazgo importante sobre Gitea Actions (leer antes de copiar nada)
Este repo se armó verificando en vivo, no asumiendo que Gitea Actions es 100% igual a GitHub Actions. Dos diferencias reales, confirmadas corriendo pipelines de verdad contra esta instancia:
- Los reusable workflows (
workflow_call) tienen que vivir bajo.gitea/workflows/— no en una carpeta cualquiera. Por esopipelines/archetype-*/solo tiene unREADME.md, el archivo invocable real está en.gitea/workflows/archetype-*.yaml. - Toda referencia cross-repo en un
uses:necesita la URL completa (https://gitea.cruzcloud.net/owner/repo/path@ref), no la forma corta de GitHub (owner/repo/path@ref) — esa forma corta se resuelve contra github.com por default en esta instancia (confirmado: intentar usarla hace que el runner intente clonargithub.com/devops/platform-templates, que no existe, y falle con "Repository not found").actions/checkout@v3sigue andando con la forma corta porque ESE sí existe en github.com de verdad.
Ejemplo end-to-end: dar de alta "mi-app-ejemplo"
Pensado para seguirse sin conocer nada más del lab que "existe Gitea en
gitea.cruzcloud.net y un cluster k3d con Argo CD sincronizando desde
ahí". Asumimos una API Node (arquetipo node-api) — para Next.js o un
sitio estático es el mismo procedimiento, cambiando el arquetipo.
1. Creá el repo de tu app en Gitea
Vía la UI (https://gitea.cruzcloud.net/repo/create) o gh/API si
tenés token con el scope correcto. Nombre: mi-app-ejemplo.
2. Copiá el Dockerfile
curl -o Dockerfile \
https://gitea.cruzcloud.net/devops/platform-templates/raw/branch/main/dockerfiles/node-api.Dockerfile
Ajustá los ARG del inicio del archivo a tu caso (puerto real, comando
de arranque) — están documentados inline, no hace falta adivinar.
3. Agregá el pipeline
.gitea/workflows/build.yaml en tu repo:
name: Build and Push
on:
push:
branches: [main]
paths:
- 'src/**'
- 'package.json'
- 'package-lock.json'
- 'Dockerfile'
- '.gitea/workflows/build.yaml'
jobs:
build:
uses: https://gitea.cruzcloud.net/devops/platform-templates/.gitea/workflows/archetype-node-api.yaml@main
with:
app_dir: .
image_name: gitea.cruzcloud.net/devops/mi-app-ejemplo
manifest_file: k8s/deployment.yaml
secrets:
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
Los 4 secrets tienen que existir en tu repo (Settings → Actions → Secrets). Si tu lab ya los tiene a nivel de organización/compartidos, puede que no haga falta redeclararlos — depende de cómo esté armado tu Gitea.
4. Generá tu par de llaves de Cosign (una vez)
cosign generate-key-pair
# genera cosign.key (privada) y cosign.pub (pública)
cosign.pub→ commiteala en tu repo (es pública a propósito).cosign.key→ no la commitees. Subila como secretCOSIGN_PRIVATE_KEY(contenido del archivo) y la password que elegiste como secretCOSIGN_PASSWORD.
5. Manifiestos K8s: copiá el base y armá tu overlay
mkdir -p k8s/patches
curl -o k8s/kustomization.yaml \
https://gitea.cruzcloud.net/devops/platform-templates/raw/branch/main/k8s-manifests/overlays-ejemplo/mi-app-ejemplo/kustomization.yaml
curl -o k8s/patches/deployment.yaml \
https://gitea.cruzcloud.net/devops/platform-templates/raw/branch/main/k8s-manifests/overlays-ejemplo/mi-app-ejemplo/patches/deployment.yaml
curl -o k8s/patches/service.yaml \
https://gitea.cruzcloud.net/devops/platform-templates/raw/branch/main/k8s-manifests/overlays-ejemplo/mi-app-ejemplo/patches/service.yaml
curl -o k8s/patches/ingress.yaml \
https://gitea.cruzcloud.net/devops/platform-templates/raw/branch/main/k8s-manifests/overlays-ejemplo/mi-app-ejemplo/patches/ingress.yaml
Esto ya viene armado con el patrón real (ver
k8s-manifests/overlays-ejemplo/mi-app-ejemplo/ en este mismo repo) —
solo tenés que:
- Cambiar
mi-app-ejemplopor el nombre real de tu app en los 3patches/*.yamly en elkustomization.yaml(nombre del Deployment/Service/Ingress,host:,namespace:). - Ajustar imagen, puerto, y ruta del healthcheck a los reales de tu app.
- Leer los comentarios de
patches/deployment.yamlsobre$patch: replace/JSON6902 antes de tocarports— Kustomize NO reemplaza esa lista con un patch de merge normal, la duplica (confirmado en vivo armando este repo). Si tu puerto real es distinto al 8080 del base, el rename+ports va en un patch JSON6902 aparte en tukustomization.yaml, exactamente como en el ejemplo. - Si tu app escucha en un puerto <1024 (80, por ejemplo): el base
saca TODAS las capabilities del contenedor por default —
necesitás agregar
capabilities.add: [NET_BIND_SERVICE]en tu patch, o el bind falla. Ver el comentario enk8s-manifests/overlays-ejemplo/mi-app-ejemplo/kustomization.yamlsi tu imagen corre como root en vez de un usuario propio (necesitás ademásrunAsNonRoot: falsea nivel pod).
6. Validá el resultado ANTES de commitear
kubectl kustomize k8s/
Revisá que el YAML que sale tenga sentido — nombre, imagen, puerto,
host correctos, sin recursos duplicados. Si algo se ve raro (dos
ports:, dos contenedores), revisá el punto 3 de arriba.
7. Creá la Application de Argo CD
Sigue el patrón de este lab: una Application de workload
(syncPolicy con prune: true, selfHeal: true) apuntando al
k8s/ de tu repo. Preguntale a quien mantiene apps-registry cómo se
registra — no es parte de este repo.
8. Push, mirá el pipeline correr, y listo
Un git push a main dispara Gitleaks → build → Trivy → Semgrep → Syft
→ Cosign → push al registry → Argo CD sincroniza. Si algo bloquea
(CRITICAL de Trivy, firma de Cosign inválida), el log del job te dice
exactamente cuál.
Migración de apps existentes
Si estás moviendo una app que ya está corriendo en producción hacia este patrón base+overlay: no es lo mismo que dar de alta una nueva. Antes de tocar nada:
- Guardá el manifest actual desplegado (
kubectl get deploy <nombre> -n <ns> -o yaml) para comparar. - Armá el overlay apuntando al mismo nombre que ya existe (usando los patches JSON6902 de rename, como en el ejemplo) — no dejes que Kustomize invente un nombre nuevo, rompe el tracking de Argo CD y la resolución DNS de otros servicios que dependan del nombre viejo.
- Revisá con qué usuario corre tu imagen actual. Si no tiene un
USERno-root en su Dockerfile, elrunAsNonRoot: truedel base rompe el pod (CreateContainerConfigError) — hace falta un override explícito hasta que la imagen se actualice aparte. Pasó exactamente esto migrandodocs-portalen este mismo lab. - Corré
kubectl kustomizesobre tu overlay y compará el resultado contra el manifest guardado en el paso 1 antes de dejar que Argo CD sincronice nada.
Documentación ampliada
docs.cruzcloud.net tiene una sección dedicada a este repo con un
diagrama del flujo completo y el detalle de la migración real que se
hizo sobre ecommerce-frontend, commerce-backend y docs-portal.