cipi deploy

Cipi utilise Déployeur pour tous les déploiements. Chaque déploiement est atomique : une nouvelle version répertoire est entièrement préparé avant le current le lien symbolique est échangé, donc le trafic n'est jamais interrompu.

Depuis v4.5.4 Cipi lots Déployeur 8, ce qui nécessite PHP ≥ 8,3. cipi deploy et cipi deploy --rollback abandonner avec un message de mise à niveau clair avant appeler Deployer lorsqu'une application est toujours épinglée vers une ancienne version PHP – changez-la avec cipi app edit <app> --php=8.3 (ou supérieur) en premier.

Déployer un pipeline

Deployer et Composer s'exécutent avec le version PHP configurée de l'application (par ex. /usr/bin/php8.5), et non la valeur par défaut du système. Ceci s'applique à cipi deploy, cipi deploy --rollback, crontab déploie des déclencheurs, cipi sync import déploie, et le deploy / composeralias dans l'utilisateur de l'application .bashrc.

  1. Arrêter les travailleurs de file d'attente (cipi worker stop)
  2. Cloner le dépôt dans releases/N/
  3. Courir composer install --no-dev (avec PHP de l'application)
  4. Lien shared/.env et shared/storage/
  5. Courir artisan migrate --force
  6. Courir artisan optimize
  7. Courir artisan storage:link
  8. Échange current lien symbolique atomiquement
  9. Redémarrer les travailleurs de file d'attente
  10. Élaguez les anciennes versions (conservez les 5 dernières)
coup
$ 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)

Depuis v5.0, s'inscrire pré-déployer des instantanés de base de données sont disponibles via --snapshot / --snapshot-required ou un paramètre d'application permanent - voir Pré-déployer des instantanés de base de données. Octane applications utilisent le laravel-octane.php Modèle de déploiement (recharger/redémarrer Octane lors du déploiement) ; activer le nœud construit avec cipi app edit <app> --node-build='…'.

Si un déploiement est interrompu (par exemple par une erreur réseau), le déployeur peut laisser un fichier de verrouillage derrière lui. Utiliser cipi deploy myapp --unlock pour le supprimer avant de le redéployer.

Pré-déployer des instantanés de base de données

Depuis v5.0, cipi deploy <app> peut vider la base de données avant le pipeline de publication s'exécute. Les instantanés atterrissent sous /var/log/cipi/backups/ — le même chemin utilisé par cipi db backup.

coup
$ cipi deploy shop --snapshot            # dump first; warn and continue on failure
$ cipi deploy shop --snapshot-required   # dump first; abort deploy if snapshot fails

Que se passe-t-il

  1. Avant le démarrage du déploiement, Cipi effectue un dump de base de données dans /var/log/cipi/backups/.
  2. Avec --snapshot-required, un dump échoué (ou un moteur de base de données manquant) blocs le déploiement.
  3. Avec --snapshot seul, un dump échoué imprime un avertissement et le déploiement continue.
--snapshotDump opt-in avant le déploiement ; avertir et continuer si l'instantané ne peut pas être pris.
--snapshot-requiredMême dump, mais le déploiement échoue lorsque l'instantané (ou le moteur) n'est pas disponible.

Activer à chaque déploiement

Activez-le de manière permanente pour une application afin que chaque déploiement (CLI, webhook ou pipeline) prenne d'abord un instantané :

coup
$ cipi app edit shop --predeploy-snapshot
cipi deploy --rollback restaure le précédent coder libération seulement. C'est le cas pas restaurer la base de données. Si vous avez besoin du dump de pré-déploiement, utilisez cipi db restore.

Pour les workflows de pipeline qui archivent également shared/ à S3 avant la sortie, voir Déploiement sécurisé : sauvegarde avant la publication.

cipi app deploy-config

Depuis v5.0.3, gérez les options de recettes de déploiement durables stockées dans apps.json et appliqué en régénérant deploy.php à partir du modèle - une alternative sûre à l'édition de PHP de forme libre.

coup
$ 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

REPOS : GET|PUT /api/apps/{name}/deploy-config (capacité apps-deploy-config, API 1.14+ / Cipi 5.0.3+). MCP : AppDeployConfigShow, AppDeployConfigUpdate.

auth.json

