1008 lines
23 KiB
Markdown
1008 lines
23 KiB
Markdown
|
|
# CI/CD pour déployer Kirby
|
||
|
|
|
||
|
|
Automatiser le déploiement de Kirby CMS avec GitLab CI ou Forgejo Actions (alternatives libres).
|
||
|
|
|
||
|
|
**🆓 Philosophie :** Ce guide privilégie les solutions **libres et open-source** :
|
||
|
|
- **Forgejo** : Alternative libre à GitHub, auto-hébergeable
|
||
|
|
- **GitLab** : Solution mature avec version Community (libre)
|
||
|
|
- Pas de dépendance aux services propriétaires
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📋 Table des matières
|
||
|
|
|
||
|
|
1. [Concepts de base](#concepts-de-base)
|
||
|
|
2. [GitLab CI (SSH/rsync)](#gitlab-ci-sshrsync)
|
||
|
|
3. [Forgejo Actions (SSH/rsync)](#forgejo-actions-sshrsync)
|
||
|
|
4. [Forgejo Actions (FTP)](#forgejo-actions-ftp)
|
||
|
|
5. [Image Docker optimisée](#image-docker-optimisée-pour-kirby)
|
||
|
|
6. [Configuration des secrets](#configuration-des-secrets)
|
||
|
|
7. [Troubleshooting](#troubleshooting)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Concepts de base
|
||
|
|
|
||
|
|
### Workflow typique
|
||
|
|
|
||
|
|
```
|
||
|
|
1. Push sur main/master
|
||
|
|
↓
|
||
|
|
2. CI/CD se déclenche
|
||
|
|
↓
|
||
|
|
3. Stage BUILD
|
||
|
|
- Installer les dépendances (Composer)
|
||
|
|
- Optimiser (autoloader, assets)
|
||
|
|
↓
|
||
|
|
4. Stage DEPLOY
|
||
|
|
- Se connecter au VPS (SSH ou FTP)
|
||
|
|
- Synchroniser les fichiers
|
||
|
|
- Exclure les dossiers sensibles (cache, sessions, accounts)
|
||
|
|
↓
|
||
|
|
5. ✅ Site déployé
|
||
|
|
```
|
||
|
|
|
||
|
|
### Prérequis VPS
|
||
|
|
|
||
|
|
- VPS configuré → [vps-setup-rapide.md](vps-setup-rapide.md)
|
||
|
|
- Site Kirby fonctionnel → [kirby-vps-deploy.md](kirby-vps-deploy.md)
|
||
|
|
- Clé SSH configurée (pour déploiement SSH)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## GitLab CI (SSH/rsync)
|
||
|
|
|
||
|
|
### Avantages
|
||
|
|
- ✅ Rapide (rsync ne transfère que les modifications)
|
||
|
|
- ✅ Sécurisé (SSH)
|
||
|
|
- ✅ Contrôle total (permissions, exclusions)
|
||
|
|
- ✅ Logs détaillés
|
||
|
|
|
||
|
|
### Configuration complète
|
||
|
|
|
||
|
|
**.gitlab-ci.yml** à la racine du projet :
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
stages:
|
||
|
|
- build
|
||
|
|
- deploy
|
||
|
|
|
||
|
|
variables:
|
||
|
|
COMPOSER_ALLOW_SUPERUSER: "1"
|
||
|
|
|
||
|
|
# ===== STAGE BUILD =====
|
||
|
|
build_prod:
|
||
|
|
stage: build
|
||
|
|
only:
|
||
|
|
- main # Déclencher uniquement sur la branche main
|
||
|
|
image: composer:2 # Image officielle Composer
|
||
|
|
script:
|
||
|
|
# Installer les dépendances Kirby
|
||
|
|
- composer install --no-dev --optimize-autoloader --ignore-platform-req=ext-gd
|
||
|
|
artifacts:
|
||
|
|
# Fichiers à passer au stage deploy
|
||
|
|
paths:
|
||
|
|
- vendor/
|
||
|
|
- kirby/
|
||
|
|
- site/
|
||
|
|
- assets/
|
||
|
|
- content/
|
||
|
|
- media/
|
||
|
|
- index.php
|
||
|
|
- .htaccess
|
||
|
|
expire_in: 1 hour # Les artifacts expirent après 1h
|
||
|
|
|
||
|
|
# ===== STAGE DEPLOY =====
|
||
|
|
deploy_prod:
|
||
|
|
stage: deploy
|
||
|
|
image: alpine:latest
|
||
|
|
only:
|
||
|
|
- main
|
||
|
|
dependencies:
|
||
|
|
- build_prod # Récupère les artifacts du build
|
||
|
|
before_script:
|
||
|
|
# Installer rsync et SSH
|
||
|
|
- apk add --no-cache rsync openssh
|
||
|
|
|
||
|
|
# Configurer la clé SSH
|
||
|
|
- mkdir -p ~/.ssh
|
||
|
|
- echo -e "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
|
||
|
|
- chmod 600 ~/.ssh/id_ed25519
|
||
|
|
|
||
|
|
# Configuration SSH (désactiver la vérification de l'host)
|
||
|
|
- |
|
||
|
|
cat > ~/.ssh/config <<EOF
|
||
|
|
Host *
|
||
|
|
StrictHostKeyChecking no
|
||
|
|
UserKnownHostsFile /dev/null
|
||
|
|
BatchMode yes
|
||
|
|
IdentitiesOnly yes
|
||
|
|
EOF
|
||
|
|
- chmod 600 ~/.ssh/config
|
||
|
|
|
||
|
|
script:
|
||
|
|
- echo "🚀 Déploiement sur le serveur de production..."
|
||
|
|
|
||
|
|
# Fonction rsync réutilisable
|
||
|
|
- |
|
||
|
|
rsync_deploy() {
|
||
|
|
src="$1"
|
||
|
|
dst="$2"
|
||
|
|
shift 2
|
||
|
|
cmd="rsync -az --delete --no-perms --no-owner --no-group"
|
||
|
|
for excl in "$@"; do
|
||
|
|
cmd="$cmd --exclude=$excl"
|
||
|
|
done
|
||
|
|
cmd="$cmd -e 'ssh -p ${SSH_PORT:-22} -i ~/.ssh/id_ed25519' $src $USERNAME@$HOST:$dst"
|
||
|
|
echo "$cmd"
|
||
|
|
eval $cmd
|
||
|
|
}
|
||
|
|
|
||
|
|
# Déployer les dossiers buildés
|
||
|
|
- rsync_deploy site/ "$PROD_PATH/site/" "accounts/" "cache/" "sessions/"
|
||
|
|
- rsync_deploy vendor/ "$PROD_PATH/vendor/"
|
||
|
|
- rsync_deploy kirby/ "$PROD_PATH/kirby/"
|
||
|
|
- rsync_deploy assets/ "$PROD_PATH/assets/"
|
||
|
|
- rsync_deploy content/ "$PROD_PATH/content/"
|
||
|
|
- rsync_deploy media/ "$PROD_PATH/media/"
|
||
|
|
- rsync_deploy .htaccess "$PROD_PATH/"
|
||
|
|
- rsync_deploy index.php "$PROD_PATH/"
|
||
|
|
|
||
|
|
- echo "✅ Déploiement terminé !"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Variables CI/CD à configurer
|
||
|
|
|
||
|
|
**GitLab → Settings → CI/CD → Variables :**
|
||
|
|
|
||
|
|
| Variable | Valeur | Protégé | Masqué |
|
||
|
|
|----------|--------|---------|--------|
|
||
|
|
| `SSH_PRIVATE_KEY` | Contenu de `~/.ssh/id_ed25519` | ✅ | ✅ |
|
||
|
|
| `HOST` | `203.0.113.10` (IP du VPS) | ✅ | ❌ |
|
||
|
|
| `USERNAME` | `debian` | ✅ | ❌ |
|
||
|
|
| `SSH_PORT` | `22` (ou autre si modifié) | ❌ | ❌ |
|
||
|
|
| `PROD_PATH` | `/var/www/monsite.com` | ✅ | ❌ |
|
||
|
|
|
||
|
|
**Obtenir la clé SSH privée :**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cat ~/.ssh/id_ed25519
|
||
|
|
# Copiez TOUT le contenu (-----BEGIN ... -----END)
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Forgejo Actions (SSH/rsync)
|
||
|
|
|
||
|
|
### Avantages
|
||
|
|
- ✅ **Libre** : Alternative open-source à GitHub
|
||
|
|
- ✅ Auto-hébergé : Vos données restent chez vous
|
||
|
|
- ✅ Compatible : Syntaxe proche de GitHub Actions
|
||
|
|
- ✅ Rapide : rsync incrémental
|
||
|
|
|
||
|
|
### Configuration
|
||
|
|
|
||
|
|
**.forgejo/workflows/deploy.yml** :
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
name: Deploy to VPS
|
||
|
|
|
||
|
|
on:
|
||
|
|
push:
|
||
|
|
branches:
|
||
|
|
- main
|
||
|
|
|
||
|
|
jobs:
|
||
|
|
deploy:
|
||
|
|
name: Build and Deploy
|
||
|
|
runs-on: docker
|
||
|
|
|
||
|
|
container:
|
||
|
|
image: php:8.2-cli # Image PHP avec Composer
|
||
|
|
|
||
|
|
steps:
|
||
|
|
- name: Checkout code
|
||
|
|
uses: actions/checkout@v4
|
||
|
|
|
||
|
|
- name: Install Composer
|
||
|
|
run: |
|
||
|
|
curl -sS https://getcomposer.org/installer | php
|
||
|
|
mv composer.phar /usr/local/bin/composer
|
||
|
|
|
||
|
|
- name: Install dependencies
|
||
|
|
run: composer install --no-dev --optimize-autoloader
|
||
|
|
|
||
|
|
- name: Deploy to VPS
|
||
|
|
env:
|
||
|
|
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
||
|
|
HOST: ${{ secrets.HOST }}
|
||
|
|
USERNAME: ${{ secrets.USERNAME }}
|
||
|
|
PROD_PATH: ${{ secrets.PROD_PATH }}
|
||
|
|
run: |
|
||
|
|
# Installer rsync et SSH
|
||
|
|
apt-get update -qq
|
||
|
|
apt-get install -y -qq rsync openssh-client
|
||
|
|
|
||
|
|
# Configurer SSH
|
||
|
|
mkdir -p ~/.ssh
|
||
|
|
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
|
||
|
|
chmod 600 ~/.ssh/id_ed25519
|
||
|
|
|
||
|
|
cat > ~/.ssh/config <<EOF
|
||
|
|
Host *
|
||
|
|
StrictHostKeyChecking no
|
||
|
|
UserKnownHostsFile /dev/null
|
||
|
|
BatchMode yes
|
||
|
|
IdentitiesOnly yes
|
||
|
|
EOF
|
||
|
|
chmod 600 ~/.ssh/config
|
||
|
|
|
||
|
|
# Déployer avec rsync
|
||
|
|
echo "🚀 Déploiement..."
|
||
|
|
|
||
|
|
rsync -avz --delete \
|
||
|
|
--exclude 'site/accounts/' \
|
||
|
|
--exclude 'site/cache/' \
|
||
|
|
--exclude 'site/sessions/' \
|
||
|
|
--exclude '.git' \
|
||
|
|
--exclude '.github' \
|
||
|
|
--exclude '.forgejo' \
|
||
|
|
--exclude 'node_modules' \
|
||
|
|
-e "ssh -i ~/.ssh/id_ed25519" \
|
||
|
|
./ $USERNAME@$HOST:$PROD_PATH/
|
||
|
|
|
||
|
|
echo "✅ Déploiement terminé !"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Secrets à configurer
|
||
|
|
|
||
|
|
**Dans Forgejo :** Settings → Secrets → Actions
|
||
|
|
|
||
|
|
| Secret | Valeur |
|
||
|
|
|--------|--------|
|
||
|
|
| `SSH_PRIVATE_KEY` | Clé privée SSH complète |
|
||
|
|
| `HOST` | IP ou domaine du VPS |
|
||
|
|
| `USERNAME` | Utilisateur SSH |
|
||
|
|
| `PROD_PATH` | Chemin absolu sur le VPS |
|
||
|
|
|
||
|
|
**💡 Note :** Si vous utilisez GitLab, la configuration des secrets est identique (Settings → CI/CD → Variables)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Forgejo Actions (FTP)
|
||
|
|
|
||
|
|
### Quand utiliser FTP ?
|
||
|
|
|
||
|
|
- ✅ Hébergement mutualisé (pas d'accès SSH)
|
||
|
|
- ✅ Hébergeur impose FTP uniquement
|
||
|
|
- ❌ Plus lent que SSH/rsync
|
||
|
|
- ❌ Moins sécurisé (sauf FTPS/SFTP)
|
||
|
|
|
||
|
|
### Configuration
|
||
|
|
|
||
|
|
**.forgejo/workflows/deploy-ftp.yml** :
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
name: Deploy via FTP
|
||
|
|
|
||
|
|
on:
|
||
|
|
push:
|
||
|
|
branches:
|
||
|
|
- main
|
||
|
|
|
||
|
|
jobs:
|
||
|
|
deploy:
|
||
|
|
name: Deploy to FTP
|
||
|
|
runs-on: docker
|
||
|
|
container:
|
||
|
|
image: forgejo-ci-php:latest # Image custom (voir ci-dessous)
|
||
|
|
|
||
|
|
steps:
|
||
|
|
- name: Checkout code
|
||
|
|
run: |
|
||
|
|
git clone --depth 1 --branch main \
|
||
|
|
https://forge.example.com/${{ github.repository }}.git .
|
||
|
|
|
||
|
|
- name: Install dependencies
|
||
|
|
run: composer install --no-dev --optimize-autoloader
|
||
|
|
|
||
|
|
- name: Deploy via FTP
|
||
|
|
env:
|
||
|
|
FTP_USERNAME: ${{ secrets.FTP_USERNAME }}
|
||
|
|
FTP_PASSWORD: ${{ secrets.FTP_PASSWORD }}
|
||
|
|
FTP_HOST: ${{ secrets.FTP_HOST }}
|
||
|
|
run: |
|
||
|
|
# Installer lftp (client FTP puissant)
|
||
|
|
apt-get update -qq && apt-get install -y -qq lftp
|
||
|
|
|
||
|
|
# Script lftp
|
||
|
|
cat > /tmp/lftp-script.txt <<SCRIPT
|
||
|
|
set ftp:ssl-allow no
|
||
|
|
open -u $FTP_USERNAME,$FTP_PASSWORD $FTP_HOST
|
||
|
|
|
||
|
|
# Synchroniser les dossiers (mirror --reverse = upload)
|
||
|
|
mirror --reverse --verbose --ignore-time --parallel=10 \
|
||
|
|
-x accounts/ -x cache/ -x sessions/ \
|
||
|
|
site site
|
||
|
|
|
||
|
|
mirror --reverse --verbose --ignore-time --parallel=10 \
|
||
|
|
kirby kirby
|
||
|
|
|
||
|
|
mirror --reverse --verbose --ignore-time --parallel=10 \
|
||
|
|
vendor vendor
|
||
|
|
|
||
|
|
mirror --reverse --verbose --ignore-time --parallel=10 \
|
||
|
|
assets assets
|
||
|
|
|
||
|
|
mirror --reverse --verbose --ignore-time --parallel=10 \
|
||
|
|
content content
|
||
|
|
|
||
|
|
# Upload des fichiers individuels
|
||
|
|
put .htaccess
|
||
|
|
put index.php
|
||
|
|
|
||
|
|
quit
|
||
|
|
SCRIPT
|
||
|
|
|
||
|
|
# Exécuter
|
||
|
|
lftp -f /tmp/lftp-script.txt
|
||
|
|
|
||
|
|
echo "✅ Déploiement FTP terminé !"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Secrets FTP
|
||
|
|
|
||
|
|
| Secret | Valeur |
|
||
|
|
|--------|--------|
|
||
|
|
| `FTP_HOST` | `ftp.example.com` |
|
||
|
|
| `FTP_USERNAME` | Utilisateur FTP |
|
||
|
|
| `FTP_PASSWORD` | Mot de passe FTP |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Image Docker optimisée pour Kirby
|
||
|
|
|
||
|
|
### Pourquoi créer une image custom ?
|
||
|
|
|
||
|
|
**Sans image custom :**
|
||
|
|
```
|
||
|
|
Chaque CI installe : Composer + dépendances + rsync/lftp
|
||
|
|
→ Temps : 2-5 minutes
|
||
|
|
→ Bande passante gaspillée
|
||
|
|
```
|
||
|
|
|
||
|
|
**Avec image custom :**
|
||
|
|
```
|
||
|
|
Image pré-configurée avec tout installé
|
||
|
|
→ Temps : 30 secondes - 1 minute
|
||
|
|
→ CI plus rapide et stable
|
||
|
|
```
|
||
|
|
|
||
|
|
### Créer l'image Docker
|
||
|
|
|
||
|
|
**Dockerfile** :
|
||
|
|
|
||
|
|
```dockerfile
|
||
|
|
FROM php:8.2-cli
|
||
|
|
|
||
|
|
# Métadonnées
|
||
|
|
LABEL maintainer="votre-email@example.com"
|
||
|
|
LABEL description="Image PHP optimisée pour CI Kirby CMS"
|
||
|
|
|
||
|
|
# Installation des extensions PHP nécessaires pour Kirby
|
||
|
|
RUN apt-get update && apt-get install -y \
|
||
|
|
# Extensions PHP
|
||
|
|
libzip-dev \
|
||
|
|
libpng-dev \
|
||
|
|
libjpeg-dev \
|
||
|
|
libfreetype6-dev \
|
||
|
|
# Outils de déploiement
|
||
|
|
rsync \
|
||
|
|
openssh-client \
|
||
|
|
lftp \
|
||
|
|
git \
|
||
|
|
unzip \
|
||
|
|
# Nettoyage
|
||
|
|
&& rm -rf /var/lib/apt/lists/*
|
||
|
|
|
||
|
|
# Configurer GD (images)
|
||
|
|
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
|
||
|
|
&& docker-php-ext-install -j$(nproc) gd zip
|
||
|
|
|
||
|
|
# Installer Composer
|
||
|
|
RUN curl -sS https://getcomposer.org/installer | php -- \
|
||
|
|
--install-dir=/usr/local/bin --filename=composer
|
||
|
|
|
||
|
|
# Définir Composer en mode superuser (pour CI)
|
||
|
|
ENV COMPOSER_ALLOW_SUPERUSER=1
|
||
|
|
|
||
|
|
# Optimisations Composer
|
||
|
|
RUN composer global config --no-plugins allow-plugins.composer/installers true
|
||
|
|
|
||
|
|
# Version et vérification
|
||
|
|
RUN php -v && composer --version && rsync --version
|
||
|
|
|
||
|
|
WORKDIR /workspace
|
||
|
|
|
||
|
|
CMD ["/bin/bash"]
|
||
|
|
```
|
||
|
|
|
||
|
|
### Builder et publier l'image
|
||
|
|
|
||
|
|
**Option 1 : Forgejo Container Registry** (recommandé - auto-hébergé)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Builder l'image
|
||
|
|
docker build -t forge.example.com/votre-user/kirby-ci:latest .
|
||
|
|
|
||
|
|
# Tester localement
|
||
|
|
docker run --rm -it forge.example.com/votre-user/kirby-ci:latest bash
|
||
|
|
composer --version
|
||
|
|
rsync --version
|
||
|
|
|
||
|
|
# Login sur votre registry Forgejo
|
||
|
|
docker login forge.example.com
|
||
|
|
|
||
|
|
# Publier
|
||
|
|
docker push forge.example.com/votre-user/kirby-ci:latest
|
||
|
|
```
|
||
|
|
|
||
|
|
**Option 2 : GitLab Container Registry**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Builder
|
||
|
|
docker build -t registry.gitlab.com/votre-user/kirby-ci:latest .
|
||
|
|
|
||
|
|
# Login GitLab
|
||
|
|
docker login registry.gitlab.com
|
||
|
|
|
||
|
|
# Publier
|
||
|
|
docker push registry.gitlab.com/votre-user/kirby-ci:latest
|
||
|
|
```
|
||
|
|
|
||
|
|
**Option 3 : Docker Hub** (public)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Builder
|
||
|
|
docker build -t votre-user/kirby-ci:latest .
|
||
|
|
|
||
|
|
# Login Docker Hub
|
||
|
|
docker login
|
||
|
|
|
||
|
|
# Publier
|
||
|
|
docker push votre-user/kirby-ci:latest
|
||
|
|
```
|
||
|
|
|
||
|
|
### Utiliser l'image dans la CI
|
||
|
|
|
||
|
|
**GitLab CI :**
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
build_prod:
|
||
|
|
stage: build
|
||
|
|
image: registry.gitlab.com/votre-user/kirby-ci:latest # Votre image custom
|
||
|
|
script:
|
||
|
|
- composer install --no-dev --optimize-autoloader
|
||
|
|
```
|
||
|
|
|
||
|
|
**Forgejo Actions :**
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
jobs:
|
||
|
|
deploy:
|
||
|
|
runs-on: docker
|
||
|
|
container:
|
||
|
|
image: forge.example.com/votre-user/kirby-ci:latest
|
||
|
|
steps:
|
||
|
|
- name: Install dependencies
|
||
|
|
run: composer install --no-dev --optimize-autoloader
|
||
|
|
```
|
||
|
|
|
||
|
|
**Gain de temps :**
|
||
|
|
- Avant : ~3-5 min (install PHP + Composer + tools)
|
||
|
|
- Après : ~30s - 1 min (juste `composer install`)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Configuration des secrets
|
||
|
|
|
||
|
|
### Clé SSH pour la CI
|
||
|
|
|
||
|
|
**1. Générer une clé dédiée à la CI (recommandé) :**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Sur votre machine locale
|
||
|
|
ssh-keygen -t ed25519 -C "ci-deploy-kirby" -f ~/.ssh/ci_deploy
|
||
|
|
|
||
|
|
# Deux fichiers créés :
|
||
|
|
# ~/.ssh/ci_deploy (PRIVÉE - pour la CI)
|
||
|
|
# ~/.ssh/ci_deploy.pub (PUBLIQUE - pour le VPS)
|
||
|
|
```
|
||
|
|
|
||
|
|
**2. Ajouter la clé publique au VPS :**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Copier la clé publique
|
||
|
|
cat ~/.ssh/ci_deploy.pub
|
||
|
|
|
||
|
|
# Sur le VPS
|
||
|
|
ssh debian@VPS
|
||
|
|
vim ~/.ssh/authorized_keys
|
||
|
|
# Collez la clé publique sur une nouvelle ligne
|
||
|
|
```
|
||
|
|
|
||
|
|
**3. Ajouter la clé privée dans les secrets CI :**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Afficher la clé PRIVÉE
|
||
|
|
cat ~/.ssh/ci_deploy
|
||
|
|
|
||
|
|
# Copiez TOUT (-----BEGIN ... -----END)
|
||
|
|
# Puis ajoutez dans les secrets CI sous le nom SSH_PRIVATE_KEY
|
||
|
|
```
|
||
|
|
|
||
|
|
### Vérification
|
||
|
|
|
||
|
|
**Tester la clé depuis votre machine :**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
ssh -i ~/.ssh/ci_deploy debian@VPS
|
||
|
|
# Doit fonctionner sans mot de passe
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Déploiement multi-environnements
|
||
|
|
|
||
|
|
### Staging + Production
|
||
|
|
|
||
|
|
**.gitlab-ci.yml** :
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
stages:
|
||
|
|
- build
|
||
|
|
- deploy-staging
|
||
|
|
- deploy-production
|
||
|
|
|
||
|
|
build:
|
||
|
|
stage: build
|
||
|
|
image: votre-user/kirby-ci:latest
|
||
|
|
script:
|
||
|
|
- composer install --no-dev --optimize-autoloader
|
||
|
|
artifacts:
|
||
|
|
paths:
|
||
|
|
- vendor/
|
||
|
|
- kirby/
|
||
|
|
- site/
|
||
|
|
- assets/
|
||
|
|
- .htaccess
|
||
|
|
- index.php
|
||
|
|
|
||
|
|
# Déploiement automatique sur staging (toutes les branches)
|
||
|
|
deploy_staging:
|
||
|
|
stage: deploy-staging
|
||
|
|
image: alpine:latest
|
||
|
|
only:
|
||
|
|
- branches
|
||
|
|
except:
|
||
|
|
- main
|
||
|
|
before_script:
|
||
|
|
- apk add --no-cache rsync openssh
|
||
|
|
- mkdir -p ~/.ssh
|
||
|
|
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
|
||
|
|
- chmod 600 ~/.ssh/id_ed25519
|
||
|
|
script:
|
||
|
|
- rsync -avz --delete \
|
||
|
|
--exclude 'site/accounts/' \
|
||
|
|
--exclude 'site/cache/' \
|
||
|
|
--exclude 'site/sessions/' \
|
||
|
|
-e "ssh -i ~/.ssh/id_ed25519" \
|
||
|
|
./ $STAGING_USERNAME@$STAGING_HOST:$STAGING_PATH/
|
||
|
|
|
||
|
|
# Déploiement manuel sur production (branche main uniquement)
|
||
|
|
deploy_production:
|
||
|
|
stage: deploy-production
|
||
|
|
image: alpine:latest
|
||
|
|
only:
|
||
|
|
- main
|
||
|
|
when: manual # Nécessite une action manuelle
|
||
|
|
before_script:
|
||
|
|
- apk add --no-cache rsync openssh
|
||
|
|
- mkdir -p ~/.ssh
|
||
|
|
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
|
||
|
|
- chmod 600 ~/.ssh/id_ed25519
|
||
|
|
script:
|
||
|
|
- rsync -avz --delete \
|
||
|
|
--exclude 'site/accounts/' \
|
||
|
|
--exclude 'site/cache/' \
|
||
|
|
--exclude 'site/sessions/' \
|
||
|
|
-e "ssh -i ~/.ssh/id_ed25519" \
|
||
|
|
./ $PROD_USERNAME@$PROD_HOST:$PROD_PATH/
|
||
|
|
```
|
||
|
|
|
||
|
|
**Variables supplémentaires :**
|
||
|
|
|
||
|
|
| Variable | Staging | Production |
|
||
|
|
|----------|---------|------------|
|
||
|
|
| `STAGING_HOST` | `staging.example.com` | - |
|
||
|
|
| `STAGING_USERNAME` | `debian` | - |
|
||
|
|
| `STAGING_PATH` | `/var/www/staging.example.com` | - |
|
||
|
|
| `PROD_HOST` | - | `example.com` |
|
||
|
|
| `PROD_USERNAME` | - | `debian` |
|
||
|
|
| `PROD_PATH` | - | `/var/www/example.com` |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Optimisations
|
||
|
|
|
||
|
|
### Cache Composer
|
||
|
|
|
||
|
|
**GitLab CI :**
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
build_prod:
|
||
|
|
stage: build
|
||
|
|
image: composer:2
|
||
|
|
cache:
|
||
|
|
key: composer-cache
|
||
|
|
paths:
|
||
|
|
- vendor/
|
||
|
|
script:
|
||
|
|
- composer install --no-dev --optimize-autoloader
|
||
|
|
```
|
||
|
|
|
||
|
|
**Forgejo Actions :**
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
- name: Cache Composer
|
||
|
|
uses: actions/cache@v4
|
||
|
|
with:
|
||
|
|
path: vendor
|
||
|
|
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||
|
|
|
||
|
|
- name: Install dependencies
|
||
|
|
run: composer install --no-dev --optimize-autoloader
|
||
|
|
```
|
||
|
|
|
||
|
|
**💡 Note :** Forgejo Actions supporte la syntaxe GitHub Actions (compatibilité)
|
||
|
|
|
||
|
|
### Déploiement incrémental (rsync optimisé)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Ne synchroniser que les fichiers modifiés
|
||
|
|
rsync -avz --delete --checksum \
|
||
|
|
--exclude 'site/accounts/' \
|
||
|
|
--exclude 'site/cache/' \
|
||
|
|
--exclude 'site/sessions/' \
|
||
|
|
--exclude 'media/' \
|
||
|
|
-e "ssh -i ~/.ssh/id_ed25519" \
|
||
|
|
./ $USERNAME@$HOST:$PROD_PATH/
|
||
|
|
```
|
||
|
|
|
||
|
|
**Options rsync importantes :**
|
||
|
|
|
||
|
|
| Option | Description |
|
||
|
|
|--------|-------------|
|
||
|
|
| `-a` | Mode archive (préserve tout) |
|
||
|
|
| `-v` | Verbeux (affiche les fichiers) |
|
||
|
|
| `-z` | Compression pendant le transfert |
|
||
|
|
| `--delete` | Supprime les fichiers distants en trop |
|
||
|
|
| `--checksum` | Compare par checksum (plus précis) |
|
||
|
|
| `--dry-run` | Simulation (ne fait rien) |
|
||
|
|
|
||
|
|
### Rollback rapide
|
||
|
|
|
||
|
|
**Script de rollback :**
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
rollback:
|
||
|
|
stage: deploy
|
||
|
|
when: manual
|
||
|
|
script:
|
||
|
|
- ssh $USERNAME@$HOST "cd $PROD_PATH && git reset --hard HEAD~1"
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Notifications
|
||
|
|
|
||
|
|
### Slack
|
||
|
|
|
||
|
|
**GitLab CI :**
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
after_script:
|
||
|
|
- |
|
||
|
|
curl -X POST -H 'Content-type: application/json' \
|
||
|
|
--data "{\"text\":\"✅ Déploiement de $CI_PROJECT_NAME réussi !\"}" \
|
||
|
|
$SLACK_WEBHOOK_URL
|
||
|
|
```
|
||
|
|
|
||
|
|
**Variables :**
|
||
|
|
- `SLACK_WEBHOOK_URL` : URL du webhook Slack
|
||
|
|
|
||
|
|
### Discord
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
after_script:
|
||
|
|
- |
|
||
|
|
curl -X POST -H 'Content-Type: application/json' \
|
||
|
|
-d "{\"content\":\"✅ **$CI_PROJECT_NAME** déployé sur production !\"}" \
|
||
|
|
$DISCORD_WEBHOOK_URL
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Troubleshooting
|
||
|
|
|
||
|
|
### Erreur "Permission denied (publickey)"
|
||
|
|
|
||
|
|
**Cause :** La clé SSH n'est pas reconnue par le VPS.
|
||
|
|
|
||
|
|
**Solutions :**
|
||
|
|
|
||
|
|
1. Vérifier que la clé publique est bien dans `~/.ssh/authorized_keys` sur le VPS
|
||
|
|
2. Vérifier les permissions :
|
||
|
|
```bash
|
||
|
|
chmod 700 ~/.ssh
|
||
|
|
chmod 600 ~/.ssh/authorized_keys
|
||
|
|
```
|
||
|
|
3. Vérifier le format de la clé privée dans les secrets (doit inclure `-----BEGIN` et `-----END`)
|
||
|
|
|
||
|
|
### Erreur "Host key verification failed"
|
||
|
|
|
||
|
|
**Cause :** Le serveur SSH demande une confirmation.
|
||
|
|
|
||
|
|
**Solution :** Désactiver la vérification dans la config SSH :
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
before_script:
|
||
|
|
- |
|
||
|
|
cat > ~/.ssh/config <<EOF
|
||
|
|
Host *
|
||
|
|
StrictHostKeyChecking no
|
||
|
|
UserKnownHostsFile /dev/null
|
||
|
|
EOF
|
||
|
|
```
|
||
|
|
|
||
|
|
### Composer install échoue
|
||
|
|
|
||
|
|
**Erreur :** `ext-gd is missing`
|
||
|
|
|
||
|
|
**Solution 1 :** Ignorer les requirements de plateforme :
|
||
|
|
|
||
|
|
```bash
|
||
|
|
composer install --no-dev --optimize-autoloader --ignore-platform-req=ext-gd
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solution 2 :** Utiliser une image avec GD pré-installé (image custom)
|
||
|
|
|
||
|
|
### rsync trop lent
|
||
|
|
|
||
|
|
**Optimisations :**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Paralléliser avec --partial-dir
|
||
|
|
rsync -avz --delete --partial-dir=.rsync-partial \
|
||
|
|
--exclude 'media/' \
|
||
|
|
./ $USERNAME@$HOST:$PROD_PATH/
|
||
|
|
|
||
|
|
# Ou exclure les gros dossiers (uploads manuels)
|
||
|
|
rsync -avz --delete \
|
||
|
|
--exclude 'media/' \
|
||
|
|
--exclude 'assets/tiles/' \
|
||
|
|
./ $USERNAME@$HOST:$PROD_PATH/
|
||
|
|
```
|
||
|
|
|
||
|
|
### FTP trop lent
|
||
|
|
|
||
|
|
**Optimisation lftp :**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
mirror --reverse --verbose --parallel=20 \
|
||
|
|
--exclude-glob cache/ \
|
||
|
|
--exclude-glob sessions/ \
|
||
|
|
site site
|
||
|
|
```
|
||
|
|
|
||
|
|
**Augmenter `--parallel` (jusqu'à 20-30 connexions simultanées)**
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Comparaison des méthodes
|
||
|
|
|
||
|
|
| Critère | SSH/rsync | FTP/lftp |
|
||
|
|
|---------|-----------|----------|
|
||
|
|
| **Vitesse** | ⭐⭐⭐⭐⭐ Très rapide | ⭐⭐⭐ Moyen |
|
||
|
|
| **Sécurité** | ⭐⭐⭐⭐⭐ SSH chiffré | ⭐⭐ FTP non chiffré (FTPS mieux) |
|
||
|
|
| **Fiabilité** | ⭐⭐⭐⭐⭐ Très stable | ⭐⭐⭐ Dépend du serveur FTP |
|
||
|
|
| **Compatibilité** | ⭐⭐⭐ VPS/dédié | ⭐⭐⭐⭐⭐ Tous hébergements |
|
||
|
|
| **Facilité** | ⭐⭐⭐⭐ Simple avec clés SSH | ⭐⭐⭐⭐⭐ Très simple |
|
||
|
|
| **Bande passante** | ⭐⭐⭐⭐⭐ Optimisée (delta) | ⭐⭐⭐ Retransfère tout |
|
||
|
|
|
||
|
|
**Recommandation :**
|
||
|
|
- VPS/Serveur dédié → **SSH/rsync** (meilleure option)
|
||
|
|
- Hébergement mutualisé → **FTP/lftp** (seule option souvent)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Exemple complet GitLab CI (production-ready)
|
||
|
|
|
||
|
|
**.gitlab-ci.yml** :
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
stages:
|
||
|
|
- build
|
||
|
|
- test
|
||
|
|
- deploy
|
||
|
|
|
||
|
|
variables:
|
||
|
|
COMPOSER_ALLOW_SUPERUSER: "1"
|
||
|
|
|
||
|
|
# ===== BUILD =====
|
||
|
|
build:
|
||
|
|
stage: build
|
||
|
|
image: votre-user/kirby-ci:latest
|
||
|
|
cache:
|
||
|
|
key: composer-$CI_COMMIT_REF_SLUG
|
||
|
|
paths:
|
||
|
|
- vendor/
|
||
|
|
script:
|
||
|
|
- composer install --no-dev --optimize-autoloader
|
||
|
|
artifacts:
|
||
|
|
paths:
|
||
|
|
- vendor/
|
||
|
|
- kirby/
|
||
|
|
- site/
|
||
|
|
- assets/
|
||
|
|
- content/
|
||
|
|
- index.php
|
||
|
|
- .htaccess
|
||
|
|
expire_in: 1 hour
|
||
|
|
|
||
|
|
# ===== TEST =====
|
||
|
|
test:
|
||
|
|
stage: test
|
||
|
|
image: votre-user/kirby-ci:latest
|
||
|
|
dependencies:
|
||
|
|
- build
|
||
|
|
script:
|
||
|
|
- echo "🧪 Vérification de la structure Kirby..."
|
||
|
|
- test -d kirby || exit 1
|
||
|
|
- test -f index.php || exit 1
|
||
|
|
- test -f .htaccess || exit 1
|
||
|
|
- echo "✅ Structure OK"
|
||
|
|
|
||
|
|
# ===== DEPLOY PRODUCTION =====
|
||
|
|
deploy_production:
|
||
|
|
stage: deploy
|
||
|
|
image: alpine:latest
|
||
|
|
only:
|
||
|
|
- main
|
||
|
|
when: manual # Déploiement manuel sur prod
|
||
|
|
dependencies:
|
||
|
|
- build
|
||
|
|
before_script:
|
||
|
|
- apk add --no-cache rsync openssh
|
||
|
|
- mkdir -p ~/.ssh
|
||
|
|
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
|
||
|
|
- chmod 600 ~/.ssh/id_ed25519
|
||
|
|
- |
|
||
|
|
cat > ~/.ssh/config <<EOF
|
||
|
|
Host *
|
||
|
|
StrictHostKeyChecking no
|
||
|
|
UserKnownHostsFile /dev/null
|
||
|
|
BatchMode yes
|
||
|
|
IdentitiesOnly yes
|
||
|
|
EOF
|
||
|
|
- chmod 600 ~/.ssh/config
|
||
|
|
script:
|
||
|
|
- echo "🚀 Déploiement sur $PROD_HOST..."
|
||
|
|
- |
|
||
|
|
rsync -avz --delete --checksum \
|
||
|
|
--exclude 'site/accounts/' \
|
||
|
|
--exclude 'site/cache/' \
|
||
|
|
--exclude 'site/sessions/' \
|
||
|
|
--exclude '.git' \
|
||
|
|
--exclude '.gitlab-ci.yml' \
|
||
|
|
-e "ssh -p ${SSH_PORT:-22} -i ~/.ssh/id_ed25519" \
|
||
|
|
./ $USERNAME@$PROD_HOST:$PROD_PATH/
|
||
|
|
- echo "✅ Déploiement terminé !"
|
||
|
|
after_script:
|
||
|
|
- |
|
||
|
|
curl -X POST -H 'Content-type: application/json' \
|
||
|
|
--data "{\"text\":\"✅ $CI_PROJECT_NAME déployé en production\"}" \
|
||
|
|
$SLACK_WEBHOOK_URL || true
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Checklist déploiement CI/CD
|
||
|
|
|
||
|
|
Avant de mettre en place la CI/CD :
|
||
|
|
|
||
|
|
- [ ] VPS configuré et accessible en SSH
|
||
|
|
- [ ] Clé SSH dédiée générée pour la CI
|
||
|
|
- [ ] Clé publique ajoutée sur le VPS (`~/.ssh/authorized_keys`)
|
||
|
|
- [ ] Connexion SSH testée depuis votre machine
|
||
|
|
- [ ] Secrets CI configurés (SSH_PRIVATE_KEY, HOST, USERNAME, PROD_PATH)
|
||
|
|
- [ ] Fichier `.gitlab-ci.yml` ou `.forgejo/workflows/deploy.yml` créé
|
||
|
|
- [ ] Test de déploiement sur branche de test
|
||
|
|
- [ ] Vérification du site après déploiement
|
||
|
|
- [ ] Notifications configurées (optionnel)
|
||
|
|
- [ ] Documentation de rollback prête
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Pour aller plus loin
|
||
|
|
|
||
|
|
### Docker Compose pour tests locaux
|
||
|
|
|
||
|
|
**docker-compose.yml** :
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
version: '3.8'
|
||
|
|
|
||
|
|
services:
|
||
|
|
kirby:
|
||
|
|
build: .
|
||
|
|
volumes:
|
||
|
|
- ./:/workspace
|
||
|
|
working_dir: /workspace
|
||
|
|
command: php -S 0.0.0.0:8000
|
||
|
|
ports:
|
||
|
|
- "8000:8000"
|
||
|
|
```
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Tester localement
|
||
|
|
docker-compose up
|
||
|
|
# Ouvrir http://localhost:8000
|
||
|
|
```
|
||
|
|
|
||
|
|
### Tests automatisés
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
test:
|
||
|
|
stage: test
|
||
|
|
image: votre-user/kirby-ci:latest
|
||
|
|
script:
|
||
|
|
- composer require --dev phpunit/phpunit
|
||
|
|
- vendor/bin/phpunit tests/
|
||
|
|
```
|
||
|
|
|
||
|
|
### Monitoring post-déploiement
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
after_script:
|
||
|
|
# Vérifier que le site répond
|
||
|
|
- |
|
||
|
|
STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://example.com)
|
||
|
|
if [ "$STATUS" != "200" ]; then
|
||
|
|
echo "❌ Le site ne répond pas (HTTP $STATUS)"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
echo "✅ Site accessible (HTTP 200)"
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Ressources
|
||
|
|
|
||
|
|
**Docker :**
|
||
|
|
- [Docker Hub](https://hub.docker.com/) - Registry d'images
|
||
|
|
- [Dockerfile reference](https://docs.docker.com/engine/reference/builder/)
|
||
|
|
|
||
|
|
**CI/CD (solutions libres) :**
|
||
|
|
- [Forgejo Actions](https://forgejo.org/docs/latest/user/actions/) - Alternative libre auto-hébergée
|
||
|
|
- [GitLab CI](https://docs.gitlab.com/ee/ci/) - Solution mature et complète
|
||
|
|
- [Woodpecker CI](https://woodpecker-ci.org/) - CI/CD minimaliste et libre
|
||
|
|
|
||
|
|
**Déploiement :**
|
||
|
|
- [rsync manual](https://linux.die.net/man/1/rsync)
|
||
|
|
- [lftp manual](https://lftp.yar.ru/lftp-man.html)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Temps de mise en place :** 1-2h pour la première configuration, puis déploiements en 2-5 minutes ! 🚀
|