<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>Architecture - Balise - arleo.eu</title><link>https://www.arleo.eu/tags/architecture/</link><description>Architecture - Balise - arleo.eu</description><generator>Hugo -- gohugo.io</generator><language>fr</language><lastBuildDate>Sat, 09 May 2026 23:23:49 +0200</lastBuildDate><atom:link href="https://www.arleo.eu/tags/architecture/" rel="self" type="application/rss+xml"/><item><title>hugo-mcp v2.0 : un plugin-system Python en 200 lignes</title><link>https://www.arleo.eu/posts/hugo-mcp-plugins-architecture/</link><pubDate>Sat, 09 May 2026 23:23:49 +0200</pubDate><author>Jmr</author><guid>https://www.arleo.eu/posts/hugo-mcp-plugins-architecture/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/hugo-mcp-plugins-architecture-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="tldr">TL;DR</h2>
<p><a href="https://github.com/jmrGrav/hugo-mcp/releases/tag/v2.0.0" target="_blank" rel="noopener noreffer ">hugo-mcp v2.0.0</a> introduit un <strong>plugin-system Python</strong> qui permet à n&rsquo;importe qui d&rsquo;ajouter des hooks après chaque <code>create_page</code> / <code>update_page</code> / <code>delete_page</code>. 200 lignes de code pour le coeur, 3 plugins de production fournis (IndexNow, Google Indexing, Cloudflare). Ce post explique le design, les arbitrages, la sécurité, et montre comment écrire son propre plugin en 5 minutes.</p>]]></description></item><item><title>Roadmap : faut-il migrer les 16 articles vers content/posts/ ?</title><link>https://www.arleo.eu/posts/roadmap-migration-content-posts/</link><pubDate>Sat, 09 May 2026 12:48:49 +0200</pubDate><author>Jmr</author><guid>https://www.arleo.eu/posts/roadmap-migration-content-posts/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/roadmap-migration-content-posts-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="statut---réflexion--décision-en-attente">Statut : 🤔 RÉFLEXION — décision en attente</h2>
<p>Cette page documente une question d&rsquo;architecture de contenu Hugo pour laquelle je n&rsquo;ai pas encore tranché.</p>
<h2 id="le-contexte">Le contexte</h2>
<p>Le thème LoveIt (que utilise arleo.eu) suit la convention Hugo recommandée : les articles de blog vont dans <code>content/posts/</code>. La home filtre <code>where .Site.RegularPages &quot;Type&quot; &quot;posts&quot;</code>.</p>
<p>Or, par historique de migration depuis Grav, mes 16 articles éditoriaux sont à la <strong>racine</strong> de <code>content/</code>, pas dans <code>content/posts/</code> :</p>
<div class="code-block code-line-numbers open" data-start="0">
    <div class="code-header language-">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><pre tabindex="0"><code>content/
├── csp-nonce/index.fr.md          ← article
├── post-mortem-522-wan-failover/  ← article
├── jquery-migration/              ← article
├── ...
├── privacy-policies/              ← page de référence (footer)
├── documentation/                 ← page de référence (menu)
└── scripts/                       ← page de référence (menu)</code></pre></div>
<p>Hugo considère par défaut tous ces dossiers comme des pages de &ldquo;Type: page&rdquo;, pas &ldquo;Type: posts&rdquo;. Donc le filtre LoveIt sur la home ne les voit pas, et je dois soit overrider <code>layouts/index.html</code>, soit filtrer manuellement.</p>
<h2 id="la-question">La question</h2>
<p>Faut-il migrer les 16 articles vers <code>content/posts/</code> pour suivre la convention LoveIt, ou les laisser à la racine ?</p>
<h2 id="option-a--migrer-vers-contentposts">Option A — Migrer vers <code>content/posts/</code></h2>
<div class="code-block code-line-numbers open" data-start="0">
    <div class="code-header language-">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><pre tabindex="0"><code>content/
