{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "PhiPhi’s Bizarre Aventure (fr)",
  "home_page_url": "https://normco.re/",
  "feed_url": "https://normco.re/fr/feed.json",
  "description": "Blog personnel de Phiphi, basé à Chengdu, en Chine.",
  "items": [
    {
      "id": "https://normco.re/fr/posts/alibaba-cloud-oss-cdn-deployment/",
      "url": "https://normco.re/fr/posts/alibaba-cloud-oss-cdn-deployment/",
      "title": "Comment ce blog est déployé sur Alibaba Cloud OSS et CDN",
      "content_html": "<p>Ce site est construit avec Lume puis déployé sur Alibaba Cloud OSS, avec Alibaba\nCloud CDN en frontal. La chaîne de déploiement reste volontairement compacte :\nun workflow GitHub, Deno disponible sur le runner, et une action personnalisée\nqui restaure un cache local de build, exécute le build, synchronise OSS et\nnettoie :\n<a href=\"https://github.com/frenchvandal/aliyun-oss-cdn-sync-action\"><code>frenchvandal/aliyun-oss-cdn-sync-action</code></a>.</p>\n<p>Je cherche désormais cinq propriétés en même temps : des identifiants à durée de\nvie courte, des builds réchauffés sur des runners neufs, des uploads\nprévisibles, des en-têtes de cache cohérents avec les fichiers livrés, et une\ncohérence de cache automatique. Cette configuration me donne les cinq sans\najouter de scripts de déploiement propres à chaque dépôt.</p>\n<h2>Pipeline en un coup d’œil</h2>\n<p>En régime de production nominal, le workflow de ce dépôt fait maintenant trois\nchoses :</p>\n<ol>\n<li>Récupère le dépôt.</li>\n<li>Installe Deno avec la version verrouillée dans <code>.tool-versions</code> et active le\ncache outillage du runner.</li>\n<li>Exécute l’action de synchronisation OSS/CDN, qui restaure <code>_cache</code>, lance\n<code>deno task build</code>, synchronise <code>_site</code>, rafraîchit le CDN et nettoie.</li>\n</ol>\n<p>À ce stade, ce build fingerprint aussi la feuille partagée et les feuilles\ncritiques par route, retire les source maps et supprime les fichiers optionnels\nde Pagefind qui ne sont plus référencés par le HTML final avant la synchro de\n<code>_site</code>.</p>\n<pre><code class=\"language-yaml\">name: Deploy static content to OSS\n\non:\n  push:\n    branches: [&quot;master&quot;]\n  workflow_dispatch:\n\npermissions:\n  contents: read\n  id-token: write\n\nconcurrency:\n  group: &quot;oss-deploy&quot;\n  cancel-in-progress: true\n\njobs:\n  deploy:\n    environment:\n      name: development\n    runs-on: macos-26\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      - name: Setup Deno environment\n        uses: denoland/setup-deno@v2\n        with:\n          deno-version-file: .tool-versions\n          cache: true\n\n      - name: Deploy to Alibaba Cloud OSS\n        uses: frenchvandal/aliyun-oss-cdn-sync-action@master\n        with:\n          role-oidc-arn: ${{ secrets.ALIBABA_CLOUD_ROLE_ARN }}\n          oidc-provider-arn: ${{ secrets.ALIBABA_CLOUD_OIDC_PROVIDER_ARN }}\n          role-session-name: ${{ github.run_id }}\n          role-session-expiration: 3600\n          cache-enabled: true\n          cache-key: &gt;-\n            lume-cache-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('deno.lock') }}-${{ hashFiles('_cache/**/*') || 'empty' }}\n          cache-restore-keys: |\n            lume-cache-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('deno.lock') }}-\n            lume-cache-${{ runner.os }}-${{ runner.arch }}-\n          input-dir: _site\n          bucket: ${{ secrets.OSS_BUCKET }}\n          region: ${{ secrets.OSS_REGION }}\n          audience: ${{ github.repository_id }}\n          cdn-enabled: true\n          cdn-base-url: ${{ secrets.OSS_CDN_BASE_URL }}\n          cdn-actions: refresh\n          build-command: deno task build\n</code></pre>\n<p>Dans ce dépôt, j’utilise maintenant <code>cdn-actions: refresh</code>. L’action supporte\ntoujours <code>refresh,preload</code>, mais le workflow de ce site n’active plus le preload\nà chaque déploiement.</p>\n<h2>Pourquoi OIDC plutôt que des clés d’accès</h2>\n<p>L’action utilise GitHub OIDC pour endosser un rôle RAM Alibaba Cloud à\nl’exécution. Aucune clé d’accès longue durée n’est stockée dans le dépôt. Le\nworkflow n’a besoin que de <code>id-token: write</code>, de l’ARN du rôle RAM et de l’ARN\ndu fournisseur OIDC.</p>\n<p>L’authentification est désormais strictement OIDC : l’action ne retombe pas sur\ndes access keys statiques passées en entrée ou via l’environnement si la prise\nde rôle échoue.</p>\n<p>La configuration actuelle transmet aussi\n<code>audience: ${{ github.repository_id }}</code>. GitHub peut émettre un ID token avec\nune audience personnalisée, et Alibaba Cloud RAM peut valider cette valeur <code>aud</code>\npar rapport aux Client ID configurés sur le fournisseur d’identité OIDC et dans\nla trust policy du rôle. La frontière de confiance est donc plus resserrée\nqu’avec l’audience par défaut seule.</p>\n<h2>Fonctionnement interne de l’action</h2>\n<p>L’action est découpée en trois phases :</p>\n<ul>\n<li><strong>pre</strong> : endosse le rôle RAM via OIDC et stocke des identifiants temporaires\ndans l’état de l’action.</li>\n<li><strong>main</strong> : restaure éventuellement le dossier local <code>_cache</code>, exécute\n<code>build-command</code>, upload les fichiers locaux sur OSS, applique les en-têtes de\ncache et déclenche les actions CDN si elles sont activées.</li>\n<li><strong>post</strong> : compare les objets distants sous le préfixe cible avec les fichiers\nlocaux produits par le build, supprime les orphelins côté distant, rafraîchit\nau besoin les URL supprimées, écrit des résumés d’exécution CDN et peut\nsauvegarder <code>_cache</code> si <code>cache-key</code> est configuré.</li>\n</ul>\n<p>La phase de nettoyage s’exécute avec <code>post-if: always()</code>, donc elle se lance\nmême si des étapes précédentes échouent. Le nettoyage, les appels CDN et la\nrestauration ou sauvegarde du cache sont volontairement non bloquants : les\navertissements sont journalisés, mais des incidents CDN ou cache transitoires\nn’interrompent pas le déploiement.</p>\n<h2>Upload, cache, quota et contrôle de dérive</h2>\n<p>Quelques détails d’implémentation comptent pour la fiabilité :</p>\n<ul>\n<li><code>build-command</code> s’exécute dans l’action avant tout upload OSS ou appel CDN.</li>\n<li>Si <code>cache-enabled</code> vaut <code>true</code> et que <code>cache-key</code> est configuré, <code>_cache</code> est\nrestauré avant le build et un nouvel instantané peut être sauvegardé pendant\n<code>post</code>.</li>\n<li><code>cache-enabled</code> ne contrôle que la restauration : je peux donc neutraliser le\nwarm cache temporairement sans perdre la sauvegarde de cache en post-step.</li>\n<li>Les uploads sont parallélisés via <code>max-concurrency</code>.</li>\n<li>Un throttling global d’API est appliqué avec <code>api-rps-limit</code>.</li>\n<li>Chaque upload de fichier est retenté jusqu’à trois fois avant d’être marqué en\néchec.</li>\n<li>Les échecs partiels d’upload remontent via <code>failed-count</code> et le résumé du job\nGitHub Actions au lieu d’être noyés dans les logs.</li>\n<li>L’étape d’upload écrit désormais automatiquement des en-têtes\n<code>Cache-Control</code> : les fichiers HTML et <code>sw.js</code> sont revalidés de manière\nagressive, les assets hashés sont envoyés comme immuables, et les ressources\nstatiques courantes reçoivent des fenêtres de revalidation plus courtes au\nlieu d’une politique unique pour tout.</li>\n<li><code>cdn-actions</code> n’accepte que <code>refresh</code> ou <code>refresh,preload</code> ; si le CDN est\nactivé et que la valeur est vide ou invalide, l’action retombe sur <code>refresh</code>.</li>\n<li>L’action vérifie le quota CDN restant avant de soumettre des lots de refresh\nou de preload.</li>\n<li>La suppression d’objets OSS peut déclencher un refresh CDN des URL supprimées,\nce qui réduit les fenêtres de cache obsolète et limite la dérive des objets\ndans le temps.</li>\n<li>L’action écrit aussi un résumé GitHub Actions pour le déploiement, le\nnettoyage et l’état des tâches CDN.</li>\n</ul>\n<p>Ce dernier point est facile à sous-estimer : l’upload seul ne suffit pas pour un\nsite statique qui évolue. Il faut aussi gérer la suppression et l’invalidation\nde cache des objets qui ne devraient plus exister.</p>\n<h2>Permissions RAM minimales</h2>\n<p>Au niveau des politiques, le rôle a besoin de permissions OSS sur le scope du\nbucket cible pour lister, uploader et supprimer les objets. Côté CDN, ce\nworkflow a besoin des permissions de refresh ainsi que des API de lecture\nutilisées pour les contrôles de quota et le suivi des tâches. Si le preload est\nactivé, il faut y ajouter la permission correspondante. La trust policy doit\nautoriser le fournisseur OIDC GitHub à endosser le rôle de déploiement.</p>\n<p>Je conserve ce rôle comme rôle de déploiement dédié, sans le mélanger à des\npermissions opérationnelles plus larges.</p>\n<h2>Réutiliser l’action dans d’autres dépôts</h2>\n<p>L’action est publiée et réutilisable :\n<a href=\"https://github.com/frenchvandal/aliyun-oss-cdn-sync-action\">github.com/frenchvandal/aliyun-oss-cdn-sync-action</a>.</p>\n<p>Ce dépôt suit actuellement <code>@master</code> parce que je fais évoluer le site et\nl’action ensemble. Dans un autre dépôt, j’épinglerais plutôt <code>@v1</code>, voire un SHA\nde commit complet. Je laisse maintenant l’action piloter le build et le cycle de\nvie de <code>_cache</code>, ce qui raccourcit encore le workflow consommateur.</p>\n<p>Pour moi, le résultat clé reste le même : des déploiements prévisibles.\nRestauration du cache, build, sync, refresh, cleanup, terminé.</p>\n",
      "date_published": "2026-03-10T00:00:00Z"
    },
    {
      "id": "https://normco.re/fr/posts/lorem-ipsum/",
      "url": "https://normco.re/fr/posts/lorem-ipsum/",
      "title": "Lorem ipsum et l’art du texte de remplissage",
      "content_html": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor\nincididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis\nnostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>\n<p>Tout le monde connaît la formule, mais peu de personnes en connaissent\nl’origine. Il s’agit d’un extrait brouillé du <em>de Finibus Bonorum et Malorum</em> de\nCicéron, un traité philosophique rédigé en 45 av. J.-C. Ce texte est utilisé\ncomme faux contenu depuis le XVIe siècle, lorsqu’un imprimeur inconnu a mélangé\nune casse de caractères pour créer un spécimen typographique.</p>\n<p><img src=\"https://normco.re/posts/lorem-ipsum/images/amol-srivastava-uOYc6OlgpUI-unsplash.jpg\" alt=\"\"></p>\n<h2>Pourquoi le texte de remplissage compte</h2>\n<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu\nfugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in\nculpa qui officia deserunt mollit anim id est laborum.</p>\n<p>Quand vous concevez une mise en page avec de vrais mots, vous concevez pour ces\nmots précis. Le faux texte vous oblige à concevoir pour la structure, pas pour\nle sens. C’est une contrainte utile : elle révèle si votre design peut absorber\nl’imprévu : un titre sur trois lignes, un paragraphe sans respiration naturelle,\nun mot trop long pour son conteneur.</p>\n<h2>Une question de fidélité</h2>\n<p>Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit\nlaboriosam ? En design, il existe une tension entre la fidélité au contenu réel\net la liberté qu’autorise l’abstraction.</p>\n<p>Les prototypes haute fidélité avec du vrai contenu exigent que ce contenu existe\ndéjà. Les wireframes basse fidélité, eux, communiquent la structure sans la\ndistraction du sens. Les deux approches ont leur place. L’essentiel est de\nsavoir quel outil sert le moment.</p>\n<pre><code class=\"language-ts\">// A small utility to generate repeating text blocks.\nfunction lorem(words: number): string {\n  const base = &quot;lorem ipsum dolor sit amet consectetur adipiscing elit&quot;;\n  const tokens = base.split(&quot; &quot;);\n  const result: string[] = [];\n  for (let i = 0; i &lt; words; i++) {\n    result.push(tokens[i % tokens.length] ?? &quot;&quot;);\n  }\n  return result.join(&quot; &quot;);\n}\n</code></pre>\n<p>Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed\nquia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.</p>\n<p><img src=\"https://normco.re/posts/lorem-ipsum/images/scott-rodgerson-z0MDyylvY1k-unsplash.jpg\" alt=\"\"></p>\n<h2>Conclusion</h2>\n<p>Le lorem ipsum est plus qu’un simple remplissage. C’est un miroir tendu au\ndesign : il reflète la structure dépouillée de sens, la forme sans contenu.\nC’est précisément pour cela qu’il traverse les époques.</p>\n",
      "date_published": "2026-02-18T00:00:00Z"
    },
    {
      "id": "https://normco.re/fr/posts/vestibulum-ante/",
      "url": "https://normco.re/fr/posts/vestibulum-ante/",
      "title": "Vestibulum Ante : seuils et nouveaux départs",
      "content_html": "<p>Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia\ncurae. Le mot latin <em>vestibulum</em> — une antichambre, une entrée, l’espace devant\nla porte — porte un poids que son descendant français « vestibule » ne restitue\npas toujours.</p>\n<p>Un seuil n’est ni une fin, ni un commencement. C’est l’espace liminal entre les\ndeux : le souffle retenu avant le premier mot, la pause après le tour de clé.\nC’est l’endroit où quelque chose s’achève et où autre chose n’a pas encore\ncommencé.</p>\n<p><img src=\"https://normco.re/posts/vestibulum-ante/images/bruno-martins-4cwf-iW6I1Q-unsplash.jpg\" alt=\"\"></p>\n<h2>La valeur de l’entre-deux</h2>\n<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac\nturpis egestas. Nous vivons dans une culture qui dévalue la transition. Nous\nvoulons des résultats, des livrables, l’objet fini. Le milieu brouillon, les\nbrouillons successifs, le doute, l’accumulation lente de compréhension, tout\ncela reste peu documenté et rarement célébré.</p>\n<p>Pourtant, l’entre-deux est l’endroit où se déroule l’essentiel de la vie. Les\ntrajets. Les salles d’attente. Le temps entre l’envoi d’un message et la lecture\nde la réponse. Les années entre le moment où l’on sait ce que l’on veut et celui\noù l’on sait comment l’obtenir.</p>\n<h2>Se tenir sur le pas de la porte</h2>\n<p>Fusce suscipit varius mi. Cum sociis natoque penatibus et magnis dis parturient\nmontes, nascetur ridiculus mus. Phasellus viverra nulla ut metus varius laoreet.</p>\n<p>J’ai déménagé à Chengdu un mardi du début d’automne. La ville était chaude d’une\nmanière qui m’a surpris, non pas la chaleur de l’été, mais la chaleur d’un lieu\nqui avait déjà décidé qu’il vous appréciait. Les rues sentaient l’huile pimentée\net l’osmanthe, et tout semblait légèrement, agréablement familier et nouveau à\nla fois.</p>\n<p>C’est cela, un seuil, je crois. Ni hostile, ni accueillant. Simplement ouvert,\nen attente de ce que vous en ferez.</p>\n<h2>Coda</h2>\n<p>Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi,\ncondimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit\neget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim\nac dui.</p>\n<p>Chaque texte possède son vestibule, la page de titre, l’introduction, le premier\nparagraphe. C’est l’espace qui prépare la lectrice ou le lecteur à ce qui vient\nensuite. Il faut l’écrire avec soin.</p>\n",
      "date_published": "2025-09-04T00:00:00Z"
    },
    {
      "id": "https://normco.re/fr/posts/proin-facilisis/",
      "url": "https://normco.re/fr/posts/proin-facilisis/",
      "title": "Proin Facilisis : rendre les choses plus simples",
      "content_html": "<p><em>Proin facilisis</em> — en latin, « favoriser l’aisance ». L’expression apparaît\ndans d’anciens textes botaniques pour décrire une plante qui facilite la\ndigestion, adoucit un passage, lève une obstruction. Comme philosophie de\nconception logicielle, la formule se transpose étonnamment bien.</p>\n<p>Un bon logiciel réduit les frictions. Il anticipe l’étape suivante de\nl’utilisateur. Il propose la bonne affordance au bon moment. Il s’efface.</p>\n<p><img src=\"https://normco.re/posts/proin-facilisis/images/brett-jordan-92-mTYj5oGs-unsplash.jpg\" alt=\"\"></p>\n<h2>L’inventaire des frictions</h2>\n<p>Proin in tellus sit amet nibh dignissim sagittis. La première étape pour réduire\nles frictions est de les cartographier. Où l’utilisateur ralentit-il ? Où\nl’attention monte-t-elle ? Où les erreurs se concentrent-elles ?</p>\n<p>Dans une application web classique, les moments de forte friction sont\nprévisibles : onboarding, envoi de formulaire, récupération après erreur et\nétats de chargement. Ce sont les vestibules du produit, les seuils que\nl’utilisateur doit franchir pour atteindre la valeur.</p>\n<pre><code class=\"language-ts\">// Friction shows up in code too.\n// Compare these two approaches to handling a missing value:\n\n// High friction — the caller must always check:\nfunction getUser(id: string): User | undefined {/* … */}\n\n// Lower friction — the error is explicit and handled at the boundary:\nfunction getUser(id: string): User {\n  const user = db.find(id);\n  if (user === undefined) {\n    throw new Error(`User not found: ${id}`);\n  }\n  return user;\n}\n</code></pre>\n<h2>Le paradoxe de la simplicité</h2>\n<p>Vivamus pretium aliquet erat. Il y a un paradoxe au cœur du « rendre simple » :\nc’est difficile. Éliminer les frictions demande une compréhension fine de\nl’utilisateur, du contexte et des modes d’échec. Cela exige plus de travail côté\nconcepteur pour en demander moins côté utilisateur.</p>\n<p>C’est pour cela que la simplicité est une forme de générosité. Chaque étape\ninutile retirée du parcours rend du temps à l’utilisateur, du temps qu’il peut\nconsacrer à ce qui compte vraiment.</p>\n<h2>Facilisis en pratique</h2>\n<p>Donec aliquet metus ut erat semper, et tincidunt nulla luctus. Quelques\nprincipes vers lesquels je reviens régulièrement :</p>\n<ul>\n<li>Les valeurs par défaut doivent convenir à la majorité des usages.</li>\n<li>Les messages d’erreur doivent expliquer le problème et la correction.</li>\n<li>Le chemin nominal ne devrait demander aucun effort mental.</li>\n<li>La configuration doit être possible, jamais obligatoire.</li>\n<li>La documentation fait partie intégrante du produit.</li>\n</ul>\n<p>Nulla facilisi. Phasellus blandit leo ut odio. Nam sed nulla non diam tincidunt\ntempus. Le nom même de ce principe — <em>nulla facilisi</em>, « rien de facile » —\nrappelle que la simplicité, bien comprise, n’est jamais accidentelle.</p>\n",
      "date_published": "2025-04-12T00:00:00Z"
    },
    {
      "id": "https://normco.re/fr/posts/instructions/",
      "url": "https://normco.re/fr/posts/instructions/",
      "title": "Comment installer ce thème ?",
      "content_html": "<p><strong>Simple blog</strong> est un thème de blog épuré et minimal pour Lume, avec prise en\ncharge des tags et des auteurs. Il permet de créer votre blog <strong>en quelques\nsecondes</strong>, et fournit des flux Atom et JSON pour vos abonnés.</p>\n<p>La manière <strong>la plus simple et la plus rapide</strong> de configurer ce thème est\nd’utiliser la <a href=\"https://deno.land/x/lume_init\">commande Lume init</a>, que vous\npouvez aussi copier facilement depuis la\n<a href=\"https://lume.land/theme/simple-blog/\">page du thème Simple Blog</a>. En lançant :</p>\n<pre><code class=\"language-bash\">deno run -A https://lume.land/init.ts --theme=simple-blog\n</code></pre>\n<p>vous créez un nouveau projet avec Simple Blog déjà configuré. Modifiez ensuite\nle fichier <code>_data.yml</code> à la racine du blog pour personnaliser le titre, la\ndescription et les métadonnées du site.</p>\n<p>Les articles doivent être enregistrés dans le dossier <code>posts</code>. Par exemple :\n<code>posts/my-first-post.md</code>.</p>\n<h2>Installer en tant que thème distant</h2>\n<p>Pour ajouter ce thème à un projet Lume existant, importez-le dans votre fichier\n<code>_config.ts</code> comme module distant. Pour le mettre à jour, changez le numéro de\nversion dans l’URL d’import :</p>\n<pre><code class=\"language-ts\">import lume from &quot;lume/mod.ts&quot;;\nimport blog from &quot;https://deno.land/x/lume_theme_simple_blog@v0.15.6/mod.ts&quot;;\n\nconst site = lume();\n\nsite.use(blog());\n\nexport default site;\n</code></pre>\n<p>Copiez ensuite le fichier\n<a href=\"https://github.com/lumeland/theme-simple-blog/blob/main/src/_data.yml\"><code>_data.yml</code></a>\nà la racine de votre blog et adaptez-le avec vos informations.</p>\n<h2>Personnalisation</h2>\n<p>Vous pouvez utiliser <a href=\"https://lume.land/cms\">lumeCMS</a> pour personnaliser le blog\net ajouter du contenu facilement.</p>\n",
      "date_published": "2022-06-12T00:00:00Z"
    }
  ],
  "language": "fr"
}