Gérer le auth.json fichier pour une application. Ce fichier se trouve à /home/<app>/shared/auth.json et est automatiquement lié symboliquement à chaque version par Déployeur - exactement comme .env. Utilisez-le pour stocker des données d'identification structurées (par exemple API clés, indicateurs de fonctionnalité ou toute charge utile JSON) que votre application Laravel peut lire au moment de l'exécution.

coup
$ 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

REPOS : GET|POST|PUT|DELETE /api/apps/{name}/auth (capacité apps-auth, API 1.14+) — Composer/structuré JSON, distinct de HTTP Basic Auth. MCP : AppAuthJsonShow, AppAuthJsonCreate, AppAuthJsonUpdate, AppAuthJsonDelete.

Détails de la commande

Commande Descriptif
cipi auth create <app> Crée shared/auth.json avec la structure initiale {"users":[]}, définit les autorisations sur 640 (propriétaire app:app), et ajoute auth.json à shared_files dans la configuration du déploiement de l'application afin qu'il soit lié symboliquement à chaque déployer.
cipi auth edit <app> Ouvre shared/auth.json dans $EDITOR (retombe à nano). Après la fermeture de l'éditeur, valide le JSON avec jq et avertit si le fichier est mal formé.
cipi auth show <app> Imprime le contenu de shared/auth.json formaté avec jq.
cipi auth delete <app> Demande une confirmation, puis supprime shared/auth.json et supprime le auth.json entrée de shared_files dans le déployeur de l'application configuration.

Intégration du déployeur

cipi auth create ajoute automatiquement auth.json à la shared_files liste dans /home/<app>/.deployer/deploy.php, et cipi auth delete le supprime. Cela signifie que le fichier est traité exactement comme .env: il persiste dans toutes les versions et n'est jamais écrasé par un déploiement.

Chaque cipi auth l'opération est enregistrée via log_action pour auditabilité. Le AUTH La section est également répertoriée dans la sortie de cipi help.

Fournisseurs Git

Cipi est prêt à travailler avec API et GitHub mais il prend en charge n'importe quel autre fournisseur Git qui prend en charge les clés de déploiement SSH – pas de dépendance vis-à-vis du fournisseur.

Pour self-hosted ou les serveurs Git personnalisés, vous devez faire confiance à l'empreinte digitale de l'hôte du serveur avant Le déployeur peut cloner via SSH. Utilisez le --trust-host drapeau pour ajouter l'empreinte digitale au utilisateur de l'application ~/.ssh/known_hosts automatiquement :

coup
# 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
Lorsqu'un port non standard est spécifié, Cipi écrit également le Host / Port accès à l'utilisateur de l'application ~/.ssh/config donc ce déployeur peut atteindre le serveur sans aucune configuration supplémentaire.

Configuration automatique de Git

Si vous enregistrez un API ou GitHub Jeton d'accès personnel, Cipi ajoute automatiquement la clé de déploiement SSH et crée le webhook sur le référentiel à chaque fois que vous exécutezcipi app create. Aucune étape manuelle requise.

Enregistrer un jeton

coup
# 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

Autorisations du jeton GitHub

Besoin de jetons à grain fin (recommandé) Administration et Webhooks réglé sur Lire et écrire sur les référentiels cibles. Jetons classiques besoin du repo portée.

Autorisations du jeton GitLab

Le api la portée est le minimum requis — GitLab n'offre pas une portée plus granulaire qui couvre à la fois les clés de déploiement et les webhooks.

Cycle de vie automatique

Événement Ce que Cipi fait automatiquement
app create Ajoute la clé de déploiement + crée webhook sur le référentiel via API. Le résumé montre "auto-configuré ✓" au lieu d'instructions manuelles.
app edit --repository=... Supprime la clé de déploiement + webhook de l'ancien référentiel, puis les ajoute au nouveau.
app delete Supprime la clé de déploiement + webhook du référentiel avant de supprimer l'application.

cipi git commandes

Commande Descriptif
cipi git status Afficher l'état de connexion du fournisseur et les détails d'intégration par application (ID de clé de déploiement, webhook ID)
cipi git github-token <token> Enregistrez un jeton d'accès personnel GitHub
cipi git gitlab-token <token> Enregistrez un jeton d'accès personnel GitLab
cipi git gitlab-url <url> Définir l'URL de base pour une instance self-hosted GitLab
cipi git remove-github Supprimez le jeton GitHub stocké
cipi git remove-gitlab Supprimez le jeton GitLab et l'URL stockés