├── posts/
│   ├── csp-nonce/index.fr.md
│   ├── post-mortem-522-wan-failover/
│   ├── jquery-migration/
│   └── ...
├── privacy-policies/
├── documentation/
└── scripts/</code></pre></div>
<h3 id="avantages">Avantages</h3>
<ul>
<li><strong>Conformité au thème</strong> : pas d&rsquo;override de layouts nécessaire</li>
<li><strong>Lisibilité repo</strong> : on voit immédiatement quels dossiers sont des articles vs des pages de référence</li>
<li><strong>Évolutivité</strong> : si LoveIt évolue (nouvelles features sur Type=posts), elles s&rsquo;appliquent gratuitement</li>
<li><strong>Mental model</strong> standard : tout dev Hugo qui découvre le repo comprend instantanément</li>
</ul>
<h3 id="inconvénients">Inconvénients</h3>
<ul>
<li><strong>Changement d&rsquo;URL</strong> : <code>/csp-nonce/</code> deviendrait <code>/posts/csp-nonce/</code> par défaut. Casse tous les liens entrants (Google, Reddit, hackernews, mes propres articles internes)</li>
<li><strong>Mitigation possible</strong> via <code>url:</code> explicite dans le front matter : <code>url: /csp-nonce/</code> — Hugo génère alors la page à <code>/csp-nonce/</code> même si le source est dans <code>posts/</code>. Les URLs sont préservées.</li>
<li><strong>Friction migration</strong> : 16 dossiers à déplacer, 16 fichiers à éditer pour ajouter <code>url:</code></li>
<li><strong>Liens internes</strong> dans le contenu Markdown — nombreux articles se citent les uns les autres. À auditer pour s&rsquo;assurer qu&rsquo;ils restent corrects.</li>
</ul>
<h2 id="option-b--garder-la-racine">Option B — Garder la racine</h2>
<div class="code-block code-line-numbers open" data-start="0">
    <div class="code-header language-">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><pre tabindex="0"><code>content/
├── csp-nonce/index.fr.md
├── post-mortem-522-wan-failover/
├── ...
├── privacy-policies/
├── documentation/
└── scripts/</code></pre></div>
<h3 id="avantages-1">Avantages</h3>
<ul>
<li><strong>URLs identiques</strong> sans rien faire — <code>/csp-nonce/</code> reste <code>/csp-nonce/</code></li>
<li><strong>Pas de migration</strong> : 0 fichier touché</li>
<li><strong>Liens internes</strong> : intacts par définition</li>
<li><strong>Override <code>layouts/home.html</code> déjà en place</strong> : 1 ligne de différence, ça marche</li>
</ul>
<h3 id="inconvénients-1">Inconvénients</h3>
<ul>
<li><strong>Override custom</strong> à maintenir si LoveIt évolue</li>
<li><strong>Lisibilité repo</strong> : pages de référence et articles mélangés à la racine</li>
<li><strong>Convention bizarre</strong> : un dev Hugo découvrant le repo se demande pourquoi pas de <code>posts/</code></li>
</ul>
<h2 id="considérations-seo">Considérations SEO</h2>
<p>Le SEO craint le changement d&rsquo;URL. Si je migre vers <code>posts/</code> SANS <code>url:</code> override, les 16 articles passent de <code>arleo.eu/csp-nonce/</code> à <code>arleo.eu/posts/csp-nonce/</code>. Cloudflare cache, Google indexe, robots.txt connaît la map du site… tout doit être ré-indexé.</p>
<p>Avec <code>url:</code> override (Option A v2), aucun changement d&rsquo;URL. Mais alors quel est l&rsquo;intérêt de migrer ? Juste la lisibilité du repo. Le coût (16 fichiers à éditer) vs le bénéfice (lisibilité interne) → probablement pas rentable.</p>
<h2 id="considérations-hugo-mcp">Considérations Hugo MCP</h2>
<p>Les outils MCP que j&rsquo;utilise pour éditer le contenu (<code>hugo-mcp</code> v1.8.1) acceptent une route comme paramètre : <code>create_page(route=&quot;/csp-nonce&quot;)</code>. Le mapping route → fichier est défini par Hugo.</p>
<p>Si je migre vers <code>posts/</code> :</p>
<ul>
<li>Avec <code>url:</code> override : MCP appelle <code>create_page(route=&quot;/csp-nonce&quot;)</code>, le fichier est créé à <code>content/posts/csp-nonce/index.fr.md</code> mais la page est servie à <code>/csp-nonce/</code>. Workflow inchangé pour l&rsquo;utilisateur.</li>
<li>Sans <code>url:</code> override : MCP doit appeler <code>create_page(route=&quot;/posts/csp-nonce&quot;)</code>. Workflow différent. Tous les articles existants tombent sur des routes inconnues.</li>
</ul>
<p>Je penche fortement pour l&rsquo;Option A v2 (migration + <code>url:</code> override) si je migre.</p>
<h2 id="la-décision-provisoire">La décision (provisoire)</h2>]]></description></item><item><title>Stratégie 4 : séparer le contenu (MCP) de la structure (Git)</title><link>https://www.arleo.eu/posts/strategie-4-mcp-vs-git/</link><pubDate>Sat, 09 May 2026 12:40:58 +0200</pubDate><author>Jmr</author><guid>https://www.arleo.eu/posts/strategie-4-mcp-vs-git/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/strategie-4-mcp-vs-git-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="le-problème">Le problème</h2>
<p>Tu as un site Hugo. Tu veux pouvoir :</p>
<ol>
<li><strong>Éditer le contenu via Claude.ai</strong> (publier un article, corriger une coquille, mettre à jour un draft) sans toucher à un terminal SSH.</li>
<li><strong>Versionner la structure</strong> (layouts, themes, hugo.toml, scripts de déploiement) dans Git, comme un dev sérieux.</li>
</ol>
<p>Premier réflexe : « tout dans Git ». Les articles aussi. Le MCP commit, push, le webhook GitHub fait un rebuild. Propre, dans la philosophie GitOps.</p>
<p>Sauf que ça ne marche pas si bien. Voici pourquoi, et la solution simple que j&rsquo;appelle la <strong>Stratégie 4</strong>.</p>
<h2 id="pourquoi--tout-dans-git--casse-en-pratique">Pourquoi « tout dans Git » casse en pratique</h2>
<p>Imaginons que ton MCP fait un <code>git commit</code> à chaque <code>create_page</code>. Stratégie naïve, mais souvent proposée. Voici les problèmes :</p>
<h3 id="problème-1--conflit-mcp--git">Problème 1 — Conflit MCP ↔ Git</h3>
<p>Tu push depuis ton laptop un nouveau layout (<code>layouts/index.html</code> modifié). Au même moment, le MCP est en train de commiter une nouvelle version d&rsquo;un article. Race condition, le MCP peut faire un <code>git pull --rebase</code> qui échoue, ou pire, écraser ton commit local.</p>
<h3 id="problème-2--identité-git-du-mcp">Problème 2 — Identité Git du MCP</h3>
<p>Le MCP commit en tant que qui ? Avec quelle clé GPG ? Si tu as une politique « commits signés obligatoires », le MCP doit gérer une clé GPG, qu&rsquo;il faut sécuriser, faire tourner, etc.</p>
<h3 id="problème-3--auto-commit-indésirable">Problème 3 — Auto-commit indésirable</h3>
<p>Tu fais un test, tu crées un article draft pour expérimenter, tu le supprimes. Mais le MCP a déjà commité. Maintenant tu as un commit &ldquo;wip test&rdquo; dans l&rsquo;historique, à rebaser ou squasher manuellement.</p>
<h3 id="problème-4--réversibilité-asymétrique">Problème 4 — Réversibilité asymétrique</h3>
<p>Un <code>git revert</code> côté repo n&rsquo;a aucun effet sur les fichiers que le MCP a déjà créés. Tu te retrouves avec un repo et un état filesystem désynchronisés.</p>
<h2 id="la-stratégie-4--séparer-les-zones">La Stratégie 4 : séparer les zones</h2>
<p>L&rsquo;idée : <strong>MCP et Git n&rsquo;écrivent jamais sur les mêmes fichiers</strong>.</p>
<table>
  <thead>
      <tr>
          <th>Zone</th>
          <th>Qui édite</th>
          <th>Versionné dans Git ?</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>content/**/*.md</code></td>
          <td><strong>MCP exclusivement</strong></td>
          <td>❌ NON (<code>.gitignore</code>)</td>
      </tr>
      <tr>
          <td><code>layouts/</code>, <code>themes/</code>, <code>static/</code>, <code>hugo.toml</code>, <code>deploy.sh</code></td>
          <td><strong>Git push exclusivement</strong></td>
          <td>✅ OUI</td>
      </tr>
  </tbody>
