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 opensslest obligatoire — sans ça, Prisma crashe aveclibssl.so.1.1: No such file or directory⚠️npm run builddanspackage.jsondoit être"tsc && tsc-alias"— jamaistscseul (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 composeetdocker-composene sont pas disponibles dans le conteneur Hermes. Utiliser exclusivement les commandesdockerdirectes.⚠️ 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.yml — identique 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.mdavant 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¶
- PsychoEnLigneV2/architecture/deploiement-sync-psycho-hermes — scripts de référence (production validée)
- PsychoEnLigneV2/architecture/workflow-developpement-deploiement — les deux contextes de travail
- PsychoEnLigneV2/architecture/cicd-options-deploiement-automatique — pourquoi GitHub Actions
- shared/references/point-mise-en-oeuvre-hermes-workflow-developpement — statut mise en œuvre PsychoEnLigne
- shared/references/hermes-workflow-developpement — workflow global PC ↔ VPS