Configuration manuelle (repli)

La configuration automatique est ignorée lorsqu'aucun jeton n'est configuré, lorsque l'appel API échoue (autorisations incorrectes, référentiel introuvable, limite de débit), ou lorsque le référentiel est hébergé chez un fournisseur autre que GitHub ou GitLab (par exemple Gitea, Forgejo, Bitbucket). Dans tous ces cas, Cipi revient au flux de travail manuel et la création de l'application se déroule normalement.

Pour configurer manuellement la clé de déploiement et webhook :

coup
# 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

Ajoutez-les ensuite dans les paramètres du référentiel de votre fournisseur :

  • Clé de déploiement — API : Paramètres → Clés de déploiement → Ajouter une clé de déploiement; GitLab : Paramètres → Référentiel → Clés de déploiement
  • Webhook — API : Paramètres → Webhooks → Ajouter webhook; API : Paramètres → Webhooks → Ajouter un nouveau webhook. Définissez l'URL de la charge utile et secret des valeurs affichées par cipi deploy myapp --webhook
Si vous supprimez un jeton de fournisseur après la création d'applications avec la configuration automatique, Cipi ne le fera pas. être en mesure de nettoyer les clés de déploiement et les webhooks lorsque vous supprimez ou modifiez ces applications. Un avertissement est affichés et vous devrez les supprimer manuellement des paramètres du référentiel du fournisseur.

Personnalisation du script de déploiement

La configuration de déploiement pour chaque application est stockée à :

/home/monapplication/.deployer/deploy.php

Ce fichier est généré automatiquement par Cipi lors app create et mis à jour automatiquement lorsque vous changez la version PHP ou déployez la branche via cipi app edit. Vous pouvez le modifier pour le personnaliser le pipeline de déploiement, mais vous devez comprendre les implications avant de le faire.

Pipeline de déploiement par défaut

Le généré automatiquement deploy.php exécute ces tâches dans l'ordre :

php
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

Ajout de tâches personnalisées

Vous pouvez ajouter des tâches avant ou après n'importe quelle étape. Pour un exemple complet de build frontend (npm install && npm run build), voir Création d'actifs front-end ci-dessous. Autres exemples courants :

php
// 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');

Création d'actifs frontend (npm / Vite)

Il n'y a pas de dédié cipi Indicateur CLI pour les builds frontend (par ex. npm install && npm run build). Personnalisation deploy.php est l’approche soutenue et attendue — définir un Déployeur task() et accroche-le avec after() ou before(). Vous ne le faites pas il faut éviter cela ; l'extension du pipeline est exactement à quoi sert le fichier.

Cipi installations Node.js et npm sur le serveur lors de l'installation. Vérifiez qu'ils sont disponibles comme l'utilisateur de l'application :

coup
$ ssh myapp@your-server-ip
myapp@server:~$ node -v && npm -v

S'engager package.json et package-lock.json à votre référentiel. Accrochez la construction après deploy:shared donc .env est lié (Vite lit VITE_* variables à partir de là) et avant deploy:symlink donc Les ressources compilées existent dans la version avant sa mise en ligne.

Ajoutez le bloc ci-dessous à la en bas de /home/myapp/.deployer/deploy.php, sous les définitions de tâches générées automatiquement par Cipi :

php
// ── 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');

Cela équivaut à npm install && npm run build à chaque déploiement. Préférer npm ci en production lorsque package-lock.json est engagé - c'est plus rapide et reproductible. Utiliser npm install au lieu de cela seulement si vous ne verrouillez pas les dépendances.

Si vous avez besoin d'étapes d'installation et de construction distinctes (par exemple, pour mettre en cache node_modules à travers les versions), divisez-les en deux tâches :

php
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');

Pour accélérer les déploiements ultérieurs, vous pouvez conserver les dépendances entre les versions en ajoutant node_modules vers les répertoires partagés de Deployer (facultatif — uniquement si votre projet prend en charge ça):

php
add('shared_dirs', ['node_modules']);

Modifiez le fichier sur le serveur en tant qu'utilisateur de l'application, puis testez avec cipi deploy myapp:

coup
$ 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
Alternative : courir npm ci && npm run build dans GitHub Actions ou GitLab CI avant l'étape de déploiement SSH, de sorte que le serveur ne reçoit que des actifs prédéfinis. Voir Pipelines CI/CD – Déploiement SSH.

