Aller au contenu

Guide de mise en place d'un nouveau projet — Checklist complète

Résumé

Guide reproductible pour mettre en place le workflow complet sur un nouveau projet : Hermes /cc → Claude Code → git push → GitHub Actions → déploiement Docker automatique. Basé sur l'expérience PsychoEnLigne (production, mai 2026).

Voir PsychoEnLigneV2/architecture/workflow-developpement-deploiement pour comprendre le résultat final. Voir PsychoEnLigneV2/architecture/deploiement-sync-psycho-hermes pour les détails techniques.


Variables à définir avant de commencer

Remplace ces valeurs dans tous les fichiers template ci-dessous :

Variable Exemple (PsychoEnLigne) Ta valeur
<NOM_PROJET> PsychoEnLigne
<NOM_LOWER> psychoenligne (minuscules, sans tiret)
<PORT_BACKEND> 3901 (port unique par projet)
<DB_NAME> psychoenLigne
<DB_USER> psycho_user
<DB_PASSWORD> supersecret123 (sans #)
<GITHUB_REPO> kanmaber/PsychoEnLigne
<SKILL_NAME> cc (nom slash command Telegram, minuscules)
<DOSSIER_PROJET> PsychoEnLigne (nom du dossier sur VPS)

⚠️ Le mot de passe DB ne doit jamais contenir # — dans .env, le # non quoté est un commentaire. ⚠️ Chaque projet doit avoir un port backend unique (3901, 3902, 3903…).


Checklist — dans cet ordre

Étape 1 — Repo GitHub + structure locale

# Sur le PC (WSL)
mkdir ~/projets/<NOM_PROJET> && cd ~/projets/<NOM_PROJET>
git init
git remote add origin https://github.com/kanmaber/<NOM_PROJET>.git
mkdir -p BackEnd FrontEnd .github/workflows

Crée le repo sur GitHub (interface web) avant de pusher.


Étape 2 — Dockerfile Backend

BackEnd/Dockerfile :

FROM node:20-alpine
RUN apk add --no-cache openssl
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY prisma ./prisma
RUN npx prisma generate
COPY . .
RUN npm run build
EXPOSE <PORT_BACKEND>
CMD ["npm", "start"]

⚠️ apk add openssl est obligatoire — sans ça, Prisma crashe avec libssl.so.1.1: No such file or directory ⚠️ npm run build dans package.json doit être "tsc && tsc-alias" — jamais tsc seul (les alias @/ ne seront pas résolus)

BackEnd/.dockerignore :

node_modules
dist
coverage
.env
.env.*
*.log
*.jsonl
.tsbuildinfo

BackEnd/prisma/schema.prisma — vérifier que le generator contient :

generator client {
  provider      = "prisma-client-js"
  binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
}


Étape 3 — Dockerfile Frontend + Nginx

FrontEnd/Dockerfile :

FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine AS production
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

FrontEnd/nginx.conf :

server {
    listen 80;
    root /usr/share/nginx/html;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location /api {
        proxy_pass http://backend:<PORT_BACKEND>;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    gzip on;
    gzip_types text/plain text/css application/json application/javascript;
}


Étape 4 — deploy.sh sur le VPS

Crée /home/kanmaber/projets/<NOM_PROJET>/deploy.sh sur le VPS :

#!/bin/bash
# deploy.sh — Rebuild et redémarre backend/frontend depuis le conteneur Hermes
# Usage : ./deploy.sh [backend|frontend|both]  (défaut: both)

set -e

PROJECT_DIR="/opt/projets/<DOSSIER_PROJET>"
cd "$PROJECT_DIR"

notify() {
  "$PROJECT_DIR/notify.sh" "$1" 2>/dev/null || true
}

git config --global --add safe.directory "$PROJECT_DIR" 2>/dev/null || true

TARGET=${1:-"both"}
notify "🚀 Déploiement <NOM_PROJET> démarré (cible: $TARGET)..."

VERSION=$(git log -1 --format='%h — %s')

deploy_backend() {
  notify "🔨 Build backend..."
  docker build -t <NOM_LOWER>-backend:latest "$PROJECT_DIR/BackEnd/"

  notify "♻️ Redémarrage backend..."
  docker stop <NOM_LOWER>-backend 2>/dev/null || true
  docker rm   <NOM_LOWER>-backend 2>/dev/null || true
  docker run -d \
    --name <NOM_LOWER>-backend \
    --restart unless-stopped \
    --network shared_infra \
    -p <PORT_BACKEND>:<PORT_BACKEND> \
    -e NODE_ENV=production \
    -e REDIS_HOST=shared-redis \
    -e REDIS_PORT=6379 \
    -e REDIS_URL=redis://shared-redis:6379 \
    -e DATABASE_URL="postgresql://<DB_USER>:<DB_PASSWORD>@shared-postgres:5432/<DB_NAME>" \
    -v "$PROJECT_DIR/BackEnd/logs:/app/logs" \
    <NOM_LOWER>-backend:latest

  # Connecter au réseau du projet pour que le frontend rejoigne le backend
  docker network connect --alias backend <NOM_LOWER>_default <NOM_LOWER>-backend

  sleep 5
  if docker exec <NOM_LOWER>-backend wget -qO- http://localhost:<PORT_BACKEND>/health 2>/dev/null | grep -q '"succes":true'; then
    notify "✅ Backend opérationnel — health check OK."
  else
    notify "⚠️ Backend démarré mais health check non confirmé."
  fi
}

deploy_frontend() {
  notify "🔨 Build frontend..."
  docker build -t <NOM_LOWER>-frontend:latest "$PROJECT_DIR/FrontEnd/"

  notify "♻️ Redémarrage frontend..."
  docker stop <NOM_LOWER>-frontend 2>/dev/null || true
  docker rm   <NOM_LOWER>-frontend 2>/dev/null || true
  docker run -d \
    --name <NOM_LOWER>-frontend \
    --restart unless-stopped \
    --network <NOM_LOWER>_default \
    -p 80:80 \
    <NOM_LOWER>-frontend:latest

  notify "✅ Frontend redémarré."
}

case "$TARGET" in
  backend)  deploy_backend ;;
  frontend) deploy_frontend ;;
  both)     deploy_backend && deploy_frontend ;;
