Implementar & CI/CD
cipi deploy
Cipi usos Implementador para todas las implementaciones. Cada implementación es atómica: una nueva versión
El directorio está completamente preparado antes de la current El enlace simbólico se intercambia, por lo que el tráfico nunca se interrumpe.
interrumpido.
cipi deploy y cipi deploy --rollback
cancelar con un mensaje de actualización claro antes invocar Deployer cuando una aplicación todavía está fijada
a una versión anterior PHP; cámbiela con cipi app edit <app> --php=8.3 (o
superior) primero.Implementar canalización
Deployer y Composer se ejecutan con el versión PHP configurada de la aplicación (por ej.
/usr/bin/php8.5), no el valor predeterminado del sistema. Esto se aplica a cipi deploy,
cipi deploy --rollback, activadores de implementación de crontab, cipi sync import despliega,
y el deploy / composeralias en el usuario de la aplicación .bashrc.
- Detener trabajadores en cola (
cipi worker stop) - Clonar repositorio en
releases/N/ - correr
composer install --no-dev(con PHP de la aplicación) - Enlace
shared/.envyshared/storage/ - correr
artisan migrate --force - correr
artisan optimize - correr
artisan storage:link - Intercambiar
currentenlace simbólico atómicamente - Reiniciar trabajadores de la cola
- Pode las versiones antiguas (conserve las últimas 5)
$ cipi deploy myapp # deploy latest commit $ cipi deploy myapp --rollback # instant rollback to previous release $ cipi deploy myapp --releases # list all releases with timestamps $ cipi deploy myapp --key # show the SSH deploy key $ cipi deploy myapp --webhook # show webhook URL and token $ cipi deploy myapp --unlock # remove a stuck deploy lock $ cipi deploy myapp --snapshot # v5.0+ opt-in DB dump before deploy $ cipi deploy myapp --snapshot-required # fail if snapshot cannot be taken $ cipi deploy myapp --trust-host=git.mycompany.com # trust a custom Git server fingerprint $ cipi deploy myapp --trust-host=git.mycompany.com:2222 # trust on non-standard port (also writes ~/.ssh/config)
desde v5.0, inscribirse instantáneas de base de datos previas a la implementación están disponibles a través de
--snapshot / --snapshot-required o una configuración de aplicación permanente; consulte
Instantáneas de base de datos previas a la implementación. Octane aplicaciones usan el
laravel-octane.php Plantilla de implementación (recargar/reiniciar Octane en la implementación); habilitar nodo
construye con cipi app edit <app> --node-build='…'.
cipi deploy myapp --unlock para eliminarlo antes de volver a implementarlo.Instantáneas de base de datos previas a la implementación
desde v5.0, cipi deploy <app> puede volcar la base de datos
antes se ejecuta el proceso de lanzamiento. Las instantáneas aterrizan debajo
/var/log/cipi/backups/ - el mismo camino utilizado por
cipi db backup.
$ cipi deploy shop --snapshot # dump first; warn and continue on failure $ cipi deploy shop --snapshot-required # dump first; abort deploy if snapshot fails
que pasa
- Antes de que se inicie Deployer, Cipi realiza un volcado de base de datos en
/var/log/cipi/backups/. - con
--snapshot-required, un volcado fallido (o falta un motor de base de datos) bloques el despliegue. - con
--snapshotsolo, un volcado fallido imprime una advertencia y la implementación continúa.
Habilitar en cada implementación
Actívelo permanentemente para una aplicación de modo que cada implementación (CLI, webhook o canalización) tome primero una instantánea:
$ cipi app edit shop --predeploy-snapshot
cipi deploy --rollback restaura el anterior código liberación solamente.
lo hace no restaurar la base de datos. Si necesita recuperar el volcado previo a la implementación, utilice
cipi db restore.Para flujos de trabajo de canalización que también archivan shared/ a S3 antes del lanzamiento, consulte
Implementación segura: copia de seguridad antes del lanzamiento.
cipi app deploy-config
desde v5.0.3, administrar opciones duraderas de recetas de Deployer almacenadas en
apps.json y se aplica regenerando deploy.php de la plantilla -
una alternativa segura a la edición de formato libre PHP.
$ cipi app deploy-config myapp $ cipi app deploy-config myapp --keep-releases=5 $ cipi app deploy-config myapp --migrate --optimize --storage-link $ cipi app deploy-config myapp --no-migrate --no-optimize $ cipi app deploy-config myapp --queue-restart --horizon-terminate $ cipi app deploy-config myapp --extra-artisan=view:clear,event:cache $ cipi app deploy-config myapp --node-build='npm ci && npm run build' $ cipi app deploy-config myapp --predeploy-snapshot
DESCANSO: GET|PUT /api/apps/{name}/deploy-config (capacidad
apps-deploy-config, API 1.14+ / Cipi 5.0.3+).
MCP: AppDeployConfigShow, AppDeployConfigUpdate.
auth.json
Gestionar el auth.json archivo para una aplicación. Este archivo vive en
/home/<app>/shared/auth.json y tiene un enlace simbólico automático en cada versión mediante
Implementador: exactamente igual .env. Úselo para almacenar datos de credenciales estructurados (por ejemplo, API
claves, indicadores de funciones o cualquier carga útil JSON) que su aplicación Laravel pueda leer en tiempo de ejecución.
$ cipi auth create myapp # create auth.json with initial { "users": [] } structure $ cipi auth edit myapp # open in $EDITOR (fallback: nano), validate JSON on close $ cipi auth show myapp # print contents formatted with jq $ cipi auth delete myapp # delete file (asks for confirmation) # Non-interactive (v5.0.3+) — API / scripts / GUI $ cipi auth create myapp --force $ cipi auth edit myapp --file=/tmp/auth.json $ cipi auth show myapp --json $ cipi auth delete myapp --force
DESCANSO: GET|POST|PUT|DELETE /api/apps/{name}/auth (capacidad apps-auth, API
1.14+) — Composer/estructurado JSON, distinto de HTTP Autenticación básica.
MCP: AppAuthJsonShow, AppAuthJsonCreate,
AppAuthJsonUpdate, AppAuthJsonDelete.
Detalles del comando
| Comando | Descripción |
|---|---|
cipi auth create <app> |
Crea shared/auth.json con la estructura inicial
{"users":[]}, establece permisos para 640 (propietario
app:app), y añade auth.json a
shared_files en la configuración del implementador de la aplicación para que tenga un enlace simbólico en cada
desplegar.
|
cipi auth edit <app> |
Abre shared/auth.json en $EDITOR (vuelve a
nano). Después de que se cierre el editor, valide JSON con
jq y advierte si el archivo tiene un formato incorrecto.
|
cipi auth show <app> |
Imprime el contenido de shared/auth.json formateado con
jq.
|
cipi auth delete <app> |
Pide confirmación y luego lo elimina. shared/auth.json y elimina el
auth.json entrada desde shared_files en el implementador de la aplicación
configuración.
|
Integración del implementador
cipi auth create agrega automáticamente auth.json al
shared_files lista en /home/<app>/.deployer/deploy.php, y
cipi auth delete lo elimina. Esto significa que el archivo se trata exactamente como
.env: persiste en todas las versiones y nunca se sobrescribe con una implementación.
cipi auth La operación se registra mediante log_action para
auditabilidad. el AUTH La sección también aparece en la salida de
cipi help.
Proveedores de Git
Cipi está listo para trabajar con GitHub y GitLab pero soporta cualquier otro proveedor de Git que admita claves de implementación SSH, sin dependencia del proveedor.
Para self-hosted o servidores Git personalizados, debe confiar en la huella digital del host del servidor antes
El implementador puede clonar a través de SSH. Utilice el --trust-host bandera para agregar la huella digital a la
usuario de la aplicación ~/.ssh/known_hosts automáticamente:
# show the deploy key and add it to your Git provider $ cipi deploy myapp --key # trust a custom Git server fingerprint (standard port) $ cipi deploy myapp --trust-host=git.mycompany.com # trust a custom Git server on a non-standard port # (also writes ~/.ssh/config automatically) $ cipi deploy myapp --trust-host=git.mycompany.com:2222
Host / Port entrada al usuario de la aplicación ~/.ssh/config entonces
ese Deployer puede llegar al servidor sin ninguna configuración adicional.
configuración automática de git
Si guardas un GitHub o GitLab Token de acceso personal, Cipi
agrega automáticamente la clave de implementación SSH y crea webhook en el repositorio cada vez que ejecutacipi app create. No se requieren pasos manuales.
guardar una ficha
# GitHub (fine-grained or classic PAT) $ cipi git github-token ghp_xxxxxxxxxxxxxxxxxxxx # GitLab (gitlab.com) $ cipi git gitlab-token glpat-xxxxxxxxxxxxxxxxxxxx # GitLab (self-hosted — set the URL before or after the token) $ cipi git gitlab-url https://gitlab.example.com $ cipi git gitlab-token glpat-xxxxxxxxxxxxxxxxxxxx
GitHub permisos de token
Se necesitan tokens detallados (recomendados) administración y
Ganchos web establecer en leer y escribir en los repositorios de destino. Fichas clásicas
necesito el repo alcance.
GitLab permisos de token
el api el alcance es el mínimo requerido: GitLab no ofrece un alcance más granular
que cubre tanto las claves de implementación como los webhooks.
Ciclo de vida automático
| Evento | Qué hace Cipi automáticamente |
|---|---|
app create |
Agrega la clave de implementación + crea webhook en el repositorio a través de API. El resumen muestra "configurado automáticamente ✓" en lugar de instrucciones manuales. |
app edit --repository=... |
Elimina la clave de implementación + webhook del repositorio anterior y luego las agrega al nuevo. |
app delete |
Elimina la clave de implementación + webhook del repositorio antes de eliminar la aplicación. |
cipi git comandos
| Comando | Descripción |
|---|---|
cipi git status |
Mostrar el estado de conexión del proveedor y los detalles de integración por aplicación (implementar ID de clave, webhook ID) |
cipi git github-token <token> |
Guarde un GitHub token de acceso personal |
cipi git gitlab-token <token> |
Guarde un GitLab token de acceso personal |
cipi git gitlab-url <url> |
Establecer la URL base para una instancia self-hosted GitLab |
cipi git remove-github |
Eliminar el token GitHub almacenado |
cipi git remove-gitlab |
Eliminar el token GitLab y la URL almacenados |
Configuración manual (alternativa)
La configuración automática se omite cuando no se configura ningún token, cuando falla la llamada API (permisos incorrectos, repositorio no encontrado, límite de velocidad), o cuando el repositorio está alojado en un proveedor que no sea GitHub o GitLab (por ejemplo, Gitea, Forgejo, Bitbucket). En todos estos casos Cipi vuelve al flujo de trabajo manual y la creación de la aplicación continúa normalmente.
Para configurar la clave de implementación y webhook manualmente:
# print the SSH deploy key to add to your Git provider $ cipi deploy myapp --key # print the webhook URL and token $ cipi deploy myapp --webhook # if using a custom Git server, trust the host fingerprint first $ cipi deploy myapp --trust-host=git.mycompany.com
Luego agréguelos en la configuración del repositorio de su proveedor:
- Implementar clave — GitHub: Configuración → Implementar claves → Agregar clave de implementación; GitLab: Configuración → Repositorio → Implementar claves
- Webhook — GitHub: Configuración → Webhooks → Agregar webhook;
GitLab: Configuración → Webhooks → Agregar nuevo webhook. Establezca la URL de carga útil y
secreto de los valores mostrados por
cipi deploy myapp --webhook
Personalización del script de implementación
La configuración de implementación para cada aplicación se almacena. en:
Este archivo es generado automáticamente por Cipi durante app create y se actualiza automáticamente cuando
cambie la versión PHP o implemente la rama a través de cipi app edit. Puedes editarlo para personalizarlo.
la canalización de implementación, pero debe comprender las implicaciones antes de hacerlo.
Canalización de implementación predeterminada
El autogenerado deploy.php ejecuta estas tareas en orden:
deploy:prepare // create releases/N/ directory deploy:vendors // composer install --no-dev deploy:shared // link shared/.env and shared/storage/ artisan:migrate // php artisan migrate --force artisan:optimize // php artisan optimize artisan:storage:link // php artisan storage:link deploy:symlink // swap current → releases/N/ atomically cipi:restart-workers // supervisorctl restart myapp-* deploy:cleanup // keep last 5 releases, delete older
Agregar tareas personalizadas
Puede agregar tareas antes o después de cualquier paso. Para ver un ejemplo completo de compilación de frontend (npm install && npm run build), ver
Construyendo activos frontend abajo. Otros ejemplos comunes:
// Run artisan db:seed after migrations after('artisan:migrate', 'artisan:db:seed'); // Clear view cache after symlink swap after('deploy:symlink', 'artisan:view:clear'); // Custom task — send a Slack notification task('notify:slack', function () { run('curl -X POST https://hooks.slack.com/... -d \'{"text":"Deployed!"}\''); }); after('deploy:symlink', 'notify:slack');
Creación de activos de interfaz (npm / Vite)
no hay dedicado cipi Marca CLI para compilaciones de frontend (p. ej. npm install && npm run build).
Personalización deploy.php es el enfoque apoyado y esperado - definir un
Implementador task() y engancharlo con after() o before(). tu no
es necesario evitar esto; extender la canalización es exactamente para lo que sirve el archivo.
Cipi instala Node.js y npm en el servidor durante la instalación. Verifique que estén disponibles como el usuario de la aplicación:
$ ssh myapp@your-server-ip
myapp@server:~$ node -v && npm -v
comprometerse package.json y package-lock.json a su repositorio. Engancha la construcción
después deploy:shared entonces .env está vinculado (Vite lee
VITE_* variables a partir de ahí) y antes deploy:symlink entonces
Los activos compilados existen en la versión antes de que entre en funcionamiento.
Agregue el bloque a continuación en el abajo de
/home/myapp/.deployer/deploy.php, debajo de las definiciones de tareas generadas automáticamente por Cipi:
// ── Custom: frontend build (safe zone — keep below Cipi-managed blocks) ── task('npm:build', function () { cd('{{release_path}}'); run('npm ci --no-audit --no-fund && npm run build'); }); // .env is linked → build assets → then migrations / optimize / symlink after('deploy:shared', 'npm:build');
Esto equivale a npm install && npm run build en cada despliegue. Prefiero
npm ci en producción cuando package-lock.json está comprometido: es más rápido y
reproducible. uso npm install en cambio, solo si no bloquea las dependencias.
Si necesita pasos de instalación y compilación separados (por ejemplo, almacenar en caché node_modules en todas las versiones),
dividirlos en dos tareas:
task('npm:ci', function () {
cd('{{release_path}}');
run('npm ci --no-audit --no-fund');
});
task('npm:build', function () {
cd('{{release_path}}');
run('npm run build');
});
after('deploy:shared', 'npm:ci');
after('npm:ci', 'npm:build');
Para acelerar implementaciones posteriores, puede conservar las dependencias entre versiones agregando
node_modules a los directorios compartidos del implementador (opcional, solo si su proyecto admite
eso):
add('shared_dirs', ['node_modules']);
Edite el archivo en el servidor como usuario de la aplicación, luego pruebe con cipi deploy myapp:
$ ssh myapp@your-server-ip myapp@server:~$ nano ~/.deployer/deploy.php # paste the custom tasks at the bottom, save, then as root: $ cipi deploy myapp
npm ci && npm run build en GitHub Acciones
o GitLab CI antes el paso de implementación SSH, por lo que el servidor solo recibe activos prediseñados.
Ver CI/CD canalizaciones: implementación de SSH.Ejecutando comandos artisan adicionales
// Seed only in specific environments
task('artisan:db:seed', function () {
run('{{bin/php}} {{release_path}}/artisan db:seed --force');
});
cipi app edit myapp --php=X o cipi app edit myapp --branch=X. hacer copia de seguridad
tus personalizaciones o mantenlas en una sección claramente separada de los bloques administrados por Cipi. un
El patrón seguro es colocar todas las tareas personalizadas al final del archivo después de la tarea predeterminada.
definición.
Deshabilitar un paso predeterminado
Para omitir una tarea (por ejemplo, si maneja las migraciones manualmente), coméntela o elimínela del
deploy definición de tarea:
// Remove the migrate step from the pipeline task('deploy', [ 'deploy:prepare', 'deploy:vendors', 'deploy:shared', // 'artisan:migrate', ← disabled 'artisan:optimize', 'artisan:storage:link', 'deploy:symlink', 'cipi:restart-workers', 'deploy:cleanup', ]);
Probando tus cambios
Después de editar deploy.php, siempre haga una implementación de prueba antes de pasar a producción:
$ cipi deploy myapp # If something goes wrong, instant rollback: $ cipi deploy myapp --rollback # If the deploy is stuck (e.g. interrupted mid-run): $ cipi deploy myapp --unlock
~/logs/deploy.log o vía
cipi app logs myapp --type=deploy. Compruébelo primero cuando solucione un problema
desplegar.
Implementar y CI/CD: descripción general
Con Cipi, CI (construir y probar) y CD (lanzamiento a producción) puede
ser divididos o combinados. En última instancia, cada implementación se ejecuta de la misma manera. Implementador
tubería en el servidor - clonar, composer install, migraciones, intercambio de enlaces simbólicos,
reinicio del trabajador. lo que cambia es que desencadena ese oleoducto.
Cipi admite dos modelos de disparador. Para la mayoría de las aplicaciones Laravel, comience con webhook + Cipi Agente camino. Pase a una canalización CI/CD completa cuando necesite puertas, copias de seguridad o orquestación de infraestructura que un simple gancho de empuje no puede expresar.
Dos formas de activar una implementación
| Webhook + Cipi Agente (recomendado) | CI/CD canalización a través de SSH | |
|---|---|---|
| gatillo | PUBLICACIONES del proveedor de Git para /cipi/webhook al empujar |
GitHub Acciones / GitLab Ejecuciones de trabajos de CI cipi deploy a través de SSH |
| Acceso al servidor desde CI | Ninguno: solo HTTPS para el dominio de tu aplicación | Clave SSH dedicada almacenada como secreto de CI |
| Pruebas previas a la implementación | Ejecutar localmente o en un trabajo de CI independiente; El despliegue aún se activa al presionar a menos que desactives el webhook | Nativo: el paso de implementación se ejecuta solo después needs: test (o equivalente) pases |
| Copia de seguridad antes del lanzamiento | Manual o cron en el servidor | Trabajo de canalización — ver despliegue seguro |
| Vista previa/revisión de aplicaciones | No compatible desde el primer momento | Pipeline crea Cipi aplicaciones por rama; consulte vista previa entornos |
| Complejidad de configuración | Bajo — composer require cipi/agent + uno webhook |
Medio: clave SSH, secretos, flujo de trabajo YAML |
¿Qué enfoque debo utilizar?
| Caso de uso | Enfoque recomendado | Dónde leer más |
|---|---|---|
Aplicación única Laravel, push-to-implementar activada main |
Webhook + Agente | Webhook configuración |
| Implementar solo si se pasan las pruebas de CI | Canalización SSH (deshabilitar producción webhook) | Implementación de canalización SSH |
| Copia de seguridad de archivos DB + antes de cada lanzamiento de producción | Tubería SSH | Implementación segura con respaldo |
| Alertas de Slack/Telegram sobre el resultado de la implementación | Tubería SSH | Implementar notificaciones |
| URL efímera por rama de función (revisar aplicaciones) | Tubería SSH | Vista previa de entornos |
| Implemente varias aplicaciones en un servidor desde un repositorio | Ya sea: webhook por aplicación o una canalización con paralelo cipi deploy |
Implementación de múltiples aplicaciones |
cipi deploy myapp --unlock si se deja una cerradura atascada.
Implementaciones automáticas: Cipi Agente y webhook
cipi-agente (cipi/agent) es un paquete Laravel que expone
POST /cipi/webhook dentro de su aplicación en ejecución. Cuando GitHub o GitLab envía un push
evento, el agente valida la firma de la carga útil, reconoce inmediatamente y pone en cola una implementación en
el servidor: no hay SSH del corredor CI, no sudo, no hay puertos de entrada abiertos más allá de HTTPS.
Cómo funciona el flujo webhook
El diseño separa acuse de recibo rápido HTTP de Implementador lento trabajo. Una implementación puede tardar varios minutos; Los proveedores de Git agotan el tiempo de espera de webhook HTTP llamadas después de ~10 segundos. Cipi resuelve esto con un archivo de bandera y el crontab del usuario de la aplicación.
Developer Git provider Your Laravel app (Cipi Agent) Server (app user cron)
│ │ │ │
│ git push main │ │ │
│ ───────────────────────────► │ │ │
│ │ POST /cipi/webhook │ │
│ │ (signed with secret) │ │
│ │ ───────────────────────────► │ │
│ │ │ 1. Verify CIPI_WEBHOOK_TOKEN │
│ │ │ 2. Check branch (CIPI_DEPLOY_BRANCH) │
│ │ │ 3. Write ~/.deploy-trigger │
│ │ ◄─────────────────────────── │ 4. Return 200 immediately │
│ │ │ │
│ │ │ every minute (* * * * *) │
│ │ │ ◄──────────────────────────────────────│
│ │ │ cron sees .deploy-trigger │
│ │ │ removes file, runs Deployer │
│ │ │ in background as app user │
│ │ │ │
│ │ │ clone → composer → migrate │
│ │ │ → symlink swap → workers │
El implementador siempre se ejecuta como aplicación usuario de Linux (por ej. myapp), con el
corregir PHP permisos binarios y de archivos: el mismo contexto que un manual
cipi deploy myapp. El webhook nunca paga directamente al Deployer; solo deja caer el
archivo desencadenante que el crontab de Cipi ya supervisa.
Requisitos previos
| Requisito | ¿Por qué? |
|---|---|
| Aplicación Cipi creada con el repositorio Git | La clave de implementación debe clonar el repositorio; consulte configuración automática de git |
| Al menos una implementación manual exitosa | El paquete del agente debe estar presente en el current liberar antes del webhook
la ruta existe |
composer require cipi/agent en el proyecto |
Registra el /cipi/webhook validación de ruta y firma |
| Webhook URL accesible a través de HTTPS | Los proveedores de Git requieren una URL pública; usar cipi ssl install primero |
CIPI_WEBHOOK_TOKEN en shared/.env |
Generado automáticamente en cipi app create; compartido en todas las versiones |
Configuración paso a paso
1. Cree la aplicación e impleméntela una vez manualmente. para que el servidor pueda clonar su repositorio:
$ cipi app create --user=myapp --domain=myapp.com \ --repository=git@github.com:you/myapp.git --branch=main --php=8.5 $ cipi deploy myapp
2. Instale el agente Cipi en su proyecto Laravel localmente, confirme y envíe:
$ composer require cipi/agent $ git add composer.json composer.lock $ git commit -m "Add Cipi Agent for webhook deploys" $ git push origin main $ cipi deploy myapp # one more manual deploy until webhook is live
3. Configure el webhook. Si guardó un token GitHub o GitLab, es posible que Cipi tenga
ya creó el webhook durante app create - consultar con
cipi git status. De lo contrario, recupere la URL y el secreto:
$ cipi deploy myapp --webhook
Agregue webhook en su proveedor de Git:
| Proveedor | URL de carga útil | campo secreto | Eventos |
|---|---|---|---|
| GitHub | https://myapp.com/cipi/webhook |
Secreto → valor de --webhook |
Sólo el push evento |
| GitLab | https://myapp.com/cipi/webhook |
Token secreto → mismo valor | Eventos push |
4. Restringir a su sucursal de implementación (recomendado para producción):
CIPI_DEPLOY_BRANCH=main
Establecer esto en shared/.env vía cipi app env myapp. Empujes a otras ramas
recibir un skipped respuesta y no se realizan ejecuciones de implementación.
5. Verificar. Empuje un pequeño compromiso para main y mire el registro de implementación:
$ cipi app logs myapp --type=deploy # or on the server as the app user: $ tail -f /home/myapp/logs/deploy.log
Aproximadamente un minuto después de la entrega webhook, debería aparecer una nueva versión de Deployer. Confirma el
compromiso en vivo con php artisan cipi:status o el salud
comprobar punto final.
configuración automática de git
cuando un GitHub o GitLab token está configurado en el servidor, Cipi
registra la clave de implementación y crea el webhook automáticamente en cada
cipi app create. El resumen de la aplicación muestra "configurado automáticamente ✓" en lugar de manual
instrucciones. Eventos del ciclo de vida (app edit --repository, app delete) mantener
claves y webhooks sincronizados.
Solución de problemas
| Síntoma | causa probable | Arreglar |
|---|---|---|
| Webhook devuelve 404 | Agente aún no implementado | correr cipi deploy myapp después de agregar cipi/agent a
composer.json
|
| Webhook devuelve 403/firma no válida | discrepancia secreta | Vuelva a copiar el token de cipi deploy myapp --webhook en la configuración del proveedor
|
| 200 OK pero sin implementación | Rama filtrada | comprobar CIPI_DEPLOY_BRANCH coincide con la rama empujada |
| Implementación atascada/error de bloqueo | Despliegue anterior interrumpido | cipi deploy myapp --unlock luego vuelve a intentarlo |
| Implementar ejecuciones dos veces con una sola pulsación | Webhook + canalización ambas activas | Desactive un activador: consulte descripción general |
deploy herramienta—
usa lo mismo .deploy-trigger mecanismo. Ver Cipi Agente
para controles de estado, MCP y funciones de anonimización.CI/CD canalizaciones: implementación de SSH
Cuando el modelo webhook no sea suficiente, ejecute GitHub Acciones o GitLab
CI/CD trabajos que SSH ingresan al servidor e invocan cipi deploy. Este es el
elección correcta siempre que se deba realizar la implementación condicional - controlado en pruebas, precedido por copias de seguridad,
seguido de notificaciones u orquestando nuevas aplicaciones de vista previa.
Cuando necesita una canalización en lugar de webhook
- Puerta de calidad - correr
php artisan test, análisis estático o frontend se construye antes de que cualquier código llegue a producción - Liberación segura - tomar una instantánea de la base de datos y
shared/a S3 antes intercambiando el enlace simbólico (despliegue seguro) - Visibilidad del equipo — publicar éxito/fallo en Slack o Telegram con la reversión activada fracaso (implementar notificaciones)
- Revisar aplicaciones — crear o actualizar una aplicación Cipi completa por rama de funciones (entornos de vista previa)
- Monorepo multiaplicación - implementar
frontendyapien paralelo después de un único trabajo de prueba
Para estos flujos de trabajo, desactivar la producción webhook (o nunca crear uno) así que solo se implementan los desencadenadores de la canalización. Aún puedes usar Cipi Agent en la aplicación para controles de estado y MCP.
Acceso SSH para CI
/root/.ssh/authorized_keys en el servidor (o el
cipi usuario si lo prefieres sudo cipi deploy) y almacenar el
clave privada como un secreto de CI. Nunca reutilice las claves de implementación de Git o las claves SSH personales.
# on your local machine $ ssh-keygen -t ed25519 -C "ci-deploy" -f ~/.ssh/ci_deploy -N "" # copy the public key to the server $ ssh-copy-id -i ~/.ssh/ci_deploy.pub root@your-server-ip # copy the private key content → add it as a CI secret (SERVER_SSH_KEY) $ cat ~/.ssh/ci_deploy
Tienda SERVER_HOST (IP del servidor o nombre de host) junto a SERVER_SSH_KEY en tu
secretos del repositorio (GitHub) o CI/CD variables (GitLab).
GitHub Acciones: prueba y luego implementa
Agregue la clave privada como un secreto de repositorio llamado SERVER_SSH_KEY y la IP del servidor como
SERVER_HOST.
# .github/workflows/deploy.yml name: Deploy on: push: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run tests run: php artisan test deploy: runs-on: ubuntu-latest needs: test # only deploy if tests pass steps: - name: Deploy via Cipi uses: appleboy/ssh-action@v1 with: host: ${{ secrets.SERVER_HOST }} username: cipi key: ${{ secrets.SERVER_SSH_KEY }} script: sudo cipi deploy myapp
Para revertir una falla, extienda el paso del guión:
script: |
sudo cipi deploy myapp || (sudo cipi deploy myapp --rollback && exit 1)
GitLab CI/CD
Agregue la clave privada como una variable CI/CD denominada SERVER_SSH_KEY (tipo: Archivo) y el servidor
propiedad intelectual como SERVER_HOST.
# .gitlab-ci.yml
stages:
- test
- deploy
test:
stage: test
script:
- php artisan test
deploy:
stage: deploy
environment: production
only:
- main
before_script:
- apt-get install -y openssh-client
- eval $(ssh-agent -s)
- echo "$SERVER_SSH_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- ssh-keyscan -H $SERVER_HOST >> ~/.ssh/known_hosts
script:
- ssh root@$SERVER_HOST "cipi deploy myapp"
Con reversión en caso de falla:
script:
- ssh root@$SERVER_HOST "cipi deploy myapp || (cipi deploy myapp --rollback && exit 1)"
Implementación de múltiples aplicaciones
Si la misma canalización administra varias aplicaciones en el mismo servidor:
# GitHub Actions — deploy multiple apps in parallel
- name: Deploy
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: root
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
cipi deploy frontend &
cipi deploy api &
wait
Patrones de canalización avanzados
Una vez que la implementación de SSH funcione, componga estas secciones en un único flujo de trabajo de producción:
| Patrón | Lo que añade el oleoducto | Guía |
|---|---|---|
| Notificaciones | Mensaje de Slack o Telegram sobre éxito, fracaso y reversión automática | Implementar notificaciones |
| Implementación segura | cipi db backup + cipi backup run antes
cipi deploy; revertir en caso de falla
|
Implementación segura con respaldo |
| Vista previa de entornos | Crear/actualizar/eliminar aplicaciones por rama Cipi con comodín DNS + SSL | Vista previa de entornos |
Una configuración madura típica utiliza el webhook para una aplicación de preparación (comentarios instantáneos sobre cada empujar) y un tubería para producción (pruebas → copia de seguridad → implementar → notificar). Cada aplicación tiene su propio desencadenante: nunca entran en conflicto porque se dirigen a diferentes Cipi usuarios de la aplicación.
Implementar notificaciones
Caso de uso de canalización: la ruta webhook se implementa silenciosamente: Git devuelve 200 y el equipo
se entera solo si miran los registros. con un canalización SSH, agregar
pasos de notificación despuéscipi deploy para transmitir éxito, fracaso y automático.
reversiones a Slack o Telegram. Los dos ejemplos siguientes funcionan con GitHub acciones y GitLab CI usando
solo llamadas HTTP estándar, sin dependencias de plataforma adicionales.
flojo
Agregue un paso final que se publique en Slack webhook independientemente del resultado de la implementación. uso
if: always() en GitHub Acciones para que la notificación se active tanto en caso de éxito como de fracaso.
Crear un entrante
Webhook en su espacio de trabajo de Slack y almacene la URL como
SLACK_WEBHOOK_URL en tus secretos de CI.
# GitHub Actions — deploy + Slack notification
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy
id: deploy
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: root
key: ${{ secrets.SERVER_SSH_KEY }}
script: cipi deploy myapp
- name: Notify Slack — success
if: success()
uses: slackapi/slack-github-action@v2
with:
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
webhook-type: incoming-webhook
payload: |
{
"text": ":white_check_mark: *myapp* deployed successfully",
"attachments": [{
"color": "good",
"fields": [
{ "title": "Branch", "value": "${{ github.ref_name }}", "short": true },
{ "title": "By", "value": "${{ github.actor }}", "short": true },
{ "title": "Commit", "value": "${{ github.sha }}", "short": false }
]
}]
}
- name: Notify Slack — failure
if: failure()
uses: slackapi/slack-github-action@v2
with:
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
webhook-type: incoming-webhook
payload: |
{
"text": ":x: *myapp* deploy FAILED — rolling back",
"attachments": [{
"color": "danger",
"fields": [
{ "title": "Branch", "value": "${{ github.ref_name }}", "short": true },
{ "title": "By", "value": "${{ github.actor }}", "short": true },
{ "title": "Run", "value": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}", "short": false }
]
}]
}
- name: Rollback on failure
if: failure()
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: root
key: ${{ secrets.SERVER_SSH_KEY }}
script: cipi deploy myapp --rollback
Para GitLab CI, utilice curl directamente, no se necesita complemento:
# .gitlab-ci.yml — deploy stage with Slack notification
deploy:
stage: deploy
script:
- ssh root@$SERVER_HOST "cipi deploy myapp" && export DEPLOY_STATUS="success" || export DEPLOY_STATUS="failed"
- |
if [ "$DEPLOY_STATUS" = "success" ]; then
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{\"text\":\":white_check_mark: *myapp* deployed by $GITLAB_USER_LOGIN on \`$CI_COMMIT_REF_NAME\`\"}"
else
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{\"text\":\":x: *myapp* deploy FAILED — <$CI_PIPELINE_URL|view pipeline>\"}"
ssh root@$SERVER_HOST "cipi deploy myapp --rollback"
exit 1
fi
Telegrama
Crea un bot de Telegram a través de @BotFather, obtenga el token del bot y busque su ID de chat/grupo.
Guárdalos como TELEGRAM_BOT_TOKEN y TELEGRAM_CHAT_ID en secretos de CI.
# GitHub Actions — deploy + Telegram notification
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy
id: deploy
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: root
key: ${{ secrets.SERVER_SSH_KEY }}
script: cipi deploy myapp
- name: Notify Telegram — success
if: success()
run: |
curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
-d chat_id="${{ secrets.TELEGRAM_CHAT_ID }}" \
-d parse_mode="Markdown" \
-d text="✅ *myapp* deployed successfully%0ABranch: \`${{ github.ref_name }}\`%0ABy: ${{ github.actor }}"
- name: Notify Telegram — failure + rollback
if: failure()
run: |
curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
-d chat_id="${{ secrets.TELEGRAM_CHAT_ID }}" \
-d parse_mode="Markdown" \
-d text="❌ *myapp* deploy FAILED — rolling back%0ABranch: \`${{ github.ref_name }}\`%0A[View run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
ssh -o StrictHostKeyChecking=no -i <(echo "${{ secrets.SERVER_SSH_KEY }}") \
root@${{ secrets.SERVER_HOST }} "cipi deploy myapp --rollback"
GitLab CI equivalente (puro curl, sin dependencias adicionales):
# .gitlab-ci.yml — deploy stage with Telegram notification
deploy:
stage: deploy
script:
- ssh root@$SERVER_HOST "cipi deploy myapp" && RESULT="✅ deployed" || RESULT="❌ FAILED"
- |
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d chat_id="$TELEGRAM_CHAT_ID" \
-d parse_mode="Markdown" \
-d text="*myapp* ${RESULT}%0ABranch: \`$CI_COMMIT_REF_NAME\`%0ABy: $GITLAB_USER_LOGIN"
- |
if echo "$RESULT" | grep -q "FAILED"; then
ssh root@$SERVER_HOST "cipi deploy myapp --rollback"
exit 1
fi
https://api.telegram.org/bot<TOKEN>/getUpdates y busca el
chat.id campo en la respuesta. Para chats privados, simplemente envíe un mensaje al bot primero.
Implementación segura: copia de seguridad antes del lanzamiento
Para un volcado integrado justo antes de que se ejecute Deployer (no se requiere etapa de canalización), use
--snapshot / --snapshot-requiredo habilitar cipi app edit <app> --predeploy-snapshot.
Caso de uso de canalización: una implementación webhook no puede ejecutar un paso de copia de seguridad antes de publicar el código:
los disparos del evento push se despliegan inmediatamente. en un CI/CD canalización, agrega un
dedicado backup etapa que debe triunfar antes deploy comienza. un
El flujo de trabajo de nivel de producción siempre debe crear un punto de restauración. antes el nuevo código va
vivir. Cipi proporciona dos comandos de respaldo complementarios que se asignan a dos niveles de seguridad diferentes:
# local DB snapshot — fast, on-disk, instant rollback $ cipi db backup myapp # → /var/log/cipi/backups/myapp_20260303_143012.sql.gz # S3 backup — DB dump + shared/ folder uploaded to your bucket $ cipi backup run myapp # → s3://your-bucket/cipi/myapp/2026-03-03_143015/db.sql.gz # → s3://your-bucket/cipi/myapp/2026-03-03_143015/shared.tar.gz
Usados juntos en una canalización, le brindan un punto de restauración local rápido y una copia fuera del servidor de la base de datos y todos los archivos cargados. La implementación solo comienza si ambas copias de seguridad se realizan correctamente.
cipi backup configure una vez en el servidor para
vincule sus credenciales S3 antes cipi backup run se puede utilizar.
cipi db backup Funciona sin ninguna configuración: siempre está disponible.
Qué hace cada comando internamente
cipi db backup <app> llamadas
mysqldump --single-transaction --routines --triggers y gzips la salida a
/var/log/cipi/backups/<app>_<timestamp>.sql.gz. El archivo permanece en el
servidor y nunca se elimina automáticamente: agregue un paso de limpieza o un cron si el espacio en disco es importante.
cipi backup run <app> hace dos cosas: vuelca la base de datos con
mariadb-dump --single-transaction en un directorio temporal y archiva todo el
/home/<app>/shared/ carpeta (que contiene .env,
storage/y cualquier archivo subido por el usuario). Luego, ambos archivos se cargan en S3 bajo el
camino cipi/<app>/<timestamp>/. Los archivos temporales se eliminan después de una exitosa
subir.
GitHub Acciones: flujo de trabajo de implementación segura
# .github/workflows/deploy.yml name: Deploy on: push: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: php artisan test backup: runs-on: ubuntu-latest needs: test steps: - name: Local DB backup uses: appleboy/ssh-action@v1 with: host: ${{ secrets.SERVER_HOST }} username: root key: ${{ secrets.SERVER_SSH_KEY }} script: cipi db backup myapp - name: S3 backup (DB + shared) uses: appleboy/ssh-action@v1 with: host: ${{ secrets.SERVER_HOST }} username: root key: ${{ secrets.SERVER_SSH_KEY }} script: cipi backup run myapp deploy: runs-on: ubuntu-latest needs: backup # only runs if backup job succeeds steps: - name: Deploy uses: appleboy/ssh-action@v1 with: host: ${{ secrets.SERVER_HOST }} username: root key: ${{ secrets.SERVER_SSH_KEY }} script: cipi deploy myapp - name: Rollback on failure if: failure() uses: appleboy/ssh-action@v1 with: host: ${{ secrets.SERVER_HOST }} username: root key: ${{ secrets.SERVER_SSH_KEY }} script: | cipi deploy myapp --rollback echo "Deploy failed — rolled back to previous release"
El gráfico de trabajos impone el orden: test → backup → deploy. si
cualquier trabajo falla, los siguientes se omiten. Si el paso de implementación falla, el
rollback El paso se activa automáticamente y restaura la versión anterior del Deployer.
GitLab CI/CD — canalización de implementación segura
# .gitlab-ci.yml
stages:
- test
- backup
- deploy
variables:
APP: myapp
.ssh: &ssh
before_script:
- apt-get install -y openssh-client
- eval $(ssh-agent -s)
- echo "$SERVER_SSH_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- ssh-keyscan -H "$SERVER_HOST" >> ~/.ssh/known_hosts
test:
stage: test
script: php artisan test
only: [main]
backup-local:
stage: backup
<<: *ssh
only: [main]
script:
- ssh root@$SERVER_HOST "cipi db backup $APP"
backup-s3:
stage: backup
<<: *ssh
only: [main]
script:
- ssh root@$SERVER_HOST "cipi backup run $APP"
deploy:
stage: deploy
<<: *ssh
only: [main]
script:
- |
ssh root@$SERVER_HOST "
cipi deploy $APP || {
cipi deploy $APP --rollback
echo 'Deploy failed — rolled back'
exit 1
}
"
after_script:
- echo "Released → https://myapp.com"
backup-local y backup-s3 están en la misma etapa por lo que se ejecutan en paralelo si
tiene varios corredores, lo que reduce el tiempo total de canalización. Ambos deben tener éxito antes de que
deploy comienza la etapa.
Restaurar desde copia de seguridad local
Si necesita revertir la base de datos a la instantánea tomada justo antes de la implementación:
# list available local snapshots $ ls -lh /var/log/cipi/backups/myapp_*.sql.gz # restore the most recent one $ cipi db restore myapp /var/log/cipi/backups/myapp_20260303_143012.sql.gz # also roll back the code release $ cipi deploy myapp --rollback
Restaurar desde la copia de seguridad S3
# list available S3 snapshots for this app $ cipi backup list myapp # download the DB snapshot from S3 $ aws s3 cp s3://your-bucket/cipi/myapp/2026-03-03_143015/db.sql.gz /tmp/db.sql.gz # restore the database $ cipi db restore myapp /tmp/db.sql.gz # (optional) restore shared/ files $ aws s3 cp s3://your-bucket/cipi/myapp/2026-03-03_143015/shared.tar.gz /tmp/shared.tar.gz $ tar -xzf /tmp/shared.tar.gz -C /home/myapp/
.sql.gz archivo a /var/log/cipi/backups/. En un calendario de despliegue muy ocupado,
agregue una limpieza cron o conserve solo los últimos N archivos:ls -t /var/log/cipi/backups/myapp_*.sql.gz | tail -n +6 | xargs rm -fEste ejemplo mantiene las 5 instantáneas más recientes y elimina las más antiguas.
Vista previa de entornos (implementación por sucursal)
Caso de uso de canalización: Los webhooks apuntan a una única URL de producción: no pueden generar una
nueva Cipi aplicación por sucursal. Los entornos de vista previa requieren una CI/CD
tubería que ingresa mediante SSH al servidor, calcula un nombre de aplicación determinista de la rama y
corre cipi app create o cipi deploy respectivamente. Cada no producción
La sucursal puede obtener su propia URL activa: una aplicación Laravel completamente implementada con su propia base de datos, trabajadores y
HTTPS. Este patrón a veces se denomina "aplicaciones de revisión" o "entornos efímeros".
El formato URL utiliza tres barras separadas por guiones, por lo que cada entorno es legible por humanos y globalmente único:
https://develop-acmeco-3a1f9c2e.preview.domain.ltd https://release-1-2-3-acmeco-3a1f9c2e.preview.domain.ltd https://main-acmeco-3a1f9c2e.preview.domain.ltd
Cómo se generan los identificadores
Se derivan tres valores en tiempo de ejecución de la canalización:
# branch name → lowercase, non-alphanum → hyphens, trim edges BRANCH_SLUG=$(echo "$BRANCH" | tr '[:upper:]' '[:lower:]' \ | sed 's/[^a-z0-9]/-/g; s/--*/-/g; s/^-//; s/-$//') # repo/project name → same treatment PROJECT_SLUG=$(echo "$PROJECT" | tr '[:upper:]' '[:lower:]' \ | sed 's/[^a-z0-9]/-/g') # deterministic MD5 hash — same branch always gets the same environment HASH=$(echo -n "${BRANCH_SLUG}${PROJECT_SLUG}" | md5sum | cut -c1-8) # Cipi app username: must be lowercase alphanumeric, 3–32 chars, no hyphens # hex chars (0–9, a–f) are valid; prefix "pr" ensures it starts with a letter APP_NAME="pr${HASH}" # e.g. pr3a1f9c2e # human-readable domain with wildcard base DOMAIN="${BRANCH_SLUG}-${PROJECT_SLUG}-${HASH}.${DEPLOY_WILDCARD_DOMAIN}"
Requisitos previos (configuración única del servidor)
A grabar
*.preview.domain.ltd → <server-ip> en su proveedor DNS. Todos los subdominios
resolver automáticamente; no se necesitan DNS cambios por rama.2. Certificado comodín SSL — obtener un certificado comodín a través del desafío DNS-01 una vez e instalarlo en el servidor. Ver el Dominios comodín sección para obtener instrucciones. La ruta del certificado utilizado por los ejemplos de canalización a continuación es
/etc/letsencrypt/live/preview.domain.ltd/.3. Acceso al repositorio — los ejemplos de canalización utilizan una URL HTTPS con una dirección personal token de acceso (PAT) integrado, por lo que no se necesita configuración de clave de implementación SSH por aplicación. La ficha sólo necesita leer acceso al repositorio.
GitHub Acciones
Agregue estos secretos al repositorio: SERVER_HOST, SERVER_SSH_KEY,
DEPLOY_WILDCARD_DOMAIN (por ej. preview.domain.ltd), GH_PAT (un
PAT detallada con acceso de lectura al repositorio).
# .github/workflows/preview.yml name: Preview on: push: branches-ignore: [main, master] # main branch uses your production pipeline delete: # clean up when a branch is deleted jobs: deploy: if: github.event_name == 'push' runs-on: ubuntu-latest steps: - name: Compute identifiers id: ids run: | BRANCH_SLUG=$(echo "${{ github.ref_name }}" \ | tr '[:upper:]' '[:lower:]' \ | sed 's/[^a-z0-9]/-/g; s/--*/-/g; s/^-//; s/-$//') PROJECT_SLUG=$(echo "${{ github.event.repository.name }}" \ | tr '[:upper:]' '[:lower:]' \ | sed 's/[^a-z0-9]/-/g') HASH=$(echo -n "${BRANCH_SLUG}${PROJECT_SLUG}" | md5sum | cut -c1-8) APP_NAME="pr${HASH}" DOMAIN="${BRANCH_SLUG}-${PROJECT_SLUG}-${HASH}.${{ secrets.DEPLOY_WILDCARD_DOMAIN }}" REPO="https://oauth2:${{ secrets.GH_PAT }}@github.com/${{ github.repository }}.git" echo "app_name=${APP_NAME}" >> "$GITHUB_OUTPUT" echo "domain=${DOMAIN}" >> "$GITHUB_OUTPUT" echo "repo_url=${REPO}" >> "$GITHUB_OUTPUT" - name: Create or update preview uses: appleboy/ssh-action@v1 with: host: ${{ secrets.SERVER_HOST }} username: root key: ${{ secrets.SERVER_SSH_KEY }} script: | APP="${{ steps.ids.outputs.app_name }}" DOMAIN="${{ steps.ids.outputs.domain }}" REPO="${{ steps.ids.outputs.repo_url }}" BRANCH="${{ github.ref_name }}" WILDCARD="/etc/letsencrypt/live/${{ secrets.DEPLOY_WILDCARD_DOMAIN }}" if cipi app show "$APP" &>/dev/null; then echo "→ Updating: $APP" cipi deploy "$APP" else echo "→ Creating: $APP → $DOMAIN" cipi app create \ --user="$APP" \ --domain="$DOMAIN" \ --repository="$REPO" \ --branch="$BRANCH" \ --php=8.5 # Patch nginx to listen on 443 using the pre-installed wildcard cert awk -v cert="$WILDCARD" ' /^ listen 80;/ { print print " listen 443 ssl http2;" print " ssl_certificate " cert "/fullchain.pem;" print " ssl_certificate_key " cert "/privkey.pem;" next } { print } ' "/etc/nginx/sites-available/$APP" > /tmp/_cipi_vhost \ && mv /tmp/_cipi_vhost "/etc/nginx/sites-available/$APP" nginx -t && systemctl reload nginx cipi deploy "$APP" fi - name: Print preview URL run: | echo "" echo " Preview → https://${{ steps.ids.outputs.domain }}" echo "" cleanup: if: github.event_name == 'delete' runs-on: ubuntu-latest steps: - name: Compute identifiers id: ids run: | BRANCH_SLUG=$(echo "${{ github.event.ref }}" \ | tr '[:upper:]' '[:lower:]' \ | sed 's/[^a-z0-9]/-/g; s/--*/-/g; s/^-//; s/-$//') PROJECT_SLUG=$(echo "${{ github.event.repository.name }}" \ | tr '[:upper:]' '[:lower:]' \ | sed 's/[^a-z0-9]/-/g') HASH=$(echo -n "${BRANCH_SLUG}${PROJECT_SLUG}" | md5sum | cut -c1-8) echo "app_name=pr${HASH}" >> "$GITHUB_OUTPUT" - name: Delete preview uses: appleboy/ssh-action@v1 with: host: ${{ secrets.SERVER_HOST }} username: root key: ${{ secrets.SERVER_SSH_KEY }} script: | APP="${{ steps.ids.outputs.app_name }}" if cipi app show "$APP" &>/dev/null; then echo "y" | cipi app delete "$APP" echo "→ Deleted: $APP" else echo "→ Not found, nothing to delete" fi
GitLab CI/CD
Agregue estas CI/CD variables: SERVER_HOST, SERVER_SSH_KEY (tipo de archivo),
DEPLOY_WILDCARD_DOMAIN, GL_TOKEN (un token de acceso al proyecto/grupo con
repositorio_lectura alcance).
# .gitlab-ci.yml stages: - preview - cleanup .ssh_setup: &ssh_setup before_script: - apt-get install -y openssh-client - eval $(ssh-agent -s) - echo "$SERVER_SSH_KEY" | tr -d '\r' | ssh-add - - mkdir -p ~/.ssh - ssh-keyscan -H "$SERVER_HOST" >> ~/.ssh/known_hosts .compute_ids: &compute_ids | BRANCH_SLUG=$(echo "$CI_COMMIT_REF_NAME" \ | tr '[:upper:]' '[:lower:]' \ | sed 's/[^a-z0-9]/-/g; s/--*/-/g; s/^-//; s/-$//') PROJECT_SLUG=$(echo "$CI_PROJECT_NAME" \ | tr '[:upper:]' '[:lower:]' \ | sed 's/[^a-z0-9]/-/g') HASH=$(echo -n "${BRANCH_SLUG}${PROJECT_SLUG}" | md5sum | cut -c1-8) APP="pr${HASH}" DOMAIN="${BRANCH_SLUG}-${PROJECT_SLUG}-${HASH}.${DEPLOY_WILDCARD_DOMAIN}" REPO="https://oauth2:${GL_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" WILDCARD="/etc/letsencrypt/live/${DEPLOY_WILDCARD_DOMAIN}" deploy-preview: stage: preview <<: *ssh_setup except: - main - master script: - *compute_ids - | ssh root@$SERVER_HOST bash -s << ENDSSH APP="$APP" DOMAIN="$DOMAIN" REPO="$REPO" BRANCH="$CI_COMMIT_REF_NAME" WILDCARD="$WILDCARD" if cipi app show "\$APP" &>/dev/null; then echo "Updating: \$APP" cipi deploy "\$APP" else echo "Creating: \$APP → \$DOMAIN" cipi app create \ --user="\$APP" \ --domain="\$DOMAIN" \ --repository="\$REPO" \ --branch="\$BRANCH" \ --php=8.5 awk -v cert="\$WILDCARD" ' /^ listen 80;/ { print print " listen 443 ssl http2;" print " ssl_certificate " cert "/fullchain.pem;" print " ssl_certificate_key " cert "/privkey.pem;" next } { print } ' "/etc/nginx/sites-available/\$APP" > /tmp/_cipi_vhost \ && mv /tmp/_cipi_vhost "/etc/nginx/sites-available/\$APP" nginx -t && systemctl reload nginx cipi deploy "\$APP" fi ENDSSH - echo "Preview → https://$DOMAIN" cleanup-preview: stage: cleanup <<: *ssh_setup only: - branches when: manual # or trigger on MR merge via rules: script: - *compute_ids - | ssh root@$SERVER_HOST " APP='$APP' if cipi app show \"\$APP\" &>/dev/null; then echo 'y' | cipi app delete \"\$APP\" fi "
cleanup-preview automáticamente cuando se envía una solicitud de fusión.
fusionados añadiendo un rules: bloque que comprueba
$CI_MERGE_REQUEST_EVENT_TYPE == "merge_train" o usando un dedicado
workflow: con if: $CI_PIPELINE_SOURCE == "merge_request_event".
Notas y límites
cipi app list periódicamente y elimine las vistas previas obsoletas.El parche nginx SSL no es idempotente — si el oleoducto corre
cipi app create dos veces (por ejemplo, debido a un reintento), el awk parche será
aplicado nuevamente. El hash asegura APP_NAME es determinista, por lo que
if cipi app show guard evita la doble creación en condiciones normales.evitar correr
cipi ssl install en una aplicación de vista previa: lo hará
sobrescriba la configuración del certificado comodín con un certificado Let's Encrypt por dominio que fallará (el
El dominio no tiene un registro DNS dedicado, solo el comodín).