Exécution de commandes artisan supplémentaires

php
// Seed only in specific environments
task('artisan:db:seed', function () {
    run('{{bin/php}} {{release_path}}/artisan db:seed --force');
});
Cipi peut écraser le déploiement.php quand tu cours cipi app edit myapp --php=X ou cipi app edit myapp --branch=X. Sauvegarder vos personnalisations ou conservez-les dans une section clairement séparée des blocs gérés par Cipi. Un le modèle sûr consiste à placer toutes les tâches personnalisées au bas du fichier après la tâche par défaut définition.

Désactiver une étape par défaut

Pour ignorer une tâche (par exemple si vous gérez les migrations manuellement), commentez-la ou supprimez-la du deploy définition de la tâche :

php
// 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',
]);

Tester vos modifications

Après édition deploy.php, effectuez toujours un test de déploiement avant de passer en production :

coup
$ 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
Le journal de déploiement est toujours disponible sur ~/logs/deploy.log ou via cipi app logs myapp --type=deploy. Vérifiez-le d'abord lors du dépannage d'un échec déployer.

Déployer et CI/CD – Présentation

Avec Cipi, CI (construire et tester) et CD (mise en production) peut être divisés ou combinés. Chaque déploiement fonctionne finalement de la même manière Déployeur canalisation sur le serveur — cloner, composer install, migrations, échange de liens symboliques, redémarrage du travailleur. Quels sont les changements qu'est-ce qui déclenche ce pipeline.

Cipi prend en charge deux modèles de déclencheurs. Pour la plupart des applications Laravel, commencez par webhook + CipiAgent chemin. Passez à un pipeline CI/CD complet lorsque vous avez besoin de portes, de sauvegardes ou une orchestration d'infrastructure qu'un simple push hook ne peut pas exprimer.

Deux façons de déclencher un déploiement

Webhook + Cipi Agent (recommandé) Pipeline CI/CD via SSH
Déclencheur Le fournisseur Git POST vers /cipi/webhook en poussée GitHub Actions / GitLab exécutions de tâches CI cipi deploy via SSH
Accès au serveur depuis CI Aucun – seulement HTTPS sur le domaine de votre application Clé SSH dédiée stockée en tant que secret CI
Tests préalables au déploiement Exécuter localement ou dans un travail CI distinct ; le déploiement se déclenche toujours en cas de poussée, sauf si vous désactivez le webhook Natif : l'étape de déploiement ne s'exécute qu'après needs: test (ou équivalent) passe
Sauvegarde avant la sortie Manuel ou cron sur le serveur Travail de pipeline - voir déploiement sécurisé
Prévisualiser/examiner les applications Non pris en charge dès la sortie de la boîte Pipeline crée des applications Cipi par branche – voir aperçu environnements
Complexité de configuration Faible — composer require cipi/agent + un webhook Moyen — Clé SSH, secrets, flux de travail YAML

Quelle approche dois-je utiliser ?

Cas d'utilisation Approche recommandée Où lire la suite
Application unique Laravel, push-to-déploiement sur main Webhook + Agent Configuration de Webhook
Déployer uniquement si les tests CI réussissent Pipeline SSH (désactiver la production webhook) Déploiement du pipeline SSH
Sauvegarde de base de données + fichiers avant chaque version de production Pipeline SSH Déploiement sécurisé avec sauvegarde
Alertes Slack / Telegram sur le résultat du déploiement Pipeline SSH Déployer les notifications
URL éphémère par branche de fonctionnalité (évaluer les applications) Pipeline SSH Environnements de prévisualisation
Déployer plusieurs applications sur un serveur à partir d'un seul dépôt Soit — webhook par application, soit un pipeline avec parallèle cipi deploy Déploiement multi-applications
Choisissez un déclencheur par application. Ne laissez pas une production webhook active tout en le pipeline en cours d'exécution se déploie lors du push : deux exécutions simultanées du déployeur sont en conflit sur le fichier de verrouillage. Utiliser cipi deploy myapp --unlock si un cadenas coincé est laissé sur place.

Déploiements automatiques : agent Cipi et webhook