esac

docker image prune -f > /dev/null 2>&1 || true
notify "✅ Déploiement terminé ! Version : $VERSION"
echo "✅ Déploiement terminé !"
# Sur le VPS, rendre exécutable
chmod +x /home/kanmaber/projets/<DOSSIER_PROJET>/deploy.sh

⚠️ docker compose et docker-compose ne sont pas disponibles dans le conteneur Hermes. Utiliser exclusivement les commandes docker directes.

⚠️ Si Redis est sur un réseau différent du backend :

docker network connect --alias redis <NOM_LOWER>_default <NOM_LOWER>-redis


Étape 5 — GitHub Actions (CI/CD)

.github/workflows/deploy.ymlidentique pour tous les projets, aucun changement :

name: Deploy <NOM_PROJET>

on:
  push:
    branches: [main]
    paths:
      - 'BackEnd/**'
      - 'FrontEnd/**'

concurrency:
  group: deploy-<NOM_LOWER>
  cancel-in-progress: true

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - name: Déterminer la cible de déploiement
        id: target
        env:
          BEFORE: ${{ github.event.before }}
          AFTER: ${{ github.sha }}
        run: |
          CHANGED=$(git diff --name-only "$BEFORE" "$AFTER" 2>/dev/null || git diff --name-only HEAD~1 HEAD)
          HAS_BACKEND=$(echo "$CHANGED" | grep -c '^BackEnd/' || true)
          HAS_FRONTEND=$(echo "$CHANGED" | grep -c '^FrontEnd/' || true)

          if [ "$HAS_BACKEND" -gt 0 ] && [ "$HAS_FRONTEND" -gt 0 ]; then
            echo "target=both" >> "$GITHUB_OUTPUT"
          elif [ "$HAS_BACKEND" -gt 0 ]; then
            echo "target=backend" >> "$GITHUB_OUTPUT"
          else
            echo "target=frontend" >> "$GITHUB_OUTPUT"
          fi

      - name: Déployer sur VPS via Hermes
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USER }}
          key: ${{ secrets.VPS_SSH_KEY }}
          script: |
            su - kanmaber -c "git -C ~/projets/<DOSSIER_PROJET> pull origin main"
            docker exec hermes-telegram /opt/projets/<DOSSIER_PROJET>/deploy.sh ${{ steps.target.outputs.target }}

Secrets GitHub à configurer (sur github.com/<GITHUB_REPO>/settings/secrets/actions) :

Secret Valeur
VPS_HOST 178.105.46.57
VPS_USER root
VPS_SSH_KEY contenu de ~/.ssh/github_actions (clé dédiée GitHub Actions)

Ces 3 secrets sont identiques pour tous les projets sur le même VPS.

Générer la clé dédiée GitHub Actions (une seule fois) :

ssh-keygen -t ed25519 -C "github-actions" -f ~/.ssh/github_actions -N ""
ssh-copy-id -i ~/.ssh/github_actions.pub root@178.105.46.57
cat ~/.ssh/github_actions   # ← coller ce contenu dans VPS_SSH_KEY


Étape 6 — Skill Hermes

Crée /home/kanmaber/hermes/data/skills/<NOM_LOWER>/code/SKILL.md :

---
name: <SKILL_NAME>
description: "Délègue une tâche de code sur le projet <NOM_PROJET> à Claude Code CLI."
version: 1.0.0
author: kanmaber
license: MIT
platforms: [linux]
metadata:
  hermes:
    tags: [<NOM_LOWER>, code, développement, backend, frontend, bug, feature]
    related_skills: [plan, spike]
---

# <NOM_PROJET> — Délégation à Claude Code

Charge ce skill quand l'utilisateur demande une tâche de développement sur **<NOM_PROJET>**.

## Étape 1 — Lancer Claude Code

