# DEPLOYMENT.md — Référence unique de déploiement SensiTools

Ce fichier est **la** référence pour déployer SensiTools. Objectif : ne plus
découvrir les problèmes en production. Le gros du diagnostic se fait **en local**
via `app:preflight` avant chaque push.

> Pour la **première mise en ligne** (sous-domaine, base, HTTPS, premier admin),
> voir `docs/deploiement-ovh.md`. Le présent fichier couvre le **cycle de
> déploiement récurrent**.

Toutes les commandes se lancent depuis le dossier de l'application
(le dossier qui contient `bin/console`). En production OVH :

```bash
cd ~/apps/SensiTools-Gestion-de-Projet/sos-symfony
```

> ⚠️ Ne pas confondre avec `~/www` : ce dossier est **conservé** (ne rien y
> écraser). L'application vit dans `~/apps/SensiTools-Gestion-de-Projet/`.

---

## Le cycle en bref

```
LOCAL / WORK                         OVH (après git pull)
  APP_ENV=prod app:preflight           git pull
  → 0 erreur                           quelques commandes (ci-dessous)
  git commit / push                    APP_ENV=prod app:preflight → 0 erreur
```

---

## Tests locaux obligatoires AVANT déploiement

Une seule commande couvre l'essentiel (lecture seule, ne modifie rien) :

```bash
APP_ENV=prod php bin/console app:preflight
```

Elle vérifie automatiquement :

- démarrage Symfony en `prod`, conteneur de services, autoload Composer ;
- commandes applicatives réellement enregistrées + routes critiques ;
- connexion base, migrations exécutées/restantes, mapping Doctrine, tables essentielles ;
- variables d'environnement obligatoires (sans afficher les secrets) ;
- `deploy.local.yaml` présent et complété ;
- fichiers externes (collaborateurs JSON, credentials Google) ;
- permissions `var/` ;
- fichiers sensibles bien ignorés par Git + fichiers requis hors Git ;
- **toutes les sources de données actives** (collaborateurs + Google Sheets) ;
- e-mail : **SKIPPED** (hors périmètre).

Le résultat se termine par `PRÊT POUR DÉPLOIEMENT` (code retour 0) ou
`DÉPLOIEMENT BLOQUÉ` (code non nul + liste des causes). Les warnings restent
visibles mais ne bloquent pas.

### Dry-runs détaillés (mapping métier) — si une source de données a changé

`app:preflight` signale l'état des sources ; pour **valider le mapping ligne par
ligne** (les bonnes données dans les bons champs), lancer le dry-run concerné :

```bash
APP_ENV=prod php bin/console app:collaborators:bootstrap --dry-run
APP_ENV=prod php bin/console app:google-sheet:sync --all --dry-run
# ou une source précise :
APP_ENV=prod php bin/console app:google-sheet:sync <code> --dry-run
```

Les URLs / IDs des Google Sheets se configurent dans l'application
(**Admin → Sources Google Sheet**) — c'est la seule source de vérité, jamais en dur.

> ⚠️ Règle : **ne rien pousser** tant que le scénario réel d'un nouveau mécanisme
> n'a pas été testé localement (dry-run OK).

---

## Actions OVH après `git pull`

> Mettre à jour cette section à **chaque** modification ayant un impact production.

| Action | Nécessaire ? | Commande |
|---|---|---|
| Récupérer le code | **oui** | `git pull` |
| Installer les dépendances | **oui si `composer.lock` a changé** | `composer install --no-dev --optimize-autoloader` |
| Migrations base | **oui si nouvelle migration** | `php bin/console doctrine:migrations:migrate --no-interaction` |
| Vider le cache | **oui** | `php bin/console cache:clear` |
| Fichier externe à déposer | **oui si absent** | `config/fixtures/collaborators.json` (hors Git, à placer sur le serveur) |
| Import / synchro | **au besoin** | `php bin/console app:collaborators:bootstrap` · `php bin/console app:google-sheet:sync <code>` |
| Contrôle final | **oui** | `APP_ENV=prod php bin/console app:preflight` |