cipi-agent (cipi/agent) est un package Laravel qui expose POST /cipi/webhook dans votre application en cours d’exécution. Lorsque GitHub ou GitLab envoie un push événement, l'agent valide la signature de la charge utile, en accuse réception immédiatement et met en file d'attente un déploiement sur le serveur - pas de SSH du coureur CI, non sudo, aucun port entrant ouvert au-delà de HTTPS.

Comment fonctionne le flux webhook

Le design sépare accusé de réception rapide HTTP de Déployeur lent travail. Un déploiement peut prendre plusieurs minutes ; Les fournisseurs Git expirent les appels webhook HTTP après environ 10 secondes. Cipi résout ce problème avec un fichier d'indicateur et la crontab de l'utilisateur de l'application.

flux
  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       │

Le déployeur s'exécute toujours en tant que utilisateur de l'application Linux (par ex. myapp), avec le corriger les autorisations PHP binaires et fichiers - le même contexte qu'un manuel cipi deploy myapp. Le webhook ne s'adresse jamais directement au déployeur ; ça laisse seulement tomber le fichier de déclenchement que la crontab de Cipi surveille déjà.

Conditions préalables

Exigence Pourquoi
Application Cipi créée avec le référentiel Git La clé de déploiement doit cloner le dépôt - voir Configuration automatique de Git
Au moins un déploiement manuel réussi Le package d'agent doit être présent dans le current version avant le webhook l'itinéraire existe
composer require cipi/agent dans le projet Enregistre le /cipi/webhook validation de l'itinéraire et de la signature
Webhook URL accessible sur HTTPS Les fournisseurs Git nécessitent une URL publique ; utiliser cipi ssl install d'abord
CIPI_WEBHOOK_TOKEN dans shared/.env Généré automatiquement à cipi app create; partagé dans toutes les versions

Configuration étape par étape

1. Créez l'application et déployez-la une fois manuellement pour que le serveur puisse cloner votre dépôt :

coup
$ cipi app create --user=myapp --domain=myapp.com \
    --repository=git@github.com:you/myapp.git --branch=main --php=8.5
$ cipi deploy myapp

2. Installez l'agent Cipi dans votre projet Laravel localement, validez et poussez :

coup
$ 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. Configurez le webhook. Si vous avez enregistré un jeton GitHub ou GitLab, Cipi peut avoir déjà créé le webhook pendant app create — vérifier auprès de cipi git status. Sinon, récupérez l'URL et le secret :

coup
$ cipi deploy myapp --webhook

Ajoutez le webhook dans votre fournisseur Git :

Fournisseur URL de la charge utile Champ secret Événements
GitHub https://myapp.com/cipi/webhook Secret → valeur de --webhook Juste le push événement
GitLab https://myapp.com/cipi/webhook Jeton secret → même valeur Événements push

4. Restreindre à votre branche de déploiement (recommandé pour la production) :

env
CIPI_DEPLOY_BRANCH=main

Mettez ceci dans shared/.env via cipi app env myapp. Pousse vers d’autres branches recevoir un skipped réponse et aucune exécution de déploiement.

5. Vérifiez. Envoyez un petit commit à main et regardez le journal de déploiement :

coup
$ cipi app logs myapp --type=deploy
# or on the server as the app user:
$ tail -f /home/myapp/logs/deploy.log

Environ une minute après la livraison de webhook, une nouvelle version de Deployer devrait apparaître. Confirmez le commit en direct avec php artisan cipi:status ou le santé vérifier point final.

Configuration automatique de Git

Quand un Jeton GitHub ou GitLab est configuré sur le serveur, Cipi enregistre la clé de déploiement et crée automatiquement le webhook à chaque cipi app create. Le résumé de l'application affiche "auto-configuré ✓" au lieu du manuel instructions. Événements du cycle de vie (app edit --repository, app delete) garder clés et webhooks synchronisés.

Dépannage

Symptôme Cause probable Corriger
Webhook renvoie 404 Agent pas encore déployé Courir cipi deploy myapp après avoir ajouté cipi/agent à composer.json
Webhook renvoie 403 / signature invalide Inadéquation secrète Recopier le jeton de cipi deploy myapp --webhook dans les paramètres du fournisseur
200 OK mais pas de déploiement Branche filtrée Vérifier CIPI_DEPLOY_BRANCH correspond à la branche poussée
Déploiement bloqué/erreur de verrouillage Déploiement précédent interrompu cipi deploy myapp --unlock puis réessaye
Le déploiement s'exécute deux fois en une seule fois Webhook + pipeline tous deux actifs Désactivez un déclencheur - voir aperçu
L'agent prend également en charge les déploiements manuels et déclenchés par l'IA via MCP deploy outil — il utilise la même chose .deploy-trigger mécanisme. Voir CipiAgent pour les contrôles de santé, MCP et les fonctionnalités d'anonymisation.