```bash
/opt/projets/<DOSSIER_PROJET>/scripts/claude-task.sh "DESCRIPTION_EXACTE_DE_LA_TACHE"

Codes de retour

Code Signification Action
0 Succès Rapporte le résultat
2 Quota épuisé Passe à l'Étape 2
1 Erreur Signale et propose de réessayer

Étape 2 — Fallback si quota épuisé

cat /opt/projets/<DOSSIER_PROJET>/.claude/quota-context.md

Notifie Telegram : "⚠️ Quota Claude atteint — je reprends avec mon modèle." Reprends uniquement ce qui manque d'après le diff.

Étape 3 — Commit et push

cd /opt/projets/<DOSSIER_PROJET> && git add -A && git commit -m "feat: [description]" && git push origin main

3b — Déploiement

Le déploiement est géré automatiquement par GitHub Actions (~30-60s après le push).

Si déploiement forcé immédiat :

docker exec hermes-telegram /opt/projets/<DOSSIER_PROJET>/deploy.sh both

Contexte du projet

  • Dossier : /opt/projets/<DOSSIER_PROJET>/
  • Conventions : lire CLAUDE.md avant toute modification
    ```bash
    # Redémarrer Hermes pour charger le nouveau skill
    docker restart hermes-telegram
    

Étape 7 — claude-task.sh

Copier depuis PsychoEnLigne et adapter le chemin :

cp /opt/projets/PsychoEnLigne/scripts/claude-task.sh \
   /opt/projets/<DOSSIER_PROJET>/scripts/claude-task.sh
chmod +x /opt/projets/<DOSSIER_PROJET>/scripts/claude-task.sh

Modifier la ligne PROJECT_DIR dans le script :

PROJECT_DIR="/opt/projets/<DOSSIER_PROJET>"


Étape 8 — Premier déploiement de validation

# Depuis Telegram ou SSH direct
docker exec hermes-telegram /opt/projets/<DOSSIER_PROJET>/deploy.sh both

Vérifications :

# Containers actifs
docker ps --format '{{.Names}}\t{{.Status}}' | grep <NOM_LOWER>

# Health check backend
curl http://178.105.46.57:<PORT_BACKEND>/health

# Logs en cas de problème
docker logs <NOM_LOWER>-backend --tail 50


Étape 9 — Test du workflow complet

Envoie depuis Telegram :

/<SKILL_NAME> "Ajoute un commentaire dans server.ts"

Vérifie : 1. Hermes répond et lance claude-task.sh 2. Claude Code modifie le fichier 3. git push effectué 4. GitHub Actions se déclenche (onglet Actions du repo GitHub) 5. deploy.sh tourne → notification Telegram ✅


Pièges documentés (production PsychoEnLigne)

Problème Cause Solution
libssl.so.1.1: No such file or directory Alpine 3.x a OpenSSL 3 apk add openssl + binaryTargets = ["native", "linux-musl-openssl-3.0.x"] dans schema.prisma
Cannot find module '@shared/...' au démarrage Alias TypeScript non résolus "build": "tsc && tsc-alias" dans package.json
npm ci échoue — Cannot read properties of undefined package-lock.json avec symlink vers projet voisin Remplacer l'entrée symlink par vraie entrée registry
Backend ne voit pas Redis (ECONNREFUSED) Containers sur réseaux Docker différents docker network connect --alias redis <NOM_LOWER>_default <NOM_LOWER>-redis
Backend utilise InMemory malgré Redis dispo __REDIS_AVAILABLE__ jamais défini Tester Redis avec PING dans server.ts avant d'init les modules
Double déploiement (Hermes + GitHub Actions) Step 3b du skill non mis à jour Retirer deploy.sh du skill — laisser GitHub Actions gérer
sed échoue sur les accents UTF-8 + sed WSL Utiliser python3 pour modifier les fichiers avec accents
Secrets GitHub manquants Oubli de configuration Aller sur github.com/<repo>/settings/secrets/actions
fatal: detected dubious ownership dans deploy.sh Git 2.35+ refuse les dossiers appartenant à un autre user (volume monté kanmaber, container root) Ajouter git config --global --add safe.directory "$PROJECT_DIR" au début de deploy.sh
git pull échoue dans le container Le container Hermes n'a pas de clé SSH GitHub Faire le pull sur l'hôte VPS via su - kanmaber dans le script GitHub Actions
git pull échoue en tant que kanmaber — Permission denied on .git/ Root a tenté un pull avant, créant des fichiers .git/ appartenant à root chown -R kanmaber:kanmaber /home/kanmaber/projets/<PROJET>/.git
Race condition — deux déploiements simultanés Deux commits rapprochés déclenchent deux jobs GitHub Actions en même temps Bloc concurrency dans deploy.yml avec cancel-in-progress: true
Token Telegram invalide dans .env projet Token copié depuis une ancienne source ou tronqué Copier depuis /home/kanmaber/hermes/data/.env (token actif)

Durée estimée par projet

Étape Temps
Étapes 1-3 (structure + Dockerfiles) 15 min
Étape 4 (deploy.sh) 10 min
Étapes 5-6 (Actions + Skill) 10 min
Étapes 7-9 (scripts + validation) 15 min
Total ~50 min

Liens connexes