---
description: "Cycle complet composer-full ↔ packages/ ↔ composer-prod : met à jour tous les plugins (regular + 3 premium) et synchronise packages/ pour le déploiement"
argument-hint: "(aucun)"
---

# /update-premium — Cycle Composer full/prod + sync premium

Easebook ne peut pas installer 3 plugins premium via Composer en CI/prod :
- `wpengine/advanced-custom-fields-pro` → ACF Pro
- `envato/real3d-flipbook` → Real3D Flipbook
- `envato/real3d-flipbook-page-editor` → Real3D Flipbook Page Editor

Ces 3 plugins sont versionnés dans `packages/` et copiés sur le serveur par la tâche Deployer `deploy:plugins:manual` (`deploy.php:80-85`).

Ce skill orchestre le swap composer-full ↔ composer-prod avec sync `packages/` automatique.

## Pré-conditions

- DDEV démarré (`ddev start` si besoin)
- Sur la branche de travail (typiquement `main`) avec un état git propre sur les fichiers composer (sinon : warning, l'utilisateur tranche)
- `auth.json` présent à la racine (credentials ACF Pro pour `connect.advancedcustomfields.com`)
- `ENVATO_TOKEN` renseigné dans le `.env` racine (token Envato pour `szepeviktor/composer-envato`). ⚠️ DDEV ne l'injecte PAS dans le conteneur (`web_environment: []`) — la Phase 2 le source à la volée depuis le `.env` monté.

## État final attendu

À la fin de l'exécution :
- `composer.json` est **identique** à `composer-prod.json` (commitable, CI happy)
- `composer.lock` correspond à composer-prod
- `composer-full.json` + `composer-full.lock` = backup de l'état complet le plus à jour
- `composer-prod.json` + `composer-prod.lock` = backup de l'état prod le plus à jour
- `packages/{3 premium}/` synchronisé avec les dernières versions
- `web/app/plugins/{3 premium}/` restauré localement (gitignored, permet de continuer à dev)

## Phases

### Phase 1 — Pre-checks

1. Vérifier la présence des 6 fichiers composer :
```bash
ls composer.json composer.lock composer-full.json composer-full.lock composer-prod.json composer-prod.lock
```
Si l'un manque : **STOP**, demander à l'utilisateur ce qu'il faut faire.

2. Vérifier la présence des 3 dossiers premium dans `packages/` :
```bash
ls packages/advanced-custom-fields-pro packages/real3d-flipbook packages/real3d-flipbook-page-editor
```
Si un dossier manque : **STOP** (le système packages/ est cassé).

3. Vérifier l'état git sur composer.json/composer.lock :
```bash
git status --porcelain composer.json composer.lock
```
Si modifs non committées : **WARNING**. Demander confirmation avant d'écraser. Si l'utilisateur confirme, continuer ; sinon stop.

4. Vérifier la présence d'`auth.json` (credentials ACF Pro) :
```bash
ls auth.json
```
Si manquant : **STOP**, le téléchargement d'ACF Pro va échouer.

5. Vérifier que `ENVATO_TOKEN` est renseigné dans `.env` :
```bash
grep -qE '^ENVATO_TOKEN=.+' .env && echo "OK" || echo "MANQUANT"
```
Si `MANQUANT` : **STOP**, le téléchargement des premium Envato va échouer.

### Phase 2 — Update mode FULL

```bash
cp composer-full.json composer.json
cp composer-full.lock composer.lock
```

⚠️ **Ne PAS lancer `ddev composer update` directement ici** — ça échoue avec *« envato/… found in lock file but not in remote repositories »*. Deux conditions doivent être réunies pour résoudre les premium :

1. **`szepeviktor/composer-envato` doit déjà être installé dans `vendor/`** au moment de la résolution : c'est lui qui fournit les paquets `envato/*` (il interroge l'API Envato avec le token et injecte l'auth des téléchargements). Or, après un cycle prod précédent (Phases 5-7) il a été désinstallé : `vendor/` est en état prod. Sans plugin actif, aucune source pour `envato/*`. → Il faut d'abord un **`composer install`** (depuis le lock full) pour le réamorcer.
2. **`ENVATO_TOKEN` doit être présent dans le conteneur DDEV.** Il est dans le `.env` Bedrock mais DDEV ne le charge pas dans le shell (`web_environment: []`). → On le **source depuis le `.env` monté** (`/var/www/html/.env`) au moment d'exécuter composer, sans jamais l'exposer en clair.

```bash
# 2a. Bootstrap : réinstalle composer-envato + premium depuis le lock full (token sourcé du .env)
ddev exec bash -c 'set -a; . <(grep -E "^ENVATO_TOKEN=" .env); set +a; composer install'

# 2b. Update : composer-envato est maintenant actif → met tout à jour (regular + premium + deps)
ddev exec bash -c 'set -a; . <(grep -E "^ENVATO_TOKEN=" .env); set +a; composer update'
```

> Le `set -a` exporte les variables sourcées ; la substitution de processus `. <(grep …)` injecte uniquement la ligne `ENVATO_TOKEN=…` du `.env`. Ne mets jamais de `${…}` autour de ces commandes (le shell hôte l'expanserait avant d'atteindre le conteneur).

> **Cas particulier — bump d'une contrainte de version (ex. WordPress `^6.7.2` → `^7.0`).**
> Édite d'abord la contrainte dans `composer-full.json` **et** `composer-prod.json` (jamais `composer.json` à la main). Le bootstrap `composer install` (2a) échouera alors avec *« Required package … is in the lock file as "X" but that does not satisfy your constraint »* : le lock full est périmé vis-à-vis de la nouvelle contrainte. Il faut bootstrapper `composer-envato` depuis le **couple json/lock cohérent de HEAD** avant de basculer sur le json édité :
>
> ```bash
> # bootstrap depuis l'état cohérent committé (json de HEAD ⟷ lock full committé)
> git show HEAD:composer-full.json > composer.json
> cp composer-full.lock composer.lock
> ddev exec bash -c 'set -a; . <(grep -E "^ENVATO_TOKEN=" .env); set +a; composer install'
> # puis bascule sur le json édité (nouvelle contrainte) et update
> cp composer-full.json composer.json
> ddev exec bash -c 'set -a; . <(grep -E "^ENVATO_TOKEN=" .env); set +a; composer update'
> ```
>
> Phase 5 (PROD) : aucun bootstrap (prod n'a pas de paquets envato, et `composer update` re-résout sans valider le lock). Après le cycle, applique le core sur la base locale : `ddev wp core update-db` puis `ddev wp language core update` (génère des changements dans `web/app/languages/` à committer).

Composer va :
- (install) Réinstaller `szepeviktor/composer-envato` + les 3 premium aux versions du lock full
- (update) Mettre à jour les plugins regular (acf-extended, secupress, etc.), interroger l'API Envato pour les dernières versions premium dispo, et télécharger ACF Pro via `connect.advancedcustomfields.com`
- Installer le tout dans `web/app/plugins/`

Si le `composer install` ou le `composer update` échoue (auth, réseau, contraintes), STOP et reporter l'erreur.

### Phase 3 — Backup état FULL

Sauvegarder le composer.json/.lock à jour comme nouvelle référence "full" :

```bash
cp composer.json composer-full.json
cp composer.lock composer-full.lock
```

### Phase 4 — Sync premium → packages/

Synchroniser les 3 plugins premium fraîchement installés vers `packages/`. `--delete` retire les fichiers obsolètes (équivalent atomique de `rm -rf` + `cp -r`) :

```bash
rsync -a --delete web/app/plugins/advanced-custom-fields-pro/ packages/advanced-custom-fields-pro/
rsync -a --delete web/app/plugins/real3d-flipbook/             packages/real3d-flipbook/
rsync -a --delete web/app/plugins/real3d-flipbook-page-editor/ packages/real3d-flipbook-page-editor/
```

> Note : le `/` final sur le source ET la destination est important — il copie le **contenu** du dossier, pas le dossier lui-même.

### Phase 5 — Switch vers PROD

```bash
cp composer-prod.json composer.json
cp composer-prod.lock composer.lock
ddev composer update
```

Composer va :
- Voir que les 3 premium ne sont plus dans `composer.json` → les **désinstaller** de `web/app/plugins/`
- Mettre aussi à jour les plugins regular à leurs dernières versions cohérentes avec les contraintes de composer-prod

### Phase 6 — Backup état PROD

```bash
cp composer.json composer-prod.json
cp composer.lock composer-prod.lock
```

### Phase 7 — Restauration locale des premium plugins

Re-copier les 3 plugins depuis `packages/` vers `web/app/plugins/` pour que le dev local continue à fonctionner. `web/app/plugins/*` étant gitignored, ces fichiers restaurés ne pollueront pas le commit :

```bash
rsync -a packages/advanced-custom-fields-pro/ web/app/plugins/advanced-custom-fields-pro/
rsync -a packages/real3d-flipbook/             web/app/plugins/real3d-flipbook/
rsync -a packages/real3d-flipbook-page-editor/ web/app/plugins/real3d-flipbook-page-editor/
```

> Pas de `--delete` ici : on copie sur un dossier que composer vient de vider, donc rien à supprimer côté destination.

### Phase 8 — Sanity check & rapport

1. Vérifier que `composer.json` = `composer-prod.json` :
```bash
diff composer.json composer-prod.json
```
Doit être vide.

2. Vérifier que `packages/` contient les 3 dossiers :
```bash
ls packages/
```

3. Vérifier que `web/app/plugins/` contient les 3 premium :
```bash
ls web/app/plugins/ | grep -E '^(advanced-custom-fields-pro|real3d-flipbook|real3d-flipbook-page-editor)$'
```

4. Récupérer les versions avant/après pour le rapport. Lire le header de chaque plugin :
```bash
grep -h "^ \* Version:" packages/advanced-custom-fields-pro/*.php | head -1
grep -h "^ \* Version:" packages/real3d-flipbook/*.php | head -1
grep -h "^ \* Version:" packages/real3d-flipbook-page-editor/*.php | head -1
```

> Note : pour avoir le "avant", lire ces mêmes lignes AVANT la phase 2 (les stocker en mémoire pour le rapport final).

## Rapport final

```
✓ Plugins premium mis à jour :
  - Advanced Custom Fields Pro    : <avant> → <après>
  - Real3D Flipbook               : <avant> → <après>
  - Real3D Flipbook Page Editor   : <avant> → <après>

✓ Plugins regular également mis à jour via composer-prod
✓ composer.json est en mode prod (commitable)
✓ web/app/plugins/ contient les 3 premium (local dev OK)
✓ packages/ synchronisé (sera shippé via Git pour deploy)

Fichiers modifiés à committer :
  - composer.json, composer.lock           (état prod)
  - composer-full.json, composer-full.lock (backup)
  - composer-prod.json, composer-prod.lock (backup)
  - packages/advanced-custom-fields-pro/
  - packages/real3d-flipbook/
  - packages/real3d-flipbook-page-editor/

Suggestion de commit :
  chore(premium): update acf pro and real3d flipbook
```

## En cas d'échec

| Phase | Échec | Action |
|---|---|---|
| 1 | Fichier composer manquant | STOP, demander à l'utilisateur |
| 1 | git dirty | Demander confirmation avant d'écraser |
| 1 | auth.json manquant | STOP, indiquer qu'il faut les credentials Envato/ACF Pro |
| 2 | *« envato/… found in lock file but not in remote repositories »* | `szepeviktor/composer-envato` n'est pas actif (bootstrap 2a oublié) **ou** `ENVATO_TOKEN` n'est pas sourcé dans le conteneur. Vérifier que 2a (`composer install`) a tourné et que le token est bien injecté (`printenv ENVATO_TOKEN` dans le conteneur). |
| 2 | `composer install` (bootstrap) : *« … does not satisfy your constraint »* | Bump de contrainte → lock full périmé. Bootstrappe `composer-envato` depuis `git show HEAD:composer-full.json` + `composer-full.lock` (cf. « Cas particulier » Phase 2), puis bascule sur le json édité pour l'`update`. |
| 2 | `composer install`/`update` full échoue (auth, réseau, contraintes) | STOP, restaurer composer.json/.lock depuis l'état précédent (`git checkout composer.json composer.lock` si propre, sinon depuis `composer-full.json` qui n'a pas encore été écrasé) |
| 4 | rsync échoue | STOP, ne PAS continuer (sinon packages/ serait désynchronisé) |
| 5 | `composer update` prod échoue | STOP, l'état est dégradé : composer.json est en prod mais .lock peut-être incohérent. Conseiller de restaurer depuis `composer-prod.json` + `composer-prod.lock` (pas encore écrasés à ce stade). |
| 7 | rsync restauration échoue | WARN seulement : l'état committable est OK, juste le dev local est cassé. L'utilisateur peut refaire la copie à la main. |

## Notes

- Ne JAMAIS lancer `ddev composer update` directement en dehors de ce skill — ça désynchroniserait le système full/prod.
- `ENVATO_TOKEN` vit uniquement dans le `.env` (jamais dans `web_environment` / `.ddev/config.yaml`, pour ne pas committer le secret). La Phase 2 le source à la volée depuis le `.env` monté dans le conteneur — c'est volontaire.
- Le `composer install` de la Phase 2 (2a) est un **réamorçage obligatoire**, pas une option : après un cycle prod, `szepeviktor/composer-envato` n'est plus dans `vendor/`, et sans lui les paquets `envato/*` n'ont aucune source distante. L'install les réinstalle depuis le lock avant que l'update puisse les résoudre. Le skill reste idempotent (relançable sans risque).
- La phase 5 supprime temporairement les 3 premium de `web/app/plugins/`. Si tu interromps le skill entre les phases 5 et 7, tu n'auras plus les premium en local. Relance le skill pour restaurer (il est idempotent).
- Le pattern rsync avec slash final est volontaire : `rsync -a --delete src/ dst/` copie le contenu de `src` dans `dst` (sans imbriquer un sous-dossier `src` dans `dst`).