Pipelines CI/CD – Déploiement SSH

Lorsque le modèle webhook ne suffit pas, exécutez GitHub Actions ou GitLab CI/CD tâches qui se connectent en SSH au serveur et invoquent cipi deploy. C'est le le bon choix chaque fois que le déploiement doit être conditionnel — gated sur tests, précédés de sauvegardes, suivi de notifications ou orchestrer de nouvelles applications en avant-première.

Quand vous avez besoin d'un pipeline au lieu d'un webhook

  • Porte de qualité — courir php artisan test, analyse statique ou frontend construit avant qu'un code n'atteigne la production
  • Libération en toute sécurité — instantané de la base de données et shared/ à S3 avant échanger le lien symbolique (déploiement sécurisé)
  • Visibilité de l'équipe - publier le succès/l'échec sur Slack ou Telegram avec restauration activée échec (déployer des notifications)
  • Examiner les applications — créer ou mettre à jour une application Cipi complète par branche de fonctionnalités (environnements de prévisualisation)
  • Monorepo multi-applications — déployer frontend et api dans parallèle après un seul travail de test

Pour ces flux de travail, désactiver la production webhook (ou ne jamais en créer un) donc seulement le pipeline déclenche le déploiement. Vous pouvez toujours utiliser l'agent Cipi dans l'application pour les contrôles de santé et MCP.

Accès SSH pour CI

Générer un dédié ed25519 paire de clés pour le coureur CI. Ajoutez le clé publique à /root/.ssh/authorized_keys sur le serveur (ou le cipi utilisateur si vous préférez sudo cipi deploy) et stockez le clé privée comme un secret CI. Ne réutilisez jamais les clés de déploiement Git ou les clés SSH personnelles.
coup
# 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