Tout est déjà enchaîné par le script :

```bash
bash scripts/deploy.sh   # sauvegarde → git pull → composer → migrations → cache
```

**Action manuelle exceptionnelle** : si l'e-mail n'est pas opérationnel, garder
la 2FA désactivée (`TWO_FACTOR_ENABLED=0` dans `.env.local` + `cache:clear`).

---

## Inventaire des sources de données

Sources actuelles de l'application. Les Google Sheets sont configurés dans
**Admin → Sources Google Sheet** (entité `GoogleSheetLink` : `code`, `purpose`,
`spreadsheetId`, `tabName`, `rangeName`).

| Code / source | Finalité | Entité cible | Clé de rapprochement | Rythme | Contrôlé par |
|---|---|---|---|---|---|
| `collaborators.json` (fichier local) | Collaborateurs | `Collaborator` | **email** | à la demande | `app:collaborators:bootstrap --dry-run` |
| `availability_google_sheet` | Congés / télétravail / disponibilités | `CollaboratorAvailability` | collaborateur + date + créneau | **quotidien** | `app:google-sheet:sync availability_google_sheet --dry-run` |
| `clients_google_sheet` | Clients | `Client` | **nom** | à la demande **+ à chaque import projet** | `app:google-sheet:sync clients_google_sheet --dry-run` |
| `contacts_google_sheet` | Base contacts | `Contact` | **email** (sans email = ignoré) | à la demande | `app:google-sheet:sync contacts_google_sheet --dry-run` |
| `projects_from_excel.json` (fichier déposé) | Projets | `Project`, phases, tâches… | **nom du projet** | à la demande (création seule) | `app:projects:import --dry-run` |

**Sens de la synchro** : toutes ces sources sont **création / mise à jour**, **jamais
de suppression**. Le Sheet fait foi pour les champs qu'il porte ; l'app conserve tout
le reste. L'import projets est **création seule** : un projet déjà présent (comparé par
nom) n'est jamais remplacé.

**Rythmes** :
- **Cron nocturne unique** (`scripts/sync-google-sheets.sh` → `app:google-sheet:sync --all`) :
  synchronise **toutes les sources actives** — `availability_google_sheet`,
  `clients_google_sheet`, `contacts_google_sheet` — une fois par nuit.
  Ligne cron OVH :
  `30 4 * * * cd ~/apps/SensiTools-Gestion-de-Projet/sos-symfony && bash scripts/sync-google-sheets.sh >> ~/sensitools-backups/sync-google-sheets.log 2>&1`
  (⚠️ remplace l'ancien `sync-availability.sh`, qui ne traitait QUE les disponibilités.)
- `clients_google_sheet` est aussi resynchronisé **au début de chaque `app:projects:import`**.
- `contacts_google_sheet` : un contact **sans email** est ignoré (signalé).
- Une source ne se synchronise que si son **GoogleSheetLink est actif** (page « Sources Google Sheet »).
- `projects_from_excel.json` : voir **Import des projets** ci-dessous.

**Si une source ne se met plus à jour** — vérifier, sur OVH :
1. Le cron est bien installé : `crontab -l` (doit lister `sync-google-sheets.sh`).
2. Lancer à la main : `bash scripts/sync-google-sheets.sh` — lire la sortie / le log.
3. Accès par source : `php bin/console app:google-sheet:test <code>` (egress vers googleapis.com + credentials).
4. Le lien est **actif** avec le bon code / la bonne finalité (page « Sources Google Sheet »).

### Import des projets (fichier produit par IA)

Les projets sont trop hétérogènes pour un parseur automatique du `.xls`. Le flux :

1. Exporter l'onglet projet en `.xls`.
2. Le confier à une IA (ChatGPT / Claude) **avec la consigne** `docs/CONSIGNE_IMPORT_PROJETS.md` :
   elle produit un JSON au format attendu.
3. Déposer le JSON sous `config/fixtures/projects_from_excel.json` (**hors Git**).
   Ce fichier étant hors Git, `git pull` ne le transporte pas : il faut le **copier par
   `scp`** depuis le poste local vers OVH (mot de passe SSH demandé, à saisir soi-même) :

```bash
# Depuis le poste local, remplacer le chemin source par celui du JSON produit :
scp "chemin/local/projects_from_excel.json" \
  sensipod@ssh.cluster102.hosting.ovh.net:apps/SensiTools-Gestion-de-Projet/sos-symfony/config/fixtures/projects_from_excel.json
```

4. Se connecter au serveur, puis simuler et importer :

```bash
ssh sensipod@ssh.cluster102.hosting.ovh.net
cd apps/SensiTools-Gestion-de-Projet/sos-symfony
php bin/console app:projects:import --dry-run   # aucune écriture
php bin/console app:projects:import             # création seule (+ synchro clients)
```

`app:projects:reload-from-excel` existe toujours mais est **destructif** (remplace tous
les projets) : à n'utiliser que pour une remise à zéro volontaire.

**Règle — les collaborateurs font foi** : `config/fixtures/collaborators.json`
(chargé en base par `app:collaborators:bootstrap`, rapprochement par **email**) est
la **source de vérité** des personnes. Toute synchronisation (disponibilités, etc.)
**ignore** une personne présente dans un Sheet mais absente de cette liste — elle
n'est **jamais créée à la volée** — et la signale (dry-run / preflight :
« collaborateurs ignorés / absents de la base »).