</table>
<p>Le <code>.gitignore</code> côté repo :</p>
<div class="code-block code-line-numbers open" data-start="0">
    <div class="code-header language-">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><pre tabindex="0"><code>content/
public/
resources/</code></pre></div>
<p>Implications :</p>
<ul>
<li>Le MCP peut écrire dans <code>content/</code> quand il veut. Aucun conflit Git possible.</li>
<li>Tu peux faire <code>git reset --hard</code> côté repo en confiance — <code>content/</code> reste intact.</li>
<li>Pas besoin que le MCP gère une identité Git.</li>
<li>Pas de &ldquo;commit pollution&rdquo;.</li>
</ul>
<h2 id="trade-off--pas-de-versioning-du-contenu">Trade-off : pas de versioning du contenu</h2>
<p>Tu perds le versioning Git du contenu. C&rsquo;est une perte réelle :</p>
<ul>
<li>Pas de <code>git blame</code> sur un article pour voir qui a écrit quoi.</li>
<li>Pas de <code>git log content/csp-nonce/index.fr.md</code> pour voir l&rsquo;historique.</li>
<li>Pas de PR review pour les articles.</li>
</ul>
<p><strong>Mitigation</strong> : snapshots VM + backup <code>content/</code> chiffré quotidien sur QNAP. Ça couvre la <strong>récupération en cas de désastre</strong>, mais pas le versioning fin (qui-a-changé-quoi-quand).</p>
<p>Pour mon homelab perso (un seul auteur, articles techniques, pas de workflow de validation éditoriale), c&rsquo;est un trade-off acceptable. Pour un blog d&rsquo;équipe avec 10 contributeurs, je reconsidérerais.</p>
<h2 id="architecture-finale">Architecture finale</h2>]]></description></item></channel></rss>