Magasin SERVER_HOST (IP du serveur ou nom d'hôte) à côté SERVER_SSH_KEY dans votre secrets du référentiel (GitHub) ou variables CI/CD (GitLab).

GitHub Actions : tester puis déployer

Ajoutez la clé privée en tant que secret du référentiel nommé SERVER_SSH_KEY et l'IP du serveur comme SERVER_HOST.

yaml
# .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

Pour une restauration en cas d'échec, étendez l'action de script :

yaml
          script: |
            sudo cipi deploy myapp || (sudo cipi deploy myapp --rollback && exit 1)

GitLab CI/CD

Ajoutez la clé privée en tant que variable CI/CD nommée SERVER_SSH_KEY (tapez : Fichier) et le serveur IP comme SERVER_HOST.

yaml
# .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"

Avec rollback en cas d'échec :

yaml
  script:
    - ssh root@$SERVER_HOST "cipi deploy myapp || (cipi deploy myapp --rollback && exit 1)"

Déploiement multi-applications

Si le même pipeline gère plusieurs applications sur le même serveur :

yaml
# 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

Modèles de pipeline avancés

Une fois le déploiement SSH fonctionnel, composez ces sections en un seul workflow de production :

Modèle Ce que le pipeline ajoute Guider
Notifications Message Slack ou Telegram sur le succès, l'échec et la restauration automatique Déployer les notifications
Déploiement sécurisé cipi db backup + cipi backup run avant cipi deploy; restauration en cas d'échec Déploiement sécurisé avec sauvegarde
Environnements de prévisualisation Créer/mettre à jour/supprimer des applications Cipi par branche avec le caractère générique DNS + SSL Environnements de prévisualisation

Une configuration mature typique utilise le webhook pour une application de mise en scène (retour instantané sur chaque pousser) et un canalisation pour la production (tests → sauvegarde → déployer → notifier). Chaque application a son propre déclencheur : ils ne sont jamais en conflit car ils ciblent différents utilisateurs de l'application Cipi.

Déployer les notifications

Cas d'utilisation du pipeline : le chemin webhook se déploie silencieusement — Git renvoie 200 et l'équipe ne le découvre que s'ils regardent les journaux. Avec un Pipeline SSH, ajouter étapes de notification aprèscipi deploy pour diffuser le succès, l'échec et l'automatique retours vers Slack ou Telegram. Les deux exemples ci-dessous fonctionnent avec GitHub Actions et GitLab CI en utilisant uniquement les appels HTTP standard — pas de dépendances de plate-forme supplémentaires.

Mou

Ajoutez une étape finale qui publie sur un Slack webhook quel que soit le résultat du déploiement. Utiliser if: always() dans GitHub Actions afin que la notification se déclenche à la fois en cas de succès et d'échec.

Créer un Entrant Webhook dans votre espace de travail Slack et stockez l'URL sous SLACK_WEBHOOK_URL dans vos secrets CI.

yaml
# 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

Pour GitLab CI, utilisez curl directement — aucun plugin n'est nécessaire :

yaml
# .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

Télégramme

Créez un bot Telegram via @BotPère, récupérez le jeton du bot et trouvez votre identifiant de discussion/groupe. Conservez-les comme TELEGRAM_BOT_TOKEN et TELEGRAM_CHAT_ID dans les secrets de CI.

yaml
# 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 Équivalent CI (pur curl, pas de dépendances supplémentaires) :

yaml
# .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
Pour trouver votre identifiant de chat Telegram, ajoutez le bot au groupe/canal cible, puis appelez https://api.telegram.org/bot<TOKEN>/getUpdates et cherche le chat.id champ dans la réponse. Pour les discussions privées, envoyez d’abord un message au bot.

Déploiement sécurisé : sauvegarde avant la publication

Pour un vidage intégré juste avant l'exécution de Deployer (aucune étape de pipeline requise), utilisez --snapshot / --snapshot-requiredou activer cipi app edit <app> --predeploy-snapshot.

Cas d'utilisation du pipeline : un déploiement webhook ne peut pas exécuter une étape de sauvegarde avant de publier le code - l'événement push se déclenche immédiatement. Dans un CI/CD pipeline, ajoutez un dédié backup étape qui doit réussir avant deploy démarre. Un le flux de travail de qualité production doit toujours créer un point de restauration avant le nouveau code va vivre. Cipi fournit deux commandes de sauvegarde complémentaires qui correspondent à deux niveaux de sécurité différents :

coup
# 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

Utilisés ensemble dans un pipeline, ils vous offrent à la fois un point de restauration local rapide et une copie hors serveur de la base de données et tous les fichiers téléchargés. Le déploiement ne démarre que si les deux sauvegardes réussissent.

Pré-requis : courir cipi backup configure une fois sur le serveur pour associez vos identifiants S3 avant cipi backup run peut être utilisé. cipi db backup fonctionne sans aucune configuration — il est toujours disponible.

Ce que fait chaque commande en interne

cipi db backup <app> appels mysqldump --single-transaction --routines --triggers et compresse la sortie dans /var/log/cipi/backups/<app>_<timestamp>.sql.gz. Le fichier reste sur le serveur et n'est jamais supprimé automatiquement - ajoutez une étape de nettoyage ou un cron si l'espace disque est important.

cipi backup run <app> fait deux choses : vide la base de données avec mariadb-dump --single-transaction dans un répertoire temporaire et archive l'intégralité /home/<app>/shared/ dossier (qui contient .env, storage/, et tous les fichiers téléchargés par l'utilisateur). Les deux archives sont ensuite téléchargées vers S3 sous le chemin cipi/<app>/<timestamp>/. Les fichiers temporaires sont supprimés après une réussite télécharger.

GitHub Actions : flux de travail de déploiement sécurisé

yaml
# .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"

Le graphique de tâches applique l'ordre : testbackupdeploy. Si toute tâche échoue, les suivantes sont ignorées. Si l'étape de déploiement elle-même échoue, le rollback L’étape se déclenche automatiquement et restaure la version précédente de Deployer.

GitLab CI/CD — pipeline de déploiement sécurisé

yaml
# .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 et backup-s3 sont dans la même étape donc ils fonctionnent en parallèle si vous disposez de plusieurs exécuteurs, ce qui réduit le temps global du pipeline. Les deux doivent réussir avant que deploy l'étape commence.

Restaurer à partir d'une sauvegarde locale

Si vous devez restaurer la base de données vers l'instantané pris juste avant le déploiement :

coup
# 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

Restaurer à partir de la sauvegarde S3

