Regla 16: Despliegue Continuo (CD portable)¶
Premisa: Premisa¶
El CD debe ser reproducible desde local y portable entre proveedores de CI. make construye artefactos; just orquesta operativas (desplegar a server/k8s, publicar release, conectar vía ssh). El CD no depende de secrets del proveedor, excepto la clave privada age para descifrar. Los secretos de la aplicación viven cifrados en .secrets/secrets.<env>.enc.yaml (sops+age, regla 13). Los pipelines externos solo parametrizan/inyectan variables que las tareas locales esperan. Cambiar de proveedor (GitHub → GitLab → Jenkins) no rompe el CD.
tags: [obligatorio]
Estructura: Estructura¶
Componentes del CD portable¶
proyecto/
├── .secrets/
│ └── secrets.<env>.enc.yaml # secretos de app/servicio destino (sops+age)
├── .sops.yaml # age recipients públicos
├── ~/.age/<proyecto>-key.txt # local: clave privada age
│ # pipeline: env del proveedor (AGE_KEY)
├── Makefile → helpers/mk/*.mk # construcción: build, test, package
├── Justfile → helpers/just/*.just # operativas: deploy, k8s-push, ssh, release
│ ├── cd.just # @deploy, @k8s-push, @ssh-deploy
│ └── github.just # @release (→ github.sh → gh)
├── helpers/shell/secrets.sh # edit, env, verify, keygen
├── helpers/shell/deploy.sh # descifra secretos + despliega
└── .github/workflows/*.yml # wrapper: inyecta AGE_KEY, dispara make/just
Flujo de despliegue¶
Local:
just edit-secrets prod → .secrets/secrets.prod.enc.yaml (cifrado)
just env prod → descifra → .env.prod (no versionado)
just deploy prod → make package + secrets env prod + deploy.sh → server/k8s
Pipeline (wrapper):
AGE_KEY inyectado por proveedor
→ just env prod (descifra con AGE_KEY) → .env.prod
→ make package + just deploy prod → CD ejecutado localmente
Secretos en el CD¶
| Fuente | Secretos de app/servicio destino | Secrets de plataforma/proveedor |
|---|---|---|
.secrets/*.enc.yaml (sops+age) |
Sí — tokens de API, passwords DB, claves de registry destino, SSH keys destino | No — GH_TOKEN, GITLAB_TOKEN, etc. |
| Pipeline/proveedor | No — no debe depender | Sí — GH_TOKEN, GITLAB_TOKEN (cada proveedor tiene su variable) |
| Clave privada age | .secrets/ no la versiona; local en ~/.age/ |
El proveedor la inyecta (env AGE_KEY) para descifrar en tiempo de ejecución |
Responsabilidades Makefile vs Justfile¶
- Makefile (construcción/artefactos):
build,test,package,image,release-build— genera el artefacto desplegable. - Justfile (task manager — operativas):
deploy <env>,k8s-push,ssh-deploy,release— orquestamake package+ despliegue al destino.just release→make package+github.just→gh release create.
tags: [opcional]
Nombre Sugerido: Nombres Sugeridos¶
- Secretos de deploy:
.secrets/secrets.<env>.enc.yaml(prod, dev, int, staging...). - Clave age: local
~/.age/<proyecto>-key.txt(nombre del repo en kebab-case); en pipeline:AGE_KEY(env del proveedor). - Scripts:
deploy.sh(operativa de deploy),secrets.sh(cifrado/descifrado). - Módulos Just:
cd.just(deploy, k8s, ssh),github.just(release). - Targets Makefile:
package(construir artefacto),release-build(construir todo lo necesario para el release).
tags: [opcional]
Comando: Comandos¶
Construcción (Makefile)¶
make build # Compilar
make test # Ejecutar tests
make package # Generar artefacto (wheel, jar, tar)
Operativas (Justfile)¶
just deploy prod # → secrets.sh env prod + make package + deploy.sh → server
just deploy staging # → secrets.sh env staging + make package + deploy.sh → staging server
just k8s-push # → make package + kubectl apply / docker push
just ssh-deploy host # → secrets.sh env prod + rsync + ssh restart
just release # → make package + github.just → gh release create
Secretos del deploy¶
just edit-secrets prod # → sops edit .secrets/secrets.prod.enc.yaml
just env prod # → descifra → .env.prod
just env prod --export # → exporta variables al entorno (para uso en deploy)
Pipeline (wrapper parametrizador)¶
- name: Deploy to production
env:
AGE_KEY: ${{ secrets.AGE_KEY }} # único secret del proveedor
run: |
echo "$AGE_KEY" > ~/.age/proyecto-key.txt
just deploy prod
tags: [opcional]
Ejemplo: Ejemplos¶
Desplegar a un servidor vía SSH¶
# Local: cifrar credenciales
just edit-secrets prod
# .secrets/secrets.prod.enc.yaml contiene:
# SSH_HOST=mi-server.com
# SSH_USER=deployer
# SSH_KEY_PATH=~/.ssh/deploy_key
# APP_PORT=8080
# Local: desplegar
just ssh-deploy mi-server.com
# → just env prod (descifra)
# → make package
# → rsync + ssh restart
Pipeline de GitHub Actions (wrapper parametrizador)¶
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Decrypt and deploy
env:
AGE_KEY: ${{ secrets.AGE_KEY }}
run: |
echo "$AGE_KEY" > ~/.age/proyecto-key.txt
just deploy prod
Publicar release en GitHub¶
# Local: publicar release
just release
# → make package (construye artefacto)
# → github.just → github.sh → gh release create
El pipeline externo solo necesita inyectar AGE_KEY para descifrar. Si el proveedor no tiene secrets, el desarrollador ejecuta el CD directamente desde local (sin pipeline).
tags: [obligatorio]
Restriccion: Restricciones¶
- El CD no depende de secrets del proveedor, excepto la clave privada age (
AGE_KEY) para descifrar. Todos los demás secretos viven en.secrets/*.enc.yaml. - Excepción deliberada: los secrets de plataforma/proveedor (GH_TOKEN, GITLAB_TOKEN, DOCKER_REGISTRY_TOKEN, etc.) no van en
.secrets/. Cada proveedor tiene su propia variable; viven en el proveedor. Si se migra de GitHub a GitLab, esos secrets se recrean en el nuevo proveedor. Los secrets de la aplicación destino NO cambian. - Cambiar de proveedor no rompe el CD. Las tareas locales (
make package,just deploy prod) son la fuente de verdad. Los pipelines solo las parametrizan. - Los pipelines externos son wrappers/parametrizadores. Inyectan variables (
AGE_KEY, envs del proveedor) y disparanmake/just. Nunca reimplementan lógica de deploy. - El CD es local-first.
just deploy proddebe funcionar en cualquier máquina del equipo sin necesidad de CI remoto. sops,age, y las herramientas destino (docker/podman,kubectl,gh,ssh) deben estar instaladas localmente (documentado en la instalación de hooks).- El
deploy.shnunca hardcodea credenciales ni hosts. Recibe todo por flags o variables de entorno descifradas de.secrets/. just releasees operativa → Justfile, no Makefile (publicar no es construir, es orquestarmake package+gh).
tags: [obligatorio]
Referencia: Referencias¶
- Regla 01: Build Tooling — separación de responsabilidades Makefile/Justfile.
- Regla 06: Integración Continua — CI local en contenedor, wrappers parametrizadores.
- Regla 13: Gestión de Secretos — sops+age,
.secrets/*.enc.yaml,secrets.sh. - Regla 14: Archivos de Configuración —
.config/sops/.sops.yaml. - Regla 15: Reutilización de Scripts — scripts atómicos + dominios (deploy.sh).
- templates/repository-structure/helpers/just/cd.just.tmpl
- templates/repository-structure/helpers/just/github.just.tmpl
- templates/repository-structure/helpers/shell/deploy.sh.tmpl
tags: [obligatorio]
Plantilla: Plantilla¶
- templates/repository-structure/helpers/just/cd.just.tmpl
- templates/repository-structure/helpers/just/github.just.tmpl
- templates/repository-structure/helpers/shell/deploy.sh.tmpl
- templates/repository-structure/helpers/shell/secrets.sh.tmpl
- templates/repository-structure/.secrets/
tags: [opcional]