---
name: site-shizendo-ovh
description: Gestion du site site.shizendo.ovh (Astro + contenus SilverBullet) — sync des contenus, build, publication SFTP, diagnostic de build, ajout de page.
---

# Skill: site-shizendo-ovh

Gestion de **site.shizendo.ovh** : site statique **Astro** bâti à partir des contenus SilverBullet, publié en SFTP sur l'app YunoHost `my_webapp`.

## Chaîne de déploiement

```
SilverBullet 08_SITES/SITE-SHIZENDO-OVH/pages/*.md
  → sync-site-content.sh   (API /.fs, ~0 token)
  → projects/site-shizendo/src/content/pages/*.md
  → astro build            → dist/
  → sftp-mirror.sh         → my_webapp@site.shizendo.ovh:/www  (port 222)
```

Trois déclencheurs :

1. **Push Forgejo** `shizendo/shizendo-site` → webhook `127.0.0.1:8787` (listener gardé vivant par cron `* * * * *` sous `flock`, signature HMAC lue dans `pass opencode/site-webhook-secret`).
2. **Cron quotidien** `0 5 * * *` → `cron-quotidien-site-deploy.sh` (sentinel d'un déploiement/jour, filet de sécurité si le webhook saute).
3. **Manuel** : `/site-deploy` (TUI) ou `~/.opencode/bin/deploy-site.sh` (shell).

**Historique git du contenu** : après publication réussie, `deploy-site.sh` commit **uniquement** `src/content/pages` (`contenus SilverBullet — déploiement <ISO>`) et pousse sur Forgejo. Push refusé → commit local conservé + `⚠` loggé (déploiement toujours réussi). Contenu inchangé → aucun commit, donc **aucune boucle webhook** (le 2ᵉ déployage déclenché par le push s'arrête sur « contenu inchangé »). Jamais de `git add -A` : le code se commit à la main. En amont, le déploiement ne fait qu'un `fetch` + `merge --ff-only` (Forgejo → local) : jamais de rétrogradation du remote.

## Chemins & scripts

| Élément | Chemin |
|---|---|
| Projet Astro (git → Forgejo `shizendo/shizendo-site`, branche `main`) | `/var/lib/opencode/projects/site-shizendo` |
| Contenus synchronisés (généré, ne pas éditer à la main) | `projects/site-shizendo/src/content/pages/` |
| Schéma de collection | `projects/site-shizendo/src/content.config.ts` |
| Nav du site (hardcodée) | `projects/site-shizendo/src/layouts/Layout.astro` |
| Sync des contenus SB | `~/.opencode/bin/sync-site-content.sh` |
| Déploiement complet | `~/.opencode/bin/deploy-site.sh` (`SKIP_GIT=1` pour builder sans pull) |
| État du site (lecture seule) | `~/.opencode/bin/site-status.sh` |
| Miroir SFTP | `~/.opencode/bin/sftp-mirror.sh <dir-local> [/www] [--dry-run]` (`DRY=1` = plan seul) |
| Cron quotidien | `~/.opencode/bin/cron-quotidien-site-deploy.sh` |
| Webhook | `~/.opencode/bin/webhook-listener.mjs` + `.sh` (garde de vie) |
| État & journaux | `~/.local/state/site-deploy/` : `deploy.log` (rotation 2000 lignes), `cron-quotidien.log`, `webhook.log`, `manifest-_www.txt`, sentinel `cron-quotidien-site-deploy.done` |

## Commande TUI
- **`/site-deploy`** → exécute `deploy-site.sh`, vérifie `https://site.shizendo.ovh/`, `/a-propos/`, `/contact/` (HTTP 200) et rend un verdict. En cas d'échec : `tail -40 ~/.local/state/site-deploy/deploy.log`.
- **`/site-status`** → état en un coup d'œil, **strictement lecture seule** (script `~/.opencode/bin/site-status.sh`) : dernier déploiement, sentinel cron, listener webhook, git (écart `origin/main`), contenus synchronisés (+ détection d'une page parasite), manifeste SFTP, HTTP des 3 routes, verdict final. Ne déploie ni ne corrige rien.
## Contenus

- **Source de vérité = SilverBullet** `08_SITES/SITE-SHIZENDO-OVH/pages/`. Ne jamais écrire dans `src/content/pages/` : le dossier est vidé (`find -name '*.md' -delete`) puis retéléchargé à chaque sync.
- `08_SITES/SITE-SHIZENDO-OVH/brouillons/` n'est **pas** synchronisé (non publié).
- Frontmatter imposé par le schéma : **`title` obligatoire** (string), `description` (défaut `''`), `draft: true` pour exclure une page du build (brouillons).
- Les README de `08_SITES/**` ont été supprimés le 2026-11-07 : aucune règle ne dépend d'un fichier README, cette page (le skill) fait foi.
- Ne **pas** créer de page `pages.md` dans le dossier `pages/` : c'est une page d'index SilverBullet, non publiée, et sans frontmatter elle casse le build.

## Checklist — ajouter une page

1. Créer `08_SITES/SITE-SHIZENDO-OVH/pages/<slug>.md` avec frontmatter `title` (+ `description` si SEO souhaité).
2. Si la page doit apparaître dans le menu : ajouter l'entrée dans `src/layouts/Layout.astro` (tableau `nav`, durcodé) et commiter/pousser sur Forgejo.
3. Lancer `/site-deploy`.
4. Vérifier `https://site.shizendo.ovh/<slug>/` → 200.
5. Documenter dans la note projet `03_PROJETS/SITE-SHIZENDO-OVH`.

## Diagnostic

| Symptôme | Cause | Action |
|---|---|---|
| `[InvalidContentEntryDataError] ... title Required` | frontmatter absent ou `title` manquant dans un .md synchronisé | compléter le frontmatter dans SilverBullet, relancer le deploy |
| Build OK mais page absente du site | `draft: true` ou fichier hors `pages/` | vérifier le frontmatter |
| `ERREUR: branche divergée (commits locaux non poussés ?)` | commits locaux + avance du remote | `git -C projects/site-shizendo status` / push, rien n'est écrasé |
| `ERREUR: échec de synchronisation des contenus SB` | token SB invalide ou dossier vide | `~/.opencode/bin/sb-token.sh`, puis relire le listing `/.fs/` |
| Build OK mais publication absente | session SFTP / mot de passe `my_webapp` | `sftp-mirror.sh` à la main, regarder `manifest-_www.txt` |
| Le webhook ne se déclenche pas | listener mort ou secret absent | `ps -ef | grep webhook-listener`, `pass show opencode/site-webhook-secret`, `tail webhook.log` |
| Cron silencieux (aucune sortie) | sentinel déjà posée **ou** échec loggé dans `cron-quotidien.log` | lire le log, pas la sortie console |

## Pièges

- **Préfixe de sync avec slash final** : `startswith(prefix)` sans `/` attrape `.../pages.md` (l'index du dossier) en plus de `.../pages/*.md` → build KO. Corrigé le 2026-11-07.
- **SFTP chrooté** : `my_webapp` est en `ForceCommand internal-sftp` → aucun shell, aucun rsync ; mot de passe passé par `mywebapp-askpass.sh` (`pass` lu à l'exécution, jamais en clair), `sftp -b` cassé l'auth → mode interactif par stdin.
- **Forgejo** `ALLOWED_HOST_LIST` doit inclure `external,loopback` sinon le webhook depuis `127.0.0.1` est refusé.
- **Aucun secret** dans un script, un commit ni un environnement (`at` recopie tout en clair) → toujours `$(pass show ...)` à l'exécution.