coup
# 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/
Les sauvegardes locales ne sont jamais supprimées automatiquement. Chaque déploiement ajoute un nouveau .sql.gz fichier à /var/log/cipi/backups/. Avec un calendrier de déploiement chargé, ajoutez un nettoyage cron ou ne conservez que les N derniers fichiers :

ls -t /var/log/cipi/backups/myapp_*.sql.gz | tail -n +6 | xargs rm -f

Cet exemple conserve les 5 instantanés les plus récents et supprime les plus anciens.

Environnements de prévisualisation (déploiement par branche)

Cas d'utilisation du pipeline : les webhooks pointent vers une seule URL de production – ils ne peuvent pas créer de nouvelle application Cipi par branche. Les environnements de prévisualisation nécessitent un CI/CD canalisation qui se connecte en SSH au serveur, calcule un nom d'application déterministe à partir de la branche et court cipi app create ou cipi deploy par conséquent. Chaque non-production La branche peut obtenir sa propre URL en direct - une application Laravel entièrement déployée avec sa propre base de données, ses propres travailleurs et HTTPS. Ce modèle est parfois appelé « évaluer les applications » ou « environnements éphémères ».

Le format URL utilise trois slugs séparés par des tirets, afin que chaque environnement soit lisible par l'homme et unique au monde :

exemples d'URL
https://develop-acmeco-3a1f9c2e.preview.domain.ltd
https://release-1-2-3-acmeco-3a1f9c2e.preview.domain.ltd
https://main-acmeco-3a1f9c2e.preview.domain.ltd

Comment les identifiants sont générés

Trois valeurs sont dérivées lors de l'exécution du pipeline :

coup
# 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}"

Prérequis (configuration unique du serveur)

1. Caractère générique DNS — ajouter un A enregistrer *.preview.domain.ltd → <server-ip> chez votre fournisseur DNS. Tous les sous-domaines résoudre automatiquement ; aucune modification DNS par branche n'est nécessaire.

2. Certificat générique SSL - obtenez un certificat générique via le défi DNS-01 une fois et installez-le sur le serveur. Voir le Domaines génériques section pour les instructions. Le chemin du certificat utilisé par les exemples de pipeline ci-dessous est /etc/letsencrypt/live/preview.domain.ltd/.

3. Accès au référentiel — les exemples de pipeline utilisent une URL HTTPS avec un nom personnel jeton d'accès (PAT) intégré, donc aucune configuration de clé de déploiement SSH par application n'est nécessaire. Le jeton n'a besoin que lire accès au référentiel.

GitHub Actions

Ajoutez ces secrets au référentiel : SERVER_HOST, SERVER_SSH_KEY, DEPLOY_WILDCARD_DOMAIN (par ex. preview.domain.ltd), GH_PAT (un PAT à granularité fine avec accès en lecture au dépôt).

yaml
# .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

Ajoutez ces variables CI/CD : SERVER_HOST, SERVER_SSH_KEY (Type de fichier), DEPLOY_WILDCARD_DOMAIN, GL_TOKEN (un jeton d'accès au projet/groupe avec read_repository portée).

yaml
# .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
      "
Dans GitLab, vous pouvez déclencher cleanup-preview automatiquement lorsqu'une demande de fusion est fusionné en ajoutant un rules: bloc qui vérifie $CI_MERGE_REQUEST_EVENT_TYPE == "merge_train" ou en utilisant un workflow: avec if: $CI_PIPELINE_SOURCE == "merge_request_event".

Remarques et limites

Chaque application en avant-première est une application Cipi complète. — il obtient son propre utilisateur Linux, sa base de données et son FPM pool, travailleur Supervisor et crontab. Sur un petit VPS, cela s'accumule rapidement. Courir cipi app list périodiquement et supprimez les aperçus obsolètes.

Le patch nginx SSL n'est pas idempotent — si le pipeline fonctionne cipi app create deux fois (par exemple suite à une nouvelle tentative), le awk le patch sera appliqué à nouveau. Le hachage assure APP_NAME est déterministe, donc le if cipi app show La protection empêche la double création dans des conditions normales.

Évitez de courir cipi ssl install sur une application de prévisualisation - ce sera remplacer la configuration du certificat générique par un certificat Let's Encrypt par domaine qui échouera (le le domaine n'a pas d'enregistrement DNS dédié, seulement le caractère générique).