En cas d'ambiguïté entre la structure réelle d'un Sheet et le mapping attendu :
`app:preflight` / le dry-run la **signalent** (warning) — aucune correspondance
métier n'est inventée silencieusement.

---

## Références OVH (production)

Chemins et coordonnées réels — **ne pas réinventer**. Les secrets ne sont jamais ici,
uniquement les emplacements.

| Élément | Valeur |
|---|---|
| Hôte SSH | `ssh.cluster102.hosting.ovh.net` (utilisateur `sensipod`) |
| Dépôt Git | `~/apps/SensiTools-Gestion-de-Projet` |
| Application Symfony | `~/apps/SensiTools-Gestion-de-Projet/sos-symfony` |
| URL publique | `https://projets.sensipode.com` |
| Base MySQL | hôte `sensipodtools01.mysql.db` · base `sensipodtools01` |
| Credentials Google (prod) | `~/secrets/sensitools/google-service-account.json` |
| Credentials Google (local) | `config/credentials/google-service-account.json` (hors Git) |
| Sauvegardes base | `~/backups/sensitools/` |
| À préserver | `~/www` (ne rien y écraser) |

**Variables d'environnement** (dans `.env.local` côté serveur, jamais dans Git) —
le preflight vérifie leur **présence**, jamais leur valeur : `APP_ENV`, `APP_DEBUG`,
`APP_SECRET`, `DATABASE_URL`, `GOOGLE_APPLICATION_CREDENTIALS`,
`GOOGLE_SHEETS_CREDENTIALS_JSON`, `MAILER_DSN`, `MAILER_FROM`, `TWO_FACTOR_ENABLED`.

**Jamais synchronisés / à ne jamais écraser depuis le dépôt** : `vendor/`,
`var/cache/`, `var/log/` (régénérés sur le serveur), les fichiers hors Git
(`collaborators.json`, `projects_from_excel.json`, credentials Google, `.env.local`).

---

## Notes

- **E-mail / Brevo** : hors périmètre. Toujours `SKIPPED` au preflight, ne bloque jamais.
- **2FA** : active pour tous (code par email). Dépend de l'e-mail → voir la
  soupape `TWO_FACTOR_ENABLED` ci-dessus.
- **Secrets** : jamais dans Git ni dans `deploy.local.yaml`. Uniquement `.env.